JS是顺序执行的吗
要讨论变量提升,不要着急,我们先从下面的代码讲起。 请思考以下代码会,结果是什么?
showName()
console.log(myname)
var myname = '念念不忘'
function showName() {
console.log('函数showName被执行');
}
使用过 JavaScript 开发的程序员应该都知道,JavaScript 是按顺序执行的。若按照这个逻辑来理解的话,那么结果应该是:
- 第一行代码由于showName函数未定义,出现报错信息;
- 第二行代码由于myname变量未定义,同样出现报错信息。
但是实际上打印信息如下
!
第 1 行输出“函数 showName 被执行”,第 2 行输出“undefined”,这和前面想象中的顺序执行有点不一样啊!
通过上面的执行结果,我们知道函数或者变量可以在定义之前使用,从上面两段代码的执行结果来看,我们可以得出如下结论:
- 在一个变量定义之前使用它,不会出错,但是该变量的值会为 undefined,而不是定义时的值;
- 在一个函数定义之前使用它,不会出错,且函数能正确执行。 由此引出疑问,变量和函数为什么能在其定义之前使用?这似乎表明 JavaScript 代码并不是一行一行执行的。同样的方式,变量和函数的处理结果为什么不一样?比如上面的执行结果,提前使用的 showName 函数能打印出来完整结果,但是提前使用的 myname 变量值却是 undefined,而不是定义时使用的“念念不忘”这个值。 要解释这两个问题,就需要先了解下什么是变量提升
变量提升
不过在介绍变量提升之前,我们先通过下面这段代码,来看看什么是 JavaScript 中的声明和赋值
var myname = '念念不忘';
其实上面这段代码可以等同于下面这段代码
var myname;
myname = '念念不忘';
上面是变量的声明和赋值,那接下来我们再来看看函数的声明和赋值,结合下面这段代码
// 函数式声明式
function test(){
console.log('test')
}
// 函数字面量式
var bar = function(){
console.log('bar')
}
第一个函数 test 是一个完整的函数声明,也就是说没有涉及到赋值操作,函数声明式提升是整个代码块提升到它所在的作用域的最开始执行。
第二个函数是函数字面量式的声明,先声明变量 bar,再把function(){console.log('bar')}赋值给 bar,和变量提升的结果是一样的,函数只是一个具体的值。
理解了声明和赋值操作,那接下来我们就可以聊聊什么是变量提升了。
所谓的变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined
下面我们来模拟下实现:
/*
* 变量提升部分
*/
// 把变量 myname提升到开头,
// 同时给myname赋值为undefined
var myname = undefined
// 把函数showName提升到开头
function showName() {
console.log('showName被调用');
}
/*
* 可执行代码部分
*/
showName()
console.log(myname)
// 去掉var声明部分,保留赋值语句
myname = '念念不忘'
从代码中可以看出,我们对原来的代码主要做了两处调整:
- 第一处是把声明的部分都提升到了代码开头,如变量 myname 和函数 showName,并给变量设置默认值 undefined;
- 第二处是移除原本声明的变量和函数,如var myname = '极客时间'的语句,移除了 var 声明,整个移除 showName 的函数声明。
通过这两步,就可以实现变量提升的效果。你也可以执行这段模拟变量提升的代码,其输出结果和第一段代码应该是完全一样的。 通过这段模拟变量提升代码,相信你已经明白了第一段代码中可以在变量或者函数定义之前使用而不报错的原因—函数和变量在执行之前都提升到了代码开头。