• JavaScript变量提升


    什么是变量提升

    当栈内存的作用域形成时,js代码执行前浏览器将带有var关键字的变量提前声明(也就是在变量所属的作用域的顶部声明,虽然声明了,但是没有定义,在赋值前——也就是写着var XXX的地方之前,值为undefined),将带有function关键字的变量提前进行声明和定义(就是说带function的只要在这个作用域内写了,就可以用,哪怕调用这个函数的地方在他前面)。当js代码执行的时候,遇到创建函数的代码会直接跳过。

    例如:

    1. console.log(a,b); //undefined undefined
    2. console.log(sum(1,2)); //3
    3. var a = 1
    4. var b = a
    5. console.log(a,b); //1 1
    6. b = 2
    7. console.log(a,b); //1 2
    8. function sum(x, y) {
    9. var total = x + y
    10. return total
    11. }
    12. console.log(sum(1,2)); //3
    13. console.log(add(1,2)); //add is not a function
    14. var add = function(x,y) {
    15. var total = x + y
    16. return total
    17. }

    执行上下文

    执行上下文的生命周期有三个阶段:创建阶段、执行阶段、销毁阶段。与变量提升关系最密切的是创建阶段。

    创建执行上下文

    在执行全局代码前,会创建一个全局执行上下文,并对全局数据进行预处理。

    在这一阶段会进行变量和函数的初始化声明,比如var与function的变量提升并添加为window属性,this赋值windows。

    同样,在调用函数前,也会创建一个函数执行上下文对象。比如var定义的局部变量,function声明的函数等等。

    分析

    1. 找到所有的非函数中的var声明
    2. 找到所有的顶级函数声明(不在大括号内的函数声明)
    3. 找到顶级let,const,class声明
    4. 找到块中的声明,函数名不与上述重复

    判断是否有重复

    只要是使用了let,const,class声明的,命名都得是独一无二的,但是var和function声明的变量和函数,名字是可以重复的。倘若var与function声明的名字相同,function优先。

    var的作用

    带var是在这个函数作用域内声明了一个变量,不带var则会向上级作用域查找,相当于使用了上级作用域的变量,如果找到window还没找到,就相当于给window设置了一个变量。

    1. var a = 10;
    2. function show() {
    3. console.log(a); //undefined
    4. var a = 2;
    5. console.log(a); //2
    6. }
    7. show();
    8. console.log(a); //10

    在函数show()里,由于变量提升,因此在函数最开始就进行了var a 这一操作,所以函数内的第一个输出不会向上级作用域查找,输出为undefined。第二个输出在输出前,已经对a进行了赋值,因此输出结果为2.当函数结束后,show()函数的作用域被释放,因此第三个输出结果为10.

    1. fun();
    2. console.log(a);
    3. console.log(b);
    4. console.log(c);
    5. function fun(){
    6. var a = b = c = 1;
    7. console.log(a);
    8. console.log(b);
    9. console.log(c);
    10. }
    11. //1 1 1 a is not defined

     函数fun()内,赋值的时候相当于

    var a = 1;

    b = 1;

    c = 1;

    因此当函数执行完之后,在全局作用域内找不到a,但是可以找到b和c

    1. fun();
    2. console.log(b);
    3. console.log(c);
    4. function fun(){
    5. var a = b = c = 1;
    6. console.log(a);
    7. console.log(b);
    8. console.log(c);
    9. }
    10. //1 1 1 1 1

    条件语句中的变量提升

    在if...else...条件语句中,不论条件是否成立,都会进行变量提升。

    1. console.log(a)
    2. if(false){
    3. var a = 1
    4. }
    5. console.log(a)
    6. /* 输出
    7. undefined
    8. undefined
    9. /

    虽然条件不成立,但是a进行了变量提升,但又因为条件不成立,因此没有给a赋值,所以在条件语句外输出a为undefined。

    需要注意的是,在条件语句中,function定义的虽然会进行变量提升,但是此时只进行了声明,只有当条件成立之后,才会进行定义。

    重名时的变量提升

    需要注意,js在执行上下文中会检测命名重复,let,const,class声明的名字之间不能重复,let,const,class和var,function的名字不能重复,但是var和function命名可以重复,当var和function命名重复的时候,情况如下:

    如果一个var声明的变量和一个function声明的函数命名相同,var声明的变量会首先进行变量提升,但是之后会被function声明的函数所覆盖。但是在执行代码时,js顺序执行,执行了给a赋值12的操作,因此打印出来a是12,在接下来的a(),此时a不是一个函数,因此报错。

    1. console.log(typeof(a)); //function
    2. var a = 12
    3. function a() {
    4. console.log(111)
    5. }
    6. console.log(a) //12
    7. a() //a is not a function

    暂时性死区

    当我们使用let来定义变量的时候,并非没有提升,但是此提升非彼提升,它和var的变量提升并不相同,js变量有创建、初始化、赋值三个步骤。var的变量提升,是在本作用域的开端,创建了变量,并对其进行初始化,初始化为undefined。当我们用let来定义变量的时候,同样会在本作用域的开端创建变量,但是并没有进行初始化,用let定义的变量初始化这一步骤是在当js执行代码执行到写有let a = 0(例子)的时候进行的,let a = 0就是将a初始化为0。因为这样,let定义变量的时候就产生了暂时性死区的情况。

    1. let a = 0;
    2. function arr(){
    3. console.log(a); //0
    4. }
    5. arr();
    1. let a = 0;
    2. function arr(){
    3. console.log(a); //报错
    4. let a = 1;
    5. console.log(a);
    6. }
    7. arr();

  • 相关阅读:
    [激光器原理与应用-5]:激光二极管LD (Laser Diode)与激光二极管驱动器(LD驱动器)
    springboot + solr
    电子学会C/C++编程等级考试2022年06月(一级)真题解析
    【云原生】设备云之Fcloud云组态第三方服务
    Linux /proc/iomem与/proc/ioports
    ContentResolver.query流程分析
    【论文阅读】Bag of Tricks for Efficient Text Classification高效率文本分类技巧包
    一文详解JVM的内存结构
    无需更换vue-cli 脚手架 uniapp-搭建项目-H5-低版本安卓IOS兼容问题(白屏)(接口请求异常)
    springboot解决@Autowired注入service时出现循环依赖问题
  • 原文地址:https://blog.csdn.net/mhc20201554114/article/details/126878684