• JavaScript 深入理解作用域链


    第一步. 定义后:每个已定义函数,都有一个内在属性[scope],其对应一个对象的列表,列表中的对象仅能内部访问。 

    例如:建立一个全局函数A,那么A的[Scope]内部属性中只包含一个全局对象(Global Object),而如果我们在A中创建一个新的函数B,那么B的[Scope]属性中就包含两个对象,函数A的Activation Object(活动对象)对象在前面,全局对象(Global Object)排在后面。 

    简而言之,一个函数的[Scope]属性中对象列表的顺序是上一层函数的Activation Object对象,然后是上上层的,一直到最外层的全局对象。 


    第二步.执行时:当一个函数被执行的时候,会自动创建一个可以执行的对象(Execution Object),并同时绑定一个作用域链(Scope Chain)。作用域链会通过下面两个步骤来建立,用于进行标识符解析。 

    1,首先,将函数对象[Scope]内部属性中的对象,按顺序复制到作用域链Scope Chain中。 
    2,其次,在函数执行时,会创建一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于作用域链的最前面。 
    所以在Scope Chain中最后顺序是本函数的Activation Object,然后是上层函数的Activation Object,再上上层的Activation Object,一直到Global Object。 (如果在此方法内部定义一个函数,那么该函数的Scope就等于于Execution Object当中的Scope Chain)

    假如声明一个全局方法Add

                function Add() {
                    var a = "test"
                }

    那么Add的scope(作用域链) 当中只有一个(Global Object)对象,这就是为什么在add当中可以使用Global Object对象当中的属性或者方法

    JavaScript作用域链

    那么在执行Add方法的时候,执行此函数时会创建一个称为“运行期上下文(execution context)”的内部对象,运行期上下文定义了函数执行时的环境。每个运行期上下文都有自己的作用域链,用于标识符解析,当运行期上下文被创建时,而它的作用域链初始化为当前运行函数的[[Scope]]所包含的对象。

    即(execution context).Scope Chain = 该函数的Scope (红色1)

    接下来会创建一个新的Activation Object对象,这个对象中包含了this、参数(arguments)、局部变量(包括命名的参数)的定义,这个Activation Object对象会被置于作用域链的最前面。 

    JavaScript作用域链

    这样一个函数的运行期作用域链就形成了。那么如果在add方法内部在声明一个方法 比如

                function add() {
                    var a = "test"
                    function sum() {
                    }
                }

    那么 sum的 Scope 就等于 add“运行期上下文(execution context)”对象的Scope Chain 



    当执行js代码的过程中,遇到一个标识符,就会根据标识符的名称,在执行上下文(Execution Context)的作用域链中进行搜索。从作用域链的第一个对象(该函数的Activation Object对象)开始,如果没有找到,就搜索作用域链中的下一个对象,如此往复,直到找到了标识符的定义。如果在搜索完作用域中的最后一个对象,也就是全局对象(Global Object)以后也没有找到,则会抛出一个错误,提示undefined。 



    由此而来的建议: 

    1. 尽量使用局部变量,这不仅仅是涉及到私有属性的问题,局部的变量从以上过程中可以看到,能够减少搜索的时间(注:在一般的情况下,不包括浏览器的优化行为)。 

    2. 避免使用with语句。因为它会修改执行上下文(Execution Context)的作用域链,在最前面添加一个对象(Variable Object)。同理,对于try-catch语句中的catch语句块也类似。

  • 相关阅读:
    Vue.js $nextTick
    JS---函数名和变量名重名
    for循环中嵌套setTimeout,执行顺序和结果该如何理解?
    Rocket MQ整体简介
    ant Desgn Pro Vue 修改 title
    STS插件创建springboot项目,pom第一行报unkown错误
    c# 读取二进制文件并转换为 16 进制显示
    c# Winform 调用可执行 exe 文件
    按字节读取txt文件缓存区大小设置多少比较好?
    天翼云服务开放端口
  • 原文地址:https://www.cnblogs.com/daxin/p/3204190.html
Copyright © 2020-2023  润新知