zoukankan      html  css  js  c++  java
  • 先说说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

  • 相关阅读:
    Samba 4.0 RC3 发布
    SymmetricDS 3.1.7 发布,数据同步和复制
    Express.js 3.0 发布,Node.js 的高性能封装
    GIFLIB 5.0.1 发布,C语言的GIF处理库
    jQuery UI 1.9.1 发布
    SVN Access Manager 0.5.5.14 发布 SVN 管理工具
    DynamicReports 3.0.3 发布 Java 报表工具
    HttpComponents HttpClient 4.2.2 GA 发布
    AppCan 2.0 正式发布,推移动应用云服务
    Ruby 2.0 的新功能已经冻结
  • 原文地址:https://www.cnblogs.com/AwenJS/p/12337542.html
Copyright © 2011-2022 走看看