- 概念
闭包是指可以包含自由变量(不是某个对象属性的变量)的代码块,这些变量在代码块对应的作用域链中定义,因此不一定在这个代码块内定义,也不一定是全局变量。那么什么是作用域链呢?
- 作用域链
代码块在一个上下文环境A中定义,这个上下文环境就是一个外层的作用域A,其可能是全局作用域,也可能是另一个代码块的内部作用域,而这个定义代码块的上下文环境A,也可能定义在另一个上下文环境B中,对应一个新的作用域B。代码块在执行时,如果用到了自由变量,会先在自己的作用域中寻找变量定义,如果找不到,则在外层的作用域A中去寻找变量定义,如果没有找到,继续到作用域A外层的作用域B中去寻找,直到最外层的全局作用域,这些作用域就构成了代码块的作用域链。
下面代码中,函数f的作用域链就是:函数f内部作用域 -> 函数addFn内部作用域 -> 全局作用域。
var addFn = (function() { var data = { count: 10 } var f = function(plus) { return data.count + plus; } return f;})();var data = { count: 20}console.log(addFn(2));
- 变量取值时机
函数执行时,用到的自由变量需要到函数对应的作用域链中去取值,注意,是在函数执行的那个时刻,自由变量在作用域链中的取值。
var it = function() { var list = []; for (var i = 0; i < 10; i++) { list.push(function(){ console.log(i); }); } return list;}var list = it();for (var i = 0; i < 10; i++) { list[i]();}
看上面这段代码,输出结果都是10,因为在函数执行的时刻,自由变量i在作用域链中的取值都是10。如果想要输出结果是0~9,代码需要改成下面这样:
var it = function() { var list = []; for (var i = 0; i < 10; i++) { (function(val) { list.push(function(){ console.log(val); }); })(i); } return list;}var list = it();for (var i = 0; i < 10; i++) { list[i]();}