闭包
定义:闭包是指有权访问另一个函数作用域中的变量的函数。
创建:创建闭包的常见方式,就是在一个函数内部创建另一个函数。
理解:
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;
}
};
}
如上例子中,代码第3、4行表示内部函数(一个匿名函数)可以访问外部函数中的变量propertyName;因为内部函数的作用域链中包含createComparisonFunction()的作用域。
//创建函数
var compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({name:"AA"},{name:"BB"});
console.log(compareNames);//f(object1,object2){...}
console.log(result);//-1
//解除对匿名函数的引用(以便释放内存)
compareNames = null;
①匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化包含createComparisonFunction()函数的活动对象和全局变量对象,所以匿名函数可以访问在createComparisonFunction()中定义的所有变量;
②createComparisonFunction()函数在执行完毕后,其活动对象不会被销毁仍留在内存中,因为匿名函数作用域链仍在引用这个活动对象;(将compareNames设置为null可以解除对匿名函数的引用,匿名函数作用域被销毁则createComparisonFunction函数作用域也被销毁)
下图7-2展示了调用compareNames()的过程产生的作用域链间的关系。
缺点:由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存;过度使用闭包可能会导致内存占用过多。
闭包与变量
定义:闭包只能取得包含函数中任何变量的最后一个值。
原因:闭包所保存的是整个变量对象,而不是某个特殊的变量。
理解:
function createFunctions(){
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function(){
return i;
};
}
return result;
}
var a = createFunctions();
a.forEach(e=>{
console.log(e());
})//输出10个10
如上代码,全局变量a的值为一个函数数组,表面上看每个函数都应该返回自己的索引值,实际上每个函数都返回10;因为每个函数的作用域链中都保存的是对createFunctions()函数活动变量的引用,它们引用的是同一个变量i,当createFunctions()函数返回后i的值为10,此时每个函数内部i的值为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;
}
var a = createFunctions();
a.forEach(e=>{
console.log(e());
})//输出0、1、2、....、8、9
重写createFunctions()函数后,每个函数会返回不同的索引值;如上代码,在闭包外定义了一个匿名函数,且传参数num,在调用内个匿名函数时传入变量i;由于函数参数是按值传递的,将变量i的当前值复制给参数num;因此函数数组中每个函数都有自己num变量的一个副本,返回各自不同的num值。
关于this对象
定义:this对象是在运行时基于函数的执行环境绑定的;
①全局函数中:this等于window;
②函数被作为对象的方法调用时:this等于那个对象;
③匿名函数中:this等于window;(匿名函数的执行环境具有全局性)
理解:
var name = "Window";
var object = {
name: "Object",
getName: function(){
return function(){
return this.name;
};
}
};
console.log(object.getName()());//"Window"(非严格模式下)
如上代码,getName()返回一个匿名函数,匿名函数中this对象指向window,因此调用object.getName()()就会立即调用它返回的函数,返回window对象中的变量name的值“Window”;
把外部作用域中的this对象保存在一个闭包能够访问到的变量里,闭包就可以访问该对象了,如下所示。
var name = "Window";
var object = {
name: "Object",
getName: function(){
var that = this;
return function(){
return that.name;
};
}
};
console.log(object.getName()());//"Object"
如上代码,在函数中声明that变量并把this对象赋值给它(此时this指向object),因此闭包可以访问这个变量that且that引用object,返回的是object的name值;通过call()或apply()改变函数执行环境也可使this指向其他对象。