关于变量提升

  我们在写js函数的时候,一般情况下,会避免变量重名,以及变量和函数重名的情况,所以很少会涉及到变量提升的概念。

  变量提升都是发生在js预编译过程中的,能够完全理解变量提升,有利于我们彻底弄懂js的概念。

        var a = 1;

        function test(a) {
            console.log(a)
            var a = 12;

            function a() {}
            console.log(a)
        }
        test(34)    

  在我们的面试过程中,如果存在笔试,那么少不了类似于这种题目。那么这么倒题目,其中两个console.log(a)究竟打印出来的是什么呢?看结果:

 

  那我们怎么理解这种提升,以及这些提升的规则又是什么呢?

  在预编译过程中,我们可以这么理解:

  系统首先创建一个对象,假设为[[AO]],然后查找形参和声明变量,作为该对象的属性,其值为undefined;然后将形参的值和实参的值统一,也就是把[[AO]]对象当中属性为形参的值赋值,如果传递了实参,那么这时候修改属性值为实际参数值;最后在函数体内查找函数声明,值为函数体。

  在上述例子中的第一个console.log(a)的位置,如果函数体内没有函数声明function a (){},那么这里打印出来的值为实际参数值34。

  当预编译结束之后,各种变量赋值也好,函数赋值也好,都是重新修改该对象内属性的值,运行到哪一步,就在哪一步修改。

  确认一点,变量提升,只适用于var定义的变量。

  如果使用let或者const定义变量,那么就不存在上述各种规则。将上述代码稍作修改:

        var a = 1;
        function test(a) {
            console.log(a)
            let a = 12;
            function a() {}
            console.log(a)
        }
        test(34)    

  唯一变动,就是在函数体内部声明变量时,使用let声明替换了之前的var声明,如果此时再次运行的话,就会发现:报错了!

  

  提示为请确认a是否已经声明。这是因为函数体内使用了let声明变量,但是在声明之前就开始调用了该变量,导致报错。

  在阮一峰老师的书《ECMAScript 6 入门》中说到:

只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。

  这就是所谓的暂时性死区。在暂时性死区内,不仅无法提前使用let声明的变量,他也不会获取函数体外部作用域内的同名变量。这就是我们例子中所展示的即使外部和形参名称均为a,我们在函数体内部也不能先使用a变量。暂时性死区会到该变量被声明后结束。

  如果希望了解更多,请参考阮一峰老师所述的“暂时性死区”。

  那就可以总结一下:

  1、只有var定义的变量存在变量提升这一说法,而且必然会发生变量提升。

  2、变量提升和函数提升的过程:

    1)先找var定义的变量和形参: a--> undefined

    2)实参和形参统一

    3)函数声明提升(仅仅函数声明,函数表达式是不存在提升一说的)

  3、如果块级作用域内出现let或者const定义的变量,而且在该变量定义之前使用了,那么必然报错。

    

  

原文地址:https://www.cnblogs.com/zhuhuoxingguang/p/10370037.html