• 变量提升的常见情况总结


    什么叫变量提升?

    把当前上下文中带有var(提升声明)/function(提升声明+定义)进行提升的声明或者定义。变量提升是将变量声明提升到它所在作用域的最开始的部分。

    • 全局上下文中:基于var/function声明的变量,也相当于给window设置了对应的属性。

    实例 1

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

    这是因为函数里的变量t声明提升到函数最开始的部分了。上面的代码1相当于代码2:

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

    在函数内部,如果没有用var进行申明,则创建的变量是全局变量,而不是局部变量了。所以,建议变量声明加上var关键字。

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

    function声明会比var声明优先级更高一点。

    1. //代码4
    2. console.log(foo);
    3. var foo=10;
    4. console.log(foo);
    5. function foo(){
    6. console.log(10);
    7. }
    8. console.log(foo);

    相当于下面的代码:

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

    运行结果如下:

    1. //代码5
    2. function foo(){
    3. console.log(a);//2
    4. }
    5. function bar(){
    6. var a=3;
    7. foo();
    8. }
    9. var a=2;
    10. bar();

    ​ 上面的代码的运行过程是 bar()-->foo(),此时的a由于在调用 bar()之前已经初始化了,所以相当于给在 foo() 函数的所在作用域中的this 对象添加了a属性,并赋值为2,所以调用 foo() 函数的a时,实际上调用的 foo() 所在作用域的 this 的a属性。

    实例 2

    1. function Foo(){
    2. //未定义,所以是属于this的一个属性,这里的this是window
    3. getName = function () {
    4. console.log(1);
    5. }
    6. return this;
    7. }
    8. Foo.getName = function() {
    9. console.log(2);
    10. };
    11. Foo.prototype.getName = function(){
    12. console.log(3);
    13. };
    14. //变量声明提升,将getName变量提升(只提升定义)
    15. var getName = function(){
    16. console.log(4);
    17. };
    18. //变量声明提升到作用域的顶部 (声明+定义一起提升)
    19. function getName(){
    20. console.log(5)
    21. };
    22. Foo.getName(); // 2,直接调用
    23. getName(); // 4,变量声明提升
    24. Foo().getName(); // 1,将getName()方法添加到window对象上
    25. getName(); // 1
    26. new Foo.getName(); // 2,直接调用
    27. new Foo().getName(); // 3 相当于 var f = new Foo() f.getName()
    28. new new Foo().getName(); // 3

    实例 3

    1. var a = 0
    2. function b(){
    3. console.log(a) // fun a 函数声明提升到作用域的顶部
    4. a = 10 //a是局部变量,赋值为10
    5. console.log(a) // 10
    6. return;
    7. function a(){}
    8. }
    9. b()
    10. console.log(a) // 0,此时的a还是外部作用域的a

    实例 4

    函数执行,函数的作用域[scope]跟它在哪执行无关,只跟它在哪定义有关。

    1. var i = 0;
    2. function A () {
    3. /**
    4. * EC(A)
    5. * 作用域链:
    6. */
    7. var i = 10;
    8. function x () {
    9. /**
    10. * EC(X)
    11. * 作用域链:<EC(x),EC(A)>
    12. */
    13. console.log(i);
    14. }
    15. return x;
    16. }
    17. var y = A();
    18. y();//10
    19. function B () {
    20. var i = 20;
    21. /**
    22. *函数的作用域跟它在哪执行无关,只跟它在哪定义有关。
    23. *y的作用域[[scope]]是 EC(A)
    24. */
    25. y();//10
    26. }
    27. B();

    实例 5

    1. var a = 1;
    2. /**
    3. * EC(G)
    4. * a
    5. * fn=0x000000 [[scope]]:EC(G)
    6. */
    7. function fn (a) {
    8. /**
    9. * 私有上下文 EC(FN)
    10. * 私有变量赋值
    11. * a=1
    12. * =0x000001 [[scope]]:EC(FN)
    13. * =2
    14. * 作用域链:
    15. * 形参赋值:a=1
    16. * 变量提升:
    17. * var a;
    18. * function a(){};不需要重新声明,但是需要重新赋值
    19. */
    20. console.log(a); //[Function:a]
    21. var a = 2; //在这里知识重新赋值
    22. function a () { } //在变量提升阶段都处理完了
    23. console.log(a);//2
    24. }
    25. fn(a);
    26. console.log(a);//1

    例题 6

    获取一个变量的值,首先看是否是自己的私有变量,不是话则会根据作用域链向上级上下文查找...一直到全局上下文(window)。找到则返回,没找到则报错:x is not undefined,并且他下面的代码也不会执行了。

    1. //首先看是否是全局变量,不是,则再看是否为window的一个属性,如果还不是,报错:ReferenceError: a is not defined(这行代码一旦报错,下面的代码都不会处理了)
    2. console.log(a);//ReferenceError: a is not defined
    3. a = 12;
    4. function fn () {
    5. console.log(a);
    6. a = 13;
    7. }
    8. fn();
    9. console.log(a);//13

    实例 7

    1. var foo = "james";
    2. (function (foo) {
    3. /**
    4. * EC(ANY)
    5. * foo:jmaes
    6. * 作用域链:
    7. * 形参赋值:foo="james"
    8. * 变量提升: var foo
    9. */
    10. console.log(foo);//james
    11. var foo = foo || 'world';
    12. console.log(foo) //jamaes
    13. })(foo)
    14. console.log(foo);//james

    实例 8

    在新版本浏览器中 function(){} + 函数如果没有出现在 {}中,则变量提升阶段是"声明+定义"(老版本浏览器不论是否出现在{}中都是"声明+定义") + 函数如果出现在{}中(除函数、对象的大括号外)则只声明。

    形式1

    1. {
    2. //把这一行代码之前对于foo的操作都映射给去全局一份
    3. //之后的操作都认为是私有的
    4. function foo () { }
    5. foo = 1
    6. }
    7. console.log(foo) //[Function :foo]

    形式2

    1. /**
    2. * EC(G)
    3. * foo
    4. * 变量提升:function foo
    5. */
    6. {
    7. /**
    8. * EC(BLOCK)
    9. * foo=ex000000
    10. * =ex000001
    11. * 变量提升
    12. * function foo(n){}
    13. * function foo(m){}
    14. */
    15. //把之前对foo的操作"映射"给全局
    16. function foo () { }
    17. foo = 1
    18. //把之前对foo的操作"映射"给全局
    19. function foo () { }
    20. console.log(foo); //1
    21. }
    22. console.log(foo) //1

    形式3

    1. /**
    2. * EC(G)
    3. * foo
    4. * 变量提升:function foo只声明不定义
    5. */
    6. console.log(foo);//undefined
    7. {
    8. /**
    9. * EC(BLOCK)
    10. * foo=ex000000
    11. * =ex000001
    12. * 变量提升
    13. * function foo(n){}
    14. * function foo(m){}
    15. */
    16. //把之前对foo的操作"映射"给全局
    17. function foo () { }
    18. foo = 1
    19. //把之前对foo的操作"映射"给全局
    20. function foo () { }
    21. foo=2;//私有的操作,跟全局无关
    22. console.log(foo); //2
    23. }
    24. console.log(foo) //1

    实例 9

    形式1

    1. var x = 1;
    2. function func (x, y = function any () { x = 2 }) {
    3. //执行func(5)
    4. /**
    5. * AO(FUNC)
    6. * x=5
    7. * y=oxooooo1
    8. * 作用域链:
    9. * 形参赋值
    10. * x=5;
    11. * y=function any(){...}
    12. * 变量提升:——
    13. * 代码执行
    14. * x=3;
    15. * y();
    16. * conosle.log(x)
    17. */
    18. //执行y()
    19. /**
    20. * AO(Y)
    21. * 作用域链:
    22. * 形参赋值:——
    23. * 变量提升:——
    24. * 代码执行:
    25. * x=2
    26. */
    27. x = 3;
    28. y();
    29. console.log(x);//2
    30. }
    31. func(5);
    32. console.log(x);//1

    注意: 在函数执行时。 + 条件1:有形参赋值默认值(不论是否传递实参,也不论默认值的类型) + 条件2:函数体中有变量声明( + 必须是基于let/const/var,注意let/const不允许重复声明,不能和形参变量名一致)。 + 函数体中用function声明的变量必须和形参中的某一个变量名字一致,才会有下述的机制。 这两个条件存在的情况下,除了默认形成的函数私有上下文,还会多创建一个块级私有上下文(函数体到括号包起来的)。

    块级私有上下文 + 在其中声明的变量是块级上下文中私有的,和函数私有上下文没啥关系了。 + 它的上级上下文是函数私有上下文。 + 并且会把函数私有上下文"形参赋值"结束后的结果,映射给私有块级上下文中的同名字段。

    形式2

    1. var x = 1;
    2. //条件1 形参赋值
    3. function func (x, y = function any () { x = 2 }) {
    4. //条件 2声明变量
    5. var x = 3;
    6. y();//y()是在函数私有上下文中执行的
    7. //x是私有块级上下文中的x,不是函数私有上下文中的x
    8. console.log(x);//3
    9. }
    10. func(5);
    11. console.log(x);//1

    形式3

    1. var x = 1;
    2. function func (x, y = function anouy () { x = 2 }) {
    3. var x = 3;
    4. var y = function any () { x = 4 }
    5. y();
    6. console.log(x)//4
    7. }
    8. func(5);
    9. console.log(x);//1

    实例 10

    1. var a = 1;
    2. function fn (a) {
    3. /**
    4. * EC(FN)
    5. * 作用域链:
    6. * 形参赋值:a=1
    7. * 变量提升:(会进行赋值覆盖)
    8. * var a;这一步浏览器回忽略,因为a私有变量已经存在AO(FN)中了
    9. * a=0x001;[[scope]]:EC(FN),不会重复声明,但是会重新赋值
    10. * 代码执行:
    11. */
    12. console.log(a)// Function a
    13. var a = 2;//赋值
    14. console.log(a);//2
    15. function a () { };//代码执行到这里,不会重新赋值,在变量提升阶段已经赋值了
    16. console.log(a);//2
    17. }
    18. fn(a);
  • 相关阅读:
    scp传输数据文件
    RabbitMQ-03(实战 、RabbitMQ 的六种消息模式)
    35+大厂总监失业4个月,面试HR说:同等职级,我们要相对年轻的。
    循环执行某段代码,待某种条件满足后停止循环 java原始Timer实现
    深度学习推荐系统(六)DeepFM模型及其在Criteo数据集上的应用
    六、python的csv模块
    第三章:栈/队列 重点题
    Kotlin学习(一)
    深度神经网络——贝叶斯与朴素贝叶斯定理
    [论文阅读笔记18] DiffusionDet论文笔记与代码解读
  • 原文地址:https://blog.csdn.net/weixin_36617251/article/details/133753623