闭包是一个可以访问另一个函数范围内的变量的函数。首先要理解的是闭包是函数。因为要求它可以访问另一个函数中的变量,所以我们经常在一个函数内部创建另一个函数,“另一个函数”就是一个闭包。本文对其进行了系统的分析,有需要的朋友可以看看。
在JavaScript中,闭包是很多人无法理解的概念,甚至很多人会把闭包和匿名函数混淆。
闭包是有权访问另一个函数作用域中的变量的函数。首先要明白的就是,闭包是函数。因为要求它可以访问另一个函数中的变量,所以我们经常在一个函数内部创建另一个函数,“另一个函数”就是一个闭包。
例如,如前所述作为比较函数:
函数createComparisonFunction(property name){
返回函数(对象1,对象2){
var value 1=object 1[property name];
var value 2=object 2[property name];
if(value 1 value 2){ return=' '-1;=' ' } else=' ' if(value 1=' ' value 2){
返回1;
}否则{
返回0;
}
};
}
/value2){
在这个函数中,因为return的函数访问的是包含函数(外部函数)的变量propertyName,所以我们认为这个函数是闭包。即使闭包被返回并在别处被调用,它仍然可以访问propertyName。之所以还能访问变量propertyName,是因为内部函数(闭包)的作用域链包含了createComparisonFunction函数的作用域。所以要彻底理解闭包,需要彻底理解函数被调用时会发生什么,以及作用域链的相关知识。
当某个函数被调用时,会创建一个执行环境(函数一旦被调用,则进入函数执行环境)和相应的作用域链(作用域链是随着执行环境的不同而动态变化的)。(对于函数而言)之后使用arguments和其他命名参数的值来初始化函数的活动对象(每个执行环境都有一个变量对象,对于函数成为活动对象)。对于有闭包的函数,在作用域链中,外部函数的活动对象总是在第二位,外部函数的外部函数的活动对象总是在第三位。作为全局执行环境,直到作用域链结束。
先不说闭包。我们先通过一个简单的例子来了解作用域链、可变对象和活动对象。
函数比较(值1,值2){
如果(值1值2){
return-1;
}else if(value1value2){
返回1;
}否则{
返回0;
}
}br var result=compare(5,10);
上面的代码首先定义了compare()函数,然后在全局范围内调用它。调用compare函数时,首先创建一个函数执行环境,每个执行环境对应这个变量对象。也就是说,作用域链和函数执行环境是同时创建的,作用域链的前端是比较函数的活动对象(在函数中,变量对象也称为活动对象)。比较活动对象包含arguments,value1和value2(关键:arguments数组对象虽然包含value1和value2,但是我们应该把它们分开列出,而不是仅仅认为比较活动对象只包含arguments,因为value1和value2也包含在比较活动对象中)。
对于上面的代码,全局执行环境的变量对象(还是那句话:每个执行环境都有对应的变量对象)包含result和compare,这是compare()执行环境的作用域链中的第二个位置。
当我们创建compare()函数时,我们会预先创建一个包含全局变量对象的作用域链。这个作用域链保存在compare函数内的[[scope]]属性中。调用compare函数时,我们会为函数创建一个执行环境,然后通过复制函数的[[scope]]属性中的对象来构建执行环境的作用域链。如下所示:
作用域链的本质就是一个指向变量对象的指针列表,仅指但实际不包含可变对象。每当在函数中访问一个变量时,就会从作用域链的前端开始沿着作用域链搜索具有相应名称的变量。我们知道,全局环境中的变量对象总是存在的,而局部环境(如compare()函数执行环境)中的变量对象只在函数执行时存在。一旦执行完成,局部变量对象(活动对象)将被销毁。但是在闭包里,就不一样了。
复制博文开头的代码,如下所示:
函数createComparisonFunction(property name){
返回函数(对象1,对象2){
var value 1=object 1[property name];
var value 2=object 2[property name];
如果(值1值2){
return-1;
}else if(value1value2){
返回1;
}否则{
返回0;
}
};
}
因为函数内部定义的函数会将包含该函数的活动对象(即外部函数)添加到其作用域链中。因此,createComparisonFunction函数内部定义的匿名函数的作用域实际上包含了外部函数的活动对象。如果我们执行下面的代码:
var compare=createComparisonFunction(' name ');
var result=compare({name:'zzw'},{ name:' ht ' });
此时,匿名函数的作用域链将引用外部函数的活动对象。因为匿名函数从外部函数返回后,其作用域链被初始化为包含外部函数的活动对象和全局变量对象。这样,匿名函数可以访问外部函数中定义的所有变量。更重要的是,即使在外部函数执行之后,它的活动对象也不会被销毁,因为匿名函数的作用域链仍然引用这个活动对象。换句话说,当createComparison()函数返回后,其执行环境的作用域链会被销毁,但是她的活动对象仍然保存在内存中。等到倪敏函数被销毁后,外部函数的活动对象才会被销毁。
由于闭包会携带者包含他的函数的作用域,因此回避其他函数占用更多的内存。过度的使用闭包可能会导致内存占用过多,我们建议只在绝对必要的时候再考虑使用闭包。
模拟块级范围
(函数(){
var now=new Date();
if(now . getmonth()==0 now . getdate()==1){
alert(“新年快乐”);
}
})();
这是对块级作用域的模仿,即定义一个匿名函数并立即调用。
演示其功能如下:
函数输出数(计数){
(函数(){
for(var I=0;icounti ){
console.log(一);
}
})();
console.log(一);
}
output numbers(5);
这是模仿块级范围之外的console.log(i)会导致错误,因为I是未定义的。它显示了在模拟块级范围后内部变量被破坏。
这就是本文的全部内容。希望这篇文章的内容能给大家的学习或者工作带来一些帮助。有问题可以留言交流。同时希望大家多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。