先说说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

 

posted @ 2020-02-21 20:58  想学JS的前端  阅读(261)  评论(0编辑  收藏  举报