JS拓展:Let和变量提升(函数预编译)

Let(定义变量)

在es5中我们声明变量一般是使用var,然而var具有变量提升的功能,有可能会给我们带来出乎意料的异常

变量提升是什么

  • 用var声明一个变量,他会被提升到函数或全局作用域的顶部。这样在变量声明之前就可以获取到此变量
  • 如果变量声明时给了初始值,那么值不会被提升,提升到最顶部的变量是一个未初始化值的变量
    JS拓展:Let和变量提升(函数预编译)

变量提升带来的问题

var ar=[];
for (var i = 0; i < 10; i++) {
ar.push(function(){console.info(i);})        
}    
//注意for循环中是没有块级作用域的

打印出来的i全部都是10,因为在打印时才去获取的i值,而这里的i由于被提升到全局作用域了,所以每次迭代的都是同一个值

解决变量提升导致的问题(let的由来)

let关键字具有块级作用域。相当于var的升级版

  • let不会进行变量提升,它只有被声明了只会才能使用。称之为TDZ(暂时性死区)
  • Let具有块级作用域,只在它当前所在或括号范围内有效
  • 不允许在重复定义let已经定义过的变量
    JS拓展:Let和变量提升(函数预编译)

函数提升及其解决方法

  • 不仅变量可以提升,函数也可以提升,并且提升时函数的函数体也会提升
  • 如果声明了多个同名函数,那么只会提升最后一个函数函数
    JS拓展:Let和变量提升(函数预编译)
    注意:通过字面量形式声明的函数,会被当做变量处理,提升时不会提升值。

函数内部的变量提升的过程和优先级

基本规则:函数优先提升,同时提升函数体
对于同名的变量和函数,只会提升函数而不会提升变量

如果函数含有参数的话,会有点区别

  1. 函数最优先级最高
    JS拓展:Let和变量提升(函数预编译)
  2. 形参第二优先
    JS拓展:Let和变量提升(函数预编译)
  3. 最后才是普通变量
    JS拓展:Let和变量提升(函数预编译)

函数内部的变量提升执行过程

这个提升过程也称之为函数预编译过程,共分为五步:

生成环境>>查找形参>>赋值实参>>查找变量>>查找函数

  1. 数在运行的瞬间,生成一个执行期环境上下文 (Active Object),简称AO;
  2. 查找是否具有形参,找到后添加到AO的属性,值为undefine,例如AO.age=undefined;
  3. 接收实参,添加到AO的属性,覆盖之前的undefined;
  4. 查找函数内是否具有变量声明,如var age;或varage=23;,如果找到了且AO上还没有这个属性,就把他添加AO上,值为undefined。如果找到了但AO上已经有了这个属性,则不作任何修改;
  5. 查找函数内是否具有其他函数的声明,如:function age(){};如果找到了就把函数赋给AO.age,而不管AO上是否已经存在此属性。如果找到了多个同名函数声明,那么只会以最后一个函数声明为准