资讯专栏INFORMATION COLUMN

关于js闭包

livem / 883人阅读

摘要:在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,直到作用域链终点即全局执行环境。更为重要的是函数在执行完毕后,其他活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。

原文链接----请点这里

  闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式,就是在一个函数内部创建另一个函数。
  之所以一个内部的函数可以访问其外部的变量,而且在其被返回或是调用时还可以访问,是因为这个内部函数的作用域链中包含外部函数的作用域。

知识储备

在了解闭包之前,先要熟悉以下几点:
  1. 首先要理解执行环境,执行环境定义了变量或函数有权访问的其他数据。
  2. 每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
  3. 每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入到一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
  4. 当某个函数被调用时,会创建一个执行环境及其相应的作用域链。然后使用arguments和其他命名参数的值来初始化函数的活动对象。在函数中,活动对象作为变量对象使用(作用域链是由每层的变量对象链起来的)。
  5. 在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,直到作用域链终点即全局执行环境。
  6. 作用域链的本质是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。

1.一般情况下

  不谈论闭包,一般的,从在全局执行环境创建一个函数开始。
  在创建一个函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在函数内部的[[Scope]]
  然后执行流进入这个函数,函数的执行环境被压入环境栈中,此函数执行环境的活动对象作为变量对象被创建并推入执行环境作用域链的前端。
  对这个例子中的函数而言,其作用域链中包含两个变量对象:本地活动对象和全局变量对象。
  无论在什么时候在函数中访问变量时,会从作用域链搜索变量名。
  一般情况下,函数执行完,局部活动对象就会被销毁,内存中仅有全局作用域(里边只有全局执行环境的变量对象)。
  以下面这段代码为例:

function compare (value1, value2) {         
//创建一个预先包含全局变量对象的作用域链,保存在[[Scope]]
    if (value1 < value2) {
//访问函数变量时,即在代码最后一条语句执行过程中,会从作用域链前端开始搜索变量名
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
}
var result = compare(5, 10);
//执行流进入函数时,compare的执行环境压入环境栈
//compare执行环境的活动对象作为变量对象接到作用域链的前端
//函数执行完,compare执行环境弹出栈,compare活动对象销毁

如图,作用域链从0开始向后查找:

2.产生闭包的情况下

如下是一个以属性名作为参数,按其属性的值对数据进行排序的函数:

function createComparisonFunction(propertyName) {     
    return function(object1,object2){         //返回一个匿名函数
        var value1=object1[propertyName];
        var value2=object2[propertyName];
        if(value1value2){
            return 1;
        } else {
            return 0;
        }
    };
}
var data=[{name:"Zachary",age:28},{name:"Nicholas",age:29}];
data.sort(createComparisonFunction("name"));
console.log(data[0]);           //Object {name: "Nicholas", age: 29}
data.sort(createComparisonFunction("age"));
console.log(data[0]);           //Object {name: "Zachary", age: 28}

createComparisonFunction()函数和返回的匿名函数的作用域链如下图所示:

  在匿名函数从createComparisonFunction()中被返回后,它的作用域链被初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。这样,匿名函数就可以访问在createComparisonFunction()中定义的所有变量。

更为重要的是:

  createComparisonFunction()函数在执行完毕后,其他活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。

  当createComparisonFunction()函数返回后,其执行环境的作用域链会被销毁,但它的活动对象仍然会留在内存中;直到匿名函数被销毁,createComparisonFunction()的活动对象才会被销毁。

  例如以下代码,返回的匿名函数被保存在变量compareNames中,通过将compareNames设置为null来解除对匿名函数的引用,解除引用之后垃圾回收例程将会清除该匿名函数,随之该匿名函数的作用域链也会被销毁,则其作用域链上的其他作用域也会安全的销毁(全局作用域除外)。

var compareNames = createComparisonFunction("name");
var result = compareNames({ name: "Nicholas" }, { name: "Greg" });
compareNames = null;

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87055.html

相关文章

  • 关于js中的闭包问题

    摘要:闭包解决的问题获取到函数内部的变量让变量不会随着函数执行完毕就销毁而是保存在内存中模拟私有变量闭包产生的问题容易造成内存泄漏,保存的多了,却没有释放的指向问题,在闭包里,指向的是关于闭包的小东西比如注释部分省了外壳, 闭包概念:能够访问另一个函数作用域的变量的函数,来个栗子 showImg(https://segmentfault.com/img/bVbvnmY?w=604&h=245...

    icyfire 评论0 收藏0
  • js知识梳理6:关于函数的要点梳理(2)(作用域链和闭包)

    摘要:在此例中,在匿名函数被返回后,它的作用域链初始化为包含函数的活动对象和全局变量对象。函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象,结果就是只是的执行环境的作用域链会被销毁,其活动对象会留在内存中。 写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,...

    aristark 评论0 收藏0
  • JS 中的闭包是什么?

    摘要:大名鼎鼎的闭包面试必问。闭包的作用是什么。看到闭包在哪了吗闭包到底是什么五年前,我也被这个问题困扰,于是去搜了并总结下来。关于闭包的谣言闭包会造成内存泄露错。闭包里面的变量明明就是我们需要的变量,凭什么说是内存泄露这个谣言是如何来的因为。 本文为饥人谷讲师方方原创文章,首发于 前端学习指南。 大名鼎鼎的闭包!面试必问。请用自己的话简述 什么是「闭包」。 「闭包」的作用是什么。 首先...

    Enlightenment 评论0 收藏0
  • 翻译连载 | JavaScript轻量级函数式编程-第7章: 闭包vs对象 |《你不知道的JS》姊妹

    摘要:相像闭包和对象之间的关系可能不是那么明显。一个没有对象的编程语言可以用闭包来模拟对象。事实上,表达一个对象为闭包形式,或闭包为对象形式是相当简单的。简而言之,闭包和对象是状态的同构表示及其相关功能。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,...

    leanxi 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<