JS-ES6(1)

作者: tokG | 来源:发表于2020-12-14 20:34 被阅读0次

    看了一些es6的相关,作下笔记总结.

    当我们看到一串JavaScript 代码,它是按照顺序执行的吗?

    执行上下文

    只有理解了执行上线文,才能更好地理解 JavaScript 语言本身,比如变量提升、作用域和闭包等。

    从 JavaScript 代码的顺序执行讲起,一步步了解 JavaScript 是怎么运行的。

    showName();console.log(myName);
    var myName = 'tokgogogo';
    function showName() {    console.log('函数 showName 被执行');}
    

    JavaScript 是按照顺序执行的。如果按照这个逻辑来理解:

    • 当执行到第一行的时候,由于函数 showName 未定义,所以执行应该报错;

    • 同样执行第二行的时候,由于变量 myName 未定义,所以同样也会报错。

    image

    然而实际的结果,先输出了“函数 showName 被执行”,又输出了 undefined 。

    根据上面的结果,函数或者变量可以在定义之前使用。那么,如果使用没有定义的变量或者函数,JavaScript 代码还能继续执行吗?

    showName();console.log(myName);
    function showName() {    console.log('函数 showName 被执行');}
    

    再次执行这段代码,JavaScript 引擎会报错:myName is not defined 。

    由上述两个执行结果来看,可以得到如下三个结论:

    1. 在执行过程中,若使用了未声明的变量,那么 JavaScript 执行会报错。

    2. 在一个变量定义之前使用,不会出错,但是该变量的值会为 undefined ,而不是定义的值。

    3. 在一个函数定义之前使用,不会出错,且函数能正确执行。

    变量提升

    var myname = 'tokgogogo';
    

    这段代码可以看成两行代码组成:

    var myname; // 声明部分
    myname = 'tokgogogo'; // 赋值部分
    

    上面是变量的声明和赋值,函数的声明和赋值:

    function foo() {    console.log('foo');}
    var bar = function() {    console.log('bar');}
    

    第一个函数 foo 是一个完整的函数声明,也就是说没有涉及到赋值操作;

    第二个函数是先声明变量 bar ,再把函数体赋值给 bar :

    image

    所谓的变量提升,是指在 JavaScript 代码执行过程中, JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是 undefined 。

    /** * 变量提升部分 */
    // 把变量 myName 提升到开头
    // 同时给 myName 赋值 undefinedvar 
    myName = undefined;// 把函数 showName 提升到开头
    function showName() {  console.log('函数 showName 被执行');}
    /** * 可执行部分 */
    showName();console.log(myName);
    // myName 赋值语句myName = 'tokgogogo';
    

    对原来的代码主要做了两处调整:

    • 第一处是把声明的部分都提升到了代码开头,如变量 myName 和函数 showName ,并给变量设置默认值 undefined ;

    • 第二处是移除原本声明的变量和函数,如 var myName = 'tokgogogo' 的语句,移除了 var 声明,整个移除 showName 的函数声明。

    通过这两步,就可以实现变量提升的效果。因此,这就是为什么可以在定义之前使用变量或者函数的原因——函数和变量在执行之前都提升到了代码开头

    JS 代码的执行流程

    实际上,变量和函数在声明在代码里的位置是不会变的,而是在编译阶段被 JavaScript 引擎放入内存中

    <u data-slate-mark="true">一段 JavaScript 代码在执行之前需要被 JavaScript 引擎编译,</u><u data-slate-mark="true">编译</u><u data-slate-mark="true">完成后,才会进入</u><u data-slate-mark="true">执行</u><u data-slate-mark="true">阶段。</u>

    image

    编译阶段

    编译阶段和变量提升的关系。

    变量提升部分的代码

    var myName = undefined;
    function showName() {
      console.log('函数 showName 被执行');
    }
    

    执行部分的代码

    showName();
    console.log(myName);
    myName = 'tokgogogo';
    

    输入一段代码,经过编译后,会生成两部分内容:执行上下文(Execution context)可执行代码

    执行上下文是 JavaScript 执行一段代码时的运行环境,比如调用一个函数,就会进入这个函数的执行上下文,确定该函数在执行期间用到的诸如 this、变量、对象以及函数等。

    在执行上下文中存在一个变量环境的对象(Viriable Environment),该对象中保存了变量提升的内容,比如上面代码中的 myName 和函数 showName,都保存在该对象中。

    该变量环境对象如下结构:

    ViriableEnvironment:    
    myName -> undefined,  
    showName -> function : { console.log(myName) }
    
    showName();
    console.log(myName);
    var myName = 'tokgogogo';
    function showName() {    
    console.log('函数 showName 被执行');
    }
    

    对于这段代码:

    • 第一行和第二行,由于这两行代码不是声明操作,所以 JavaScript 引擎不会做任何处理;

    • 第三行,由于这行是经过 var 声明的,因此 JavaScript 引擎将在环境对象中创建一个名为 myName 的属性,并使用 undefined 对其初始化;

    • 第四行,JavaScript 引擎发现了一个通过 function 定义的函数,所以它将函数定义存储到堆(HEAP)中,并在环境对象中创建一个 showName 的属性,然后将该属性值指向堆中函数的位置。

    这样就生成了变量环境对象。接下来 JavaScript 引擎会把声明以外的代码编译为字节码。

    执行阶段

    JavaScript 引擎开始执行“可执行代码”,按照顺序一行一行的执行:

    • 当执行到 showName 函数时,JavaScript 引擎变开始在变量环境对象中查找该函数,由于变量环境对象中存在该函数的引用,所以 JavaScript 引擎便开始执行该函数,并输入“函数 showName 被执行”结果。

    • 接下来打印“myName”信息,JavaScript 引擎继续在变量环境对象中查找该对象,由于变量环境存在 myName 变量,值为 undefined ,所以这时候就输出 undefined 。

    • 接下来执行第三行,把“tokgogogo”赋值给 myName 变量,赋值后变量环境中的 myName 属性值改变为“tokgogogo”,变量环境如下:

    ViriableEnvironment:   
    myName -> "tokgogogo",  
    showName -> function : { console.log(myName) }
    

    代码中出现相同的变量或者函数怎么办

    function showName() {
      console.log('showName 1');
    }
    showName();
    function showName() {
      console.log('showName 2');
    }
    showName();
    

    在上面的代码中,先定义了一个 showName 函数,打印 1 ;然后调用 showName ,并定义了一个 showName 函数,打印 2 ;最后接着调用 showName 。

    其完整执行流程:

    • 首先是编译阶段。遇到了第一个 showName 函数,会将该函数体存放到变量环境中。接下来是第二个 showName 函数,继续存放至变量环境中,但是变量环境中已经存在一个 showName 函数了。因此,第二个 showName 函数会将第一个 showName 函数覆盖掉。这样变量环境中就只存在第二个 showName 函数了。

    • 接下来是执行阶段。先执行第一个 showName 函数,但由于是从变量环境中查找 showName 函数,而变量环境中只保存了第二个 showName 函数,所以最终调用的是第二个函数,打印的内容是 2 。第二次执行 showName 函数也走同样的流程,所以输出的结果也是 2 。

    总结

    • JavaScript 代码执行过程中,需要先做变量提升,而之所以需要实现变量提升,是因为JavaScript 代码在执行之前需要先编译

    • 编译阶段,变量和函数会被存放到变量环境中,变量的默认值被设置为 undefined ;在代码执行阶段,JavaScript 引擎从变量环境中去查找自定义的变量和函数。

    • 如果在编译阶段,存在两个相同的函数,那么最终存放在变量环境中的是最后定义的那个,这是因为后定义的会覆盖掉之前定义的。

    JavaScript 的执行机制:先编译,再执行

    showName(); // 2
    var showName = function() {  console.log(1);}
    function showName() {  console.log(2);}
    showName(); // 1
    

    编译阶段:

    var showName = undefined;
    function showName() {  console.log(2);}
    

    执行阶段:

    showName();
    showName = function() {  console.log(1);}
    showName();
    

    相关文章

      网友评论

          本文标题:JS-ES6(1)

          本文链接:https://www.haomeiwen.com/subject/tphmgktx.html