For循环闭包问题

进来深感技术不精,拾起很久以前买的全新的看过几次的阮老师的《ES6 标准入门》

也翻过两三次但是书签都是没变过的在第十页

我终于知道我在哪卡的关了

书中第10页,有一段阮老师用来教学的错误代码

var a = [];
for(var i = 0; i < 10; i++) {
    a[i] = function(){
        console.log(i);
    };
}
a[6](); //10

这段代码本意应该是控制台日志输出0-9,但是最后运行a[6]()的时候,i的值已经经过循环后变成了10,所以无论运行a[0-9],返回的函数都是

function(){
    console.log(i);
}

所以输出的i都是最新的10

然后我的思维就开始跳出五行天地外了,我就在想,那么怎么让他做到a[n]()的时候控制台输出对应的n呢

这时候可能就会有同学出来说了

哇,你这个瓜皮,我一手赋值搞定你还搞什么function,神魔恋!

看着!

var a = [];
for(var i = 0; i < 10; i++){
    a[i] = i;
}
a[6] //6!

(想办法圆场)

哇,大佬就是大佬,一眼就看出了这里面的玄机。

由于最初那个例子,a[i]()的值是一个不确定的函数,因为元素值都是function(){console.log(i)},这个i不确定,所以导致了一些问题。

所以大佬就直接给a[i],附上一个确确实实的值,怎么跑都跑不了,怎么变都变不了。

同样的思路,平时我们在实践中的情况可能复杂一点,所要赋的值往往不如上一例的i来的那么轻松直接。往往都是需要通过i进行一些计算后的值。那么同样的,不管这个值是什么,我们只要把它确定了,它就怎么都不会干扰我们的判断。

var a = [];
for(var i = 0; i < 10; i++) {
    a[i] = function(){
        console.log(i);
    };
}
a[6](); //10

那么这一例,我们只要在赋值的时候,确定下console.log(i)这个表达式的值,让它成为console.log(1) console.log(2)等,那么它就不会影响到我们的判断。

但是js的函数声明提升特性,使得a[i]在被赋值的时候,只是声明了一个匿名函数而已。只有当你在运行a[n]()的时候,js才会去运行函数里面的内容。但是我们要是等到它运行的时候才调用的话,i早就变成10了,黄花菜都凉了。

那么有没有一款…… 有的!小米枪……

所以按照我们的思路,我们需要在赋值的时候就要立刻确定i的值,也就是要在赋值的时候立刻计算函数式。

所以这里引入立即执行函数(IIFE),这个不是什么特殊的东西,只是一个技巧的妙用吧(我觉得)


一个函数表达式,在必须用到它的时候,js就回去计算表达式的值。比如:

var a = !function(){
    return true;
};
a //false

所以我们需要创造在一个必须使用它的值环境,迫使js去计算函数表达式的值。

考虑到 + - !之类的运算符会对函数本身的值造成影响,而且有时候还不好处理这些运算后的值。

那么有没有一个…… 有的!

那就是()小括号

在平时写代码的时候,它也相当于一个求括号内的值的这么一个操作,我们用小括号包裹函数表达式,迫使js去计算函数表达式的值即可。


var a = [];
for(var i = 0; i < 10; i++) {
    a[i] = function(){
        console.log(i);
    };
}
a[6](); //10

(只是搬下来方便查看&充字数)

我们可以用立即执行函数,在赋值之前,迫使js计算函数表达式的值,即可得出console.log(1)这样子的确定值。

包裹起来就长这个样子

var a = [];
for(var i = 0; i < 10; i++) {
    a[i] = (  function(temp){console.log(temp)}(i)  );
}
var a = function myFunction(i){ return i}(1)
//就相当于(不完全相当,存在函数提升的区别)
function myFunction(i){
    return i
};
var a = myFunction(1);
//就是声明一个将要传入参数的函数

所以上上个代码段我们用小括号迫使js去计算了function(1)的值,然后就可以看到齐刷刷的log打印出来了。但是a里面所有元素的值都是undefined啊。那是因为我们的立即执行函数没有返回任何值给a数组。

所以我们可以让立即执行函数,返回一个函数,每当想要运行的时候就可以用a[n]()去运行。所以我们改进一下。

var a = [];
for(var i = 0; i < 10; i++) {
    a[i] = (function(temp){
        return function(){
			console.log(temp)
		}
	}(i))
}

这里我们让立即执行函数返回了一个 function(){console.log(temp)} 这里的temp是经过迫使计算好的,所以是一个实实在在的值。

a[6] = function(){console.log(6)}

所以我们执行a[6]() 就能够在控制台看到6的log了。


(留坑) (立即执行函数还是一个闭包还是啥来着,我等下去百度一下)