手机版

浅谈JavaScript的闭包功能

时间:2021-09-07 来源:互联网 编辑:宝哥软件园 浏览:

在JavaScript中,闭包是一个很多人都无法理解的概念,甚至很多人把闭包和匿名函数混为一谈。

闭包是可以访问另一个函数范围内的变量的函数。首先要理解的是闭包是函数。因为需要访问另一个函数中的变量,所以我们经常在函数内部创建另一个函数,“另一个函数”就是闭包。

例如,如前所述,作为比较函数:

函数createComparisonFunction(property name){返回函数(object1,object 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){ return 1;} else { return 0;} };}/value2){在这个函数中,因为return的函数访问包含函数(外部函数)的变量propertyName,所以我们认为这个函数是闭包。即使这个闭包在别处被返回和调用,它仍然可以访问propertyName,之所以还可以访问变量propertyName,是因为内部函数(闭包)的作用域链包含了createComparisonFunction函数的作用域。因此,为了彻底理解闭包,我们需要彻底理解函数被调用时发生了什么,以及范围链的知识。

函数被调用时,会创建一个执行环境(函数一旦被调用,就会进入函数执行环境)和相应的作用域链(作用域链随不同的执行环境而动态变化)。(对于函数)然后使用参数和其他命名参数的值来初始化函数的活动对象(每个执行环境都有一个变量对象,它成为函数的活动对象)。对于带闭包的函数,在作用域链中,外部函数的活动对象总是排在第二位,外部函数的外部函数的活动对象总是排在第三位。直到作为范围链端点的全局执行环境。

让我们从一个简单的例子开始,来理解范围链、变量对象和活动对象。

函数比较(值1,值2){ if(值1值2){ return-1;} else if(value 1 value 2){ return 1;} else { return 0;}}br var result=compare(5,10);上面的代码首先定义了compare()函数,然后在全局范围内调用它。调用compare函数时,先创建一个函数执行环境,每个执行环境对应这个变量对象,也就是说作用域链和函数执行环境是同时创建的,作用域链的前端是compare函数的活动对象(在函数中,变量对象也称为活动对象)。Compare activity对象包含参数value1和value2(关键:虽然arguments array对象包含value1和value2,但我们应该单独列出它们,而不是仅仅认为compare activity对象中只包含参数,因为value1和value2也包含在compare activity对象中)。

就上述代码而言,全局执行环境的变量对象(同样,每个执行环境中都有一个对应的变量对象)包含result和compare,这是compare()执行环境的作用域链中的第二位。

当我们创建compare()函数时,我们将提前创建一个包含全局变量对象的范围链,它将存储在compare函数内部的[[scope]]属性中。当我们调用compare函数时,我们将为函数创建一个执行环境,然后通过复制函数[[scope]]属性中的对象来构建执行环境的范围链。如下所示:

范围链的本质是指向变量对象的指针列表,它只引用变量对象,但实际上并不包含变量对象。每当在函数中访问变量时,将从作用域链的前端沿着作用域链搜索具有相应名称的变量。我们知道,全局环境中的变量对象总是存在的,而局部环境(如compare()函数执行环境)中的变量对象只在函数执行时存在。一旦执行完成,局部变量对象(活动对象)将被销毁。但是在闭包中,情况就不一样了。

复制博文开头的代码,如下所示:

函数createComparisonFunction(property name){返回函数(object1,object 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){ return 1;} else { return 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('新年快乐');}})();这是模仿块级作用域,即定义并立即调用匿名函数。

以下是为了证明其作用:

函数outputNumbers(count){(function(){ for(var I=0;icountI){ console . log(I);} })();console . log(I);} output numbers(5);这是console.log(i),不在模拟块级别的范围内,将会导致错误,因为I没有定义。这意味着内部变量在模拟块级作用域执行后被销毁。

以上就是本文的全部内容。希望这篇文章的内容对你的学习或工作有所帮助。有问题可以留言交流,希望多多支持我们!

版权声明:浅谈JavaScript的闭包功能是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。