zoukankan      html  css  js  c++  java
  • JS进阶之---执行上下文,变量对象,变量提升

     

    一、结构顺序大体介绍

      JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段。

        编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定。

        执行阶段由引擎完成,主要任务是执行可执行代码,执行上下文在这个阶段创建。执行上下文也分为创建阶段和执行阶段。

          1.首先进入全局环境,创建一个全局执行上下文,全局变量对象window,全局作用域Global,确定this指向,this==window。

          2.在执行阶段,会完成变量赋值,函数引用,以及执行其他代码等。浏览器的断点调试只能用于执行阶段。

          3.在执行阶段,如果遇到函数引用,会进入函数环境,创建一个执行上下文。而在这个执行上下文中,也分为创建阶段和执行阶段。接下来我们主要讨论函数环境的创建阶段和执行阶段,不考虑全局环境,因为全局环境也可以理解为在一个大的函数环境中。行为基本一致。

     

    二、执行上下文

      每次当Js引擎转到可执行代码的时候,就会进入一个执行上下文。执行上下文可以理解为当前代码的执行环境,它会形成一个作用域。JavaScript中的运行环境大概包括三种情况。

    • 全局环境:JavaScript代码运行起来会首先进入该环境
    • 函数环境:当函数被调用执行时,会进入当前函数中执行代码
    • eval

      当代码在执行过程中,遇到以上三种情况,都会生成一个执行上下文,放入栈中,我们称其为函数调用栈(call stack)。栈底永远都是全局上下文,而栈顶就是当前正在执行的上下文。处于栈顶的上下文执行完毕之后,就会自动出栈。

      注意:函数中,遇到return能直接终止可执行代码的执行,因此会直接将当前上下文弹出栈。

      执行上下文特点:

        • 单线程
        • 同步执行,只有栈顶的上下文处于执行中,其他上下文需要等待
        • 全局上下文只有唯一的一个,它在浏览器关闭时出栈
        • 函数的执行上下文的个数没有限制
        • 每次某个函数被调用,就会有个新的执行上下文为其创建,即使是调用的自身函数,也是如此。
     

      当调用一个函数时,一个新的执行上下文就会被创建。而一个执行上下文的生命周期可以分为两个阶段。
        创建阶段
:在这个阶段中,执行上下文会分别创建变量对象,建立作用域链,以及确定this的指向。
        代码执行:阶段
创建完成之后,就会开始执行代码,这个时候,会完成变量赋值,函数引用,以及执行其他代码。

          

        

    三、变量对象

      在执行上下文的创建阶段,会进行变量对象的创建。

        而变量对象的创建过程又分为:
          1.参数对象创建。建立arguments对象,检查当前上下文中的参数,建立该对象下的属性与属性值。

          2.函数声明。检查当前上下文的函数声明,也就是使用function关键字声明的函数。在变量对象中以函数名建立一个属性,属性值为指向该函数所在内存地址的引用。如果函数名的属性已经存在,那么该属性将会被新的引用所覆盖。
function声明会比var声明优先级更高一点。
          3.变量声明。检查当前上下文中的变量声明,每找到一个变量声明,就在变量对象中以变量名建立一个属性,属性值为undefined。如果该变量名的属性已经存在,为了防止同名的函数被修改为undefined,则会直接跳过,原属性值不会被修改。

      在执行上下文的执行阶段,进行变量对象的赋值,函数的引用等。

        进入执行阶段之前,变量对象中的属性都不能访问!但是进入执行阶段之后,变量对象转变为了活动对象,里面的属性都能被访问了,然后开始进行执行阶段的操作。

        变量对象和活动对象其实都是同一个对象,只是处于执行上下文的不同生命周期。

    四、举个例子: 

    function test() {
        var a = 1;
        function foo() {
            return 2;
        }
    }
    
    test();

      我们直接从test()的执行上下文开始理解。全局作用域中运行test()时,test()的执行上下文开始创建。为了便于理解,我们用如下的形式来表示。 

      创建阶段:

    testEC = {
        // 变量对象
        VO: {},
        scopeChain: {},
        this: {}
    }
    
    // 因为本文暂时不详细解释作用域链和this,所以把变量对象专门提出来说明
    
    // VO 为 Variable Object的缩写,即变量对象
    VO = {
        arguments: {...},  //参数对象。注:在浏览器的展示中,函数的参数可能并不是放在arguments对象中,这里为了方便理解,我做了这样的处理
        foo: <foo reference>  // 表示foo的地址引用
        a: undefined
    }

      执行阶段:

    VO ->  AO   // Active Object 即活动对象
    AO = {
        arguments: {...},
        foo: <foo reference>,
        a: 1
    }

    五、变量提升

      变量提升是将变量声明提升到它所在作用域的最开始的部分。如:

    console.log(a);  //undefined
    console.log(b); //Uncaught ReferenceError: b is not defined
    var a = 1;

      代码执行分为创建和执行两个阶段,代码开始执行的时候,创建阶段已经完成,所以变量对象已经创建完成。此时a是undefined,而b是没有的。

      所以也就解释了变量提升的现象,因为在执行的时候,所有的变量对象早已创建完成,只是还没有被赋值。

     

  • 相关阅读:
    使用docker搭建nginx集群,实现负载均衡
    机器重启以后,主从出现报错:ERROR 1872 (HY000): Slave failed to initialize relay log info structure from the repos
    Slave_SQL_Running: No mysql同步故障解决方法
    docker搭建MySQL主从集群
    Linux下如何查看版本信息
    Docker中使用CentOS7镜像
    在 CentOS 上安装及使用 VirtualBox
    Docker Machine 安装使用教程
    main主函数
    is and ==
  • 原文地址:https://www.cnblogs.com/lishuxue/p/6558788.html
Copyright © 2011-2022 走看看