javascript——闭包
闭包的原理特性
闭包:是指有权访问另一个函数作用域中的变量的函数。
创建闭包的常见方式:在一个函数内部创建另一个函数
分析上面的话,划重点:闭包是一个函数,函数,函数。重要事情说三遍。然后就是它有特权,可以访问另一个函数作用域的变量。其实这个另一个函数就是外部函数。
之前一篇文章讲到了作用域和作用域链。https://blog.csdn.net/viewyu12345/article/details/79653587
来解释闭包为何有这种特殊性。当一个函数被调用的时候,我们知道会创建一个执行环境及相应的作用域链,使用arguments和其他命名参数的值来初始化函数的活动对象。作用域链的末端永远是是全局对象,开始端就是当前的活动对象。咱调用函数不就是处理数据之类的,但是处理数据就涉及到访问数据。那么这个作用域链就规定了你如何访问(从初始端往上),哪些可以访问(在作用域链中的数据才能访问到)。
一般来说,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。半路杀出个程咬金,闭包逆天了,就不同!
闭包将会包含外部函数的活动对象
function compare(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;
}
};
}
语言太苍白,还是代码比较直观点。上面这个代码内部返回的就是一个闭包。可以看到,里面还使用了外部函数的变量propertyName。之所以能够访问这个变量,其实就是上面说的,包含了外部函数的作用域。
分析一下这个匿名函数执行的时候的作用域链,就一目了然了,这里作用域链的作用必须明白。
左侧的黑色圆圈没有任何意义,只是为了形象的表达作用域这个链。便于理解吧。
这样从图中就可以知道,外部函数的作用域还是存在在匿名函数的作用域链中。即使外部函数执行完成,外部函数的作用域链销毁,但是匿名函数没有。
闭包的缺陷
内存问题
由于闭包的特殊性,会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,所以我们有必要在在不用的时候释放内存,以便垃圾回收器回收。
//创建函数
var compareNames = compare("name");
//调用函数
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
访问变量问题
闭包只能取得包含函数中任何变量的最后一个值。
上经典的循环读取变量问题
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
老手看这个太简单了,但是新手会觉得result数组返回的就是返回0-10的函数 ,其实全是返回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;
}
稍稍改动一下返回的函数。这时候,我们每次都会传递i进去,参数我们知道其实就是副本,那么num每次都是不一样的。在这个函数里面我们又创建了一个闭包,来每次的访问这个num,因此达到我们想要的效果。