最近在看js的闭包,有一个地方看了许久都没有理解。
1 function createFunctions() { 2 var result = new Array(); 3 for (var i = 0; i < 10; i++) { 4 result[i] = function () { 5 return i; 6 } 7 } 8 return result; 9 }
书上说每个函数都返回10,我百思不得其解,总觉得返回的应该是0,1,2,3....9。于是自己写代码测试了一下,果然是都是10.
1 var getResult = createFunctions(); 2 console.log(getResult); 3 for (var i = 0; i < 10; i++) { 4 var tt = getResult[i]; 5 console.log(tt()); 6 }
当时第一次写的时候console.log()里的tt是没有加圆括号的,结果拿到的是十个一模一样的函数。
每个函数体都是return i;这时我忽然明白了,在createFunctions函数内部只是为每个数组元素赋值为具有相同函数体的匿名函数,而并没有执行这个函数。真正想要获得这些函数值是需要执行函数的。而当执行这些函数时,createFunctions的作用于链已经被销毁了,只留下了i这个活动对象被保存在匿名函数的作用域链中,而此时i的值为10.因此10个函数的执行结果均为10.
那么要想数组中的元素分别是那些索引应该怎么办呢?此时我在createFunctions的函数内部直接把匿名函数执行后的结果赋值给了数组中的元素,代码如下:
1 function createMyFunctions() { 2 var result = new Array(); 3 for (var i = 0; i < 10; i++) { 4 result[i] = (function () { 5 return i; 6 })(); 7 } 8 return result; 9 } 10 var getMyResult = createMyFunctions(); 11 console.log(getMyResult);
但是这样的话,getMyResult数组中的元素就是[0,1,2,...,9]了。那如果想让数组中的元素仍然为十个函数,但是函数的返回值是索引,书上是这样写的:
1 function createTheirFunctions() { 2 var result = new Array(); 3 for (var i = 0; i < 10; i++) { 4 result[i] = (function (num) { 5 return function () { 6 return num; 7 }; 8 })(i); 9 } 10 return result; 11 } 12 var getTheirResult = createTheirFunctions(); 13 console.log(getTheirResult); 14 for (var i = 0; i < 10; i++) { 15 var tt = getTheirResult[i]; 16 console.log(tt()); 17 }
上面是利用了嵌套函数,还有另一种方法是使用with语句创建块级作用域:
1 function createYourFunctions() { 2 var result = new Array(); 3 for (var i = 0; i < 10; i++) { 4 var t = {value: i}; 5 with (t) { 6 result[i] = function () { 7 return value; 8 }; 9 } 10 } 11 return result; 12 } 13 var getYourResult = createYourFunctions(); 14 console.log(getYourResult); 15 for (var i = 0; i < 10; i++) { 16 var tt = getYourResult[i]; 17 console.log(tt()); 18 }
当然用with并不是我想出来的办法,是一位大神想出来的,听了大神一大堆的分析后,我深感js的深奥!
下面附上和大神的聊天记录(研究如何输出0-9):
时间: 2024-10-12 21:33:09