ECMAScript中经常把闭包与匿名函数混用,所以很多时候会搞不清这两个概念
闭包是指有权访问另一个函数作用域中的变量的函数。——《JS高程》
Closures (闭包)是使用被作用域封闭的变量,函数,闭包等执行的一个函数的作用域。通常我们用和其相应的函数来指代这些作用域。(可以访问独立数据的函数)
闭包是一个函数和声明该函数的词法环境的组合。从理论角度来说,所有函数都是闭包。
——MDN https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
函数表达式
JavaScript中声明函数有两种方式
// 1.函数声明
function functionName(arg0, arg1, arg2) {
//函数体
}
// 2.匿名函数赋值
var functionName = function(arg0, arg1, arg2){
//函数体
}
作用域链
在函数执行过程中,需要在作用域链中查找变量:
function compare(value1, value2){
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
}
var result = compare(5, 10);
当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象);
闭包
而闭包有所不同
闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。
function createComparisonFunction(propertyName) {
return function(object1, object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });
在createComparisonFunction
这个函数中,返回的匿名函数赋值给了compare
这个变量,我们可以说compare
是一个闭包。
返回的匿名函数的作用域链中可以访问在createComparisonFunction
中的所有变量,而且函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。
直到匿名函数被销毁后,createComparisonFunction()的活动对象才会被销毁;
//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
首先,创建的比较函数被保存在变量compareNames 中。而通过将compareNames 设置为等于null
解除该函数的引用,就等于通知垃圾回收例程将其清除。随着匿名函数的作用域链被销毁,其他作用域
(除了全局作用域)也都可以安全地销毁了。
闭包与匿名函数
在开发中,我们常会用到这样的写法
(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());
这里声明匿名函数立即执行,使得局部变量不会污染到全局变量,并可以访问全局变量(外部变量);
var num = 1;
(function () {
var num = 2;
console.log(num); // 2
}())
console.log(num); // 1
匿名函数通常与闭包一起使用,但并无必然联系;
因为闭包保存的是变量对象,所以我们往往要用匿名函数立即执行来保存过程中的值;(见需要注意)
闭包的实用
想了想,自己总结的不如直接看文档。
MDN 文档 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures
- 在函数中给事件驱动型的变量添加函数;
- 模拟私有方法
需要注意
作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。 ——《JS高程》
因为闭包所保存的是整个变量对象;
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
//都是10
我们必须通过匿名函数的立即执行来进行保存
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
//1-10
当然,ES6可以使用let来替代
function createFunctions(){
var result = new Array();
for (let i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}