zoukankan      html  css  js  c++  java
  • js_闭包

    定义

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

    函数是怎样创建的

    js在执行代码前会对代码进行预处理。例如用var声明变量和用function声明函数,这一阶段同样包括声明提升的情况。当预处理发现有函数声明时,就会创建该函数。但预处理会跳过所有函数内部代码。只有调用函数时才,执行流进入函数内部才会进行处理。

    函数创建时发生了什么

    创建一个预先包含外部环境变量对象的作用域链,这个作用域链被保存在函数内部的[[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.初始化本地活动对象并塞到作用域链最前端(索引最优先)
  • 相关阅读:
    NET在后置代码中输入JS提示语句(背景不会变白)
    corev4.css 左菜单修改CSS
    寺庙里的那点荡事儿
    sharepoint 2010中通过命令部署和卸载FEATURE
    定时任务 Timer JOB
    获取MOSS个人站点的SPWeb对象
    C#对Active Directory进行增删修查的类源码
    权限操作
    在SharePoint中,检验用户(SPUser)是否属于给定的组(SPGroup)的方法(代码)
    DirectoryEntry所有字段对应解释
  • 原文地址:https://www.cnblogs.com/Syinho/p/13229451.html
Copyright © 2011-2022 走看看