闭包
要理解闭包,首先要理解 JavasSript 的特殊的变量作用域。
变量的作用域无非就两种:全局变量和局部变量。
在一个函数运行时,在调用刚开始时,会自动创建一个新的词法环境以存储这个调用的局部变量和参数。
当代码要访问一个变量时 —— 首先会搜索内部词法环境,然后搜索外部环境,然后搜索更外部的环境,以此类推,直到全局词法环境。
如果在任何地方都找不到这个变量,那么在严格模式下就会报错(在非严格模式下,为了向下兼容,给未定义的变量赋值会创建一个全局变量)。
JavasSript 语言的特别之处就在于:函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
function a(){
var n=999;
function b(){
alert(n); // 999
}
}
注意点: 在函数内部声明变量的时候,一定要使用 var 命令。如果不用的话,你实际上声明的是一个全局变量!
闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
为什么 JavaScript 中的所有函数都是闭包的?
JavaScript 中的函数会自动通过隐藏的 [[Environment]] 属性记住创建它们的位置,所以它们都可以访问外部变量。
[[Environment]]:内见属性
- 每个函数都有;
- 存放词法环境的作用;
- v8引擎中无法读写;
[[Environment]]叫词法环境对象:
- 整个脚本文件执行前会产生一个
- 函数实例创建后会产生一个
[[Environment]]属性记录了:当前函数的词法环境对象==>外层函数的词法环境对象==>全局的词法环境对象;这样就形成了作用域链。
有一个例外: 如果我们使用
new Function创建一个函数,那么该函数的[[Environment]]并不指向当前的词法环境,而是指向全局环境。因此,此类函数无法访问外部(outer)变量,只能访问全局变量。
使用闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。