码迷,mamicode.com
首页 > Web开发 > 详细

js_闭包

时间:2020-07-04 01:25:03      阅读:68      评论:0      收藏:0      [点我收藏+]

标签:com   索引   开始   属性   loading   预处理   声明   指针   进入   

定义

闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式是在一个函数中创建另一个函数

函数创建时发生了什么

创建一个预先包含全局环境变量对象的作用域链,这个作用域链被保存在函数内部的[[scope]]属性中。

函数被调用时发生了什么

函数被调用,即执行流进入函数中

  • 为函数创建一个执行环境,复制函数内部的[[scope]]属性中的对象构建起函数执行环境的作用域链。
  • 使用arguments和其它命名参数的值来初始化函数的活动对象。
  • 将函数的活动对象(在此作为变量对象使用)推入执行环境作用域链的前端。
    技术图片

技术图片

如上例所示,作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。在函数中访问变量时,会从作用域链中从本地活动变量一直搜索到全局变量对象。函数执行完毕后,局部活动对象被销毁,内存中仅保存全局执行环境和它的变量对象。

在函数内部创建函数形成闭包后,作用域链是怎样的

        function outer(propertyAge) {
            return function (obj1, obj2) {
                var val1 = obj1[propertyAge];
                var val2 = obj2[propertyAge];
                if (val1 > val2) {
                    console.log(`${obj1.name}比较大`);
                } else if (val1 < val2) {
                    console.log(`${obj2.name}比较大`);
                } else {
                    console.log(`${obj1.name}与${obj2.name}一样大`);
                }
            }
        }
        var person1 = {
            name: ‘Nick‘,
            age: 27
        };
        var person2 = {
            name: ‘Jane‘,
            age: 25
        };
        var compare = outer(‘age‘);
        compare(person1, person2); //‘Nick比较大‘

从头开始捋:
1 创建outer函数时,将全局作用域的作用域链(即全局执行环境下的变量对象的指针)的副本加入到函数的内部的[[scope]]属性中。
2 调用outer函数,为outer函数创建一个执行环境,创建作用域链,复制函数内部的[[scope]]属性的变量对象的指针,把它塞进作用域链。
3 将outer函数的arguments对象和其它命名参数的值初始化为活动变量,作为当前局部作用域的变量对象,并把它塞进作用域链的前端。(此时的outer函数的作用域链拥有本地活动对象和全局变量对象的指针)
4 执行outer函数完毕,将内部匿名函数返回给全局变量compare。执行流进入到outer函数内时便会对局部作用域执行预处理(js预处理不会进入到函数内部,全局作用域预处理阶段outer函数连局部作用域都没有创建,当代码执行流进入到outer函数内部后才会创建匿名函数),内部函数因为函数声明提升会被直接创建(执行流进入到outer函数中时,匿名函数就被创建了)。执行创建函数的流程————将外部作用域的作用域链的副本存入到自己的[[scope]]属性中去。执行return语句后执行流会直接跳出当前作用域,outer函数已经完成使命,其作用域被直接销毁,作用域中保存的作用域链也会被直接被销毁。
5 outer函数的作用域和作用域链虽然被销毁了,但是对outer函数作用域链中保存的全局变量对象和outer函数本地活动对象的引用被复制保存到匿名函数中了(这些变量对象和活动对象被完好地保存在堆内存中,销毁的只是outer函数作用域链对它们的引用的指针),对于对象而言,只要引用存在就不会被销毁。
6 调用匿名函数,创建匿名函数的作用域,创建该作用域的作用域链,将[[scope]]属性中的作用域链副本塞进作用域链,使用arguments对象和其它命名参数的值初始化本地活动对象,并将本地活动对象视为变量对象,塞进匿名函数作用域链的最前端。此时的匿名函数作用域链按索引优先级包括了本地活动对象,outer函数的活动对象,全局变量对象。也因此匿名函数可以访问到在outer函数和全局作用域中定义的变量,函数以及参数。完成闭包操作。

需要注意的地方

  • js预处理不会进入函数。当执行流进入函数创建局部作用域后才会对局部作用域进行预处理,才会创建匿名函数。
  • 创建函数时只会做一件事,保存外部作用域的副本到函数的[[scope]]内部属性中。
  • 调用函数执行的操作:
    1.创建局部作用域。
    2.复制[[scope]]中保存的作用域链。
    3.初始化本地活动对象并塞到作用域链最前端(索引最优先)

js_闭包

标签:com   索引   开始   属性   loading   预处理   声明   指针   进入   

原文地址:https://www.cnblogs.com/Syinho/p/13229451.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!