• 先说说var和函数声明


    变量提升

    变量提升应该是var最引人瞩目的一个特点了。不说废话,先上代码。 

    1 //code1:
    2 a = 10
    3 var a
    4 console.log(a)//10
    5 
    6 //code2:
    7 var a
    8 a = 10
    9 console.log(a)//10

     

    聪明的你肯定都猜对了输出结果。其实代码1和代码2是等价的。为什么呢?关键就是变量提升(hoisting)。对于用var声明的变量,其声明都会被提升到作用域顶部。

    来分析一下代码1:

    1. 浏览器预编译阶段,浏览器找到声明语句var a,创建变量a,初始化undefined。
    2. 浏览器执行阶段,a = 10给a赋值10,然后打印。

    所以,变量提升其实就是声明语句被提升到作用域的顶部执行的一种现象。

    ps:但如果有深入了解js运行机制的人肯定对这种说法是不解的。其实,所谓变量提升不过是从表面上简化地解释了js的运行机制。实质上,在预编译阶段阶段,浏览器会首先找到所有的变量和函数声明,把变量初始化值为undefined(此处暂不考虑let和const声明),函数声明整体提升(下面会讲到)。然后浏览器再开始执行代码,比如赋值,运算等等。所以,看起来就像变量的声明被提升到作用域顶部执行了一样。不过所谓变量提升也并没有标准的定义,我所说的定义只是当下一种普遍的理解。不过对初学者来说,用变量提升来理解var确实可能是一中比较好的做法,上来就提运行机制什么的,那会吓跑很多JS的潜在爱好者吧。后面有机会的话也还会讲讲JS的运行机制哦。

    特别注意,函数作用域中用var声明的变量在全局作用域无法访问到。例如:

    console.log(a)
    
    
    function c (){
       var a
       console.log(b)    
       function d() {
           var b
        }
    }  

    这里代码会报错,因为a只在函数c作用域声明,b只在函数d作用域有声明。当使用的变量声明不在当前作用域中,引擎会到上一级作用域去寻找,如果找不到,就会报错,并不会去下一级别作用域中寻找。

    那不用var声明会怎么样?

    先看代码:

    //code:1
    console.log(a) // Uncaught ReferenceError: a is not defined
    a = 3
    
    
    //code:2
    a = 3
    console.log(a)//3

    代码1中编译器会报错,而2则会正常打印3。在1中a并没有声明,所以不会有变量提升,所以打印a时显示未定义。2中编译器预编译时没找到任何变量声明,执行时遇到未声明的变量a,把其初始化为全局变量,再进行赋值,表面上看起来与var a = 3并无什么区别,但本质上还是不一样的。

    不如再来聊聊函数声明和var的联系

    先上代码

    alert(a); // 弹出function a(){alert(2)} function a(){ alert(2); }
    
    function a(){
     alert(2);
    }

    和var声明的变量一样,函数声明也存在提升现象。也许你发现了不同:函数声明是整体提升的(alert中弹出的并不是undefined,而是整个函数整体),而var声明的变量只是声明提升初始化为undefined。

    接下来再看一段代码:

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

    屏幕前的你是否感到有点懵逼?what?两者都有变量提升,那foo最后是函数还是变量?

    答案是,还是函数,并且这段代码会打印出foo。为什么呢?如果现阶段你还不想深究,那你可以暂时认为,函数声明提升优于变量提升。

    那如果在var foo = 2后打印foo,是函数还是2呢?答案是2。

    事实上,在浏览器内部对代码是这么处理的:

    1. 预编译阶段,找到函数声明和变量声明,为其开辟内存空间。
    2. 再从头开始执行代码,比如函数调用和赋值等等操作。

    在这段代码里面,浏览器在预编译阶段找到了函数foo的声明,那么会为foo开辟内存空间,然后对函数内部的声明等等还会有一些操作。然后找到了变量foo的声明,但由于foo已经存在内存空间中,所以不再为其开辟内存空间和初始化。

    然后开始执行代码,先调用函数,打印出了foo。接下来为foo赋值2,这时候foo由function变成了number。这时候如果再打印foo,就是2了。

    很多人会问,如果代码是下面这样,结果会怎么样:

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

    可能我会告诉你,这段代码和上面那段代码是等价的。并且如果你在最下面再添一行代码,打印foo,结果仍是2。为什么呢?因为函数声明是整体提升的,即便他的声明在变量后,他的声明依然会覆盖上面变量的声明,然而反过来变量声明是无法覆盖函数声明的。如果对于初学者,从表面上看,那最好的理解就是认为函数声明比变量声明优先级高。注意,我说的只是声明哦。

    注意

    下面这种情况属于函数表达式,函数不会提升

    1 a() //报错: a is not a function
    2 
    3 var a = function(){
    4     console.log('123')
    5 }
    1 console.log(a) //undefined
    2 
    3 var a = function(){
    4     console.log('123')
    5 }
    var lock = true
    a()  //报错:a is not a function
    
    if (lock) {
        function a(){
            console.log('1')
        }
    }else{
        function a(){
            console.log('1')
        }
    }

    开小灶啦

    对我上面讲的可能很多观众还是无法理解的,好学的朋友建议再去搜索一些JS运行机制相关文章进行学习。

    上面说到函数声明提升时浏览器内部还会对函数进行一些操作,具体可以参考https://zhuanlan.zhihu.com/p/36373165

  • 相关阅读:
    【Unity 3D】学习笔记29:游戏的例子——简单的小制作地图
    mysql位_01检查错误代码的方法
    编程获取linuxservercpu、内存和磁盘使用
    poj 2482 Stars in Your Window(扫描线)
    Largest Rectangle in Histogram
    【剑指offer】删除字符也出现在一个字符串
    linux sdio card睡眠治疗 sdio card removed解决方案
    Wooyun
    APT攻击
    Elasticsearch 学习~
  • 原文地址:https://www.cnblogs.com/AwenJS/p/12337542.html
Copyright © 2020-2023  润新知