javascript 变量提升 函数有吗 怎么用?
1.JS代码执行顺序
我们直觉上会认为JS的代码在执行时是由上到下一行一行执行的,但实际并不完全正确,下面的例子会证明:
a = 'haha'
var a
console.log(a)
上面的代码会输出什么呢?
如果按照我们认为的由上到下一行一行执行,那么应该输出undefined
,但是实际结果是'haha'
。
接着再看一个代码:
console.log(a)
var a = 'haha'
那这个输出的是什么?
鉴于上面代码表现出来的非自上而下的特点,有可能认为是’haha’。或者有认为变量a没有声明,所以会报错,但实际结果是undefined
为什么会是这样呢?到底发生什么?让我们带着这个问题看下去
2.变量提升
2.1 编译
我们要知道,引擎在解释JS代码之前首先要对代码进行编译,在编译阶段中有一部分工作就是找到所有的声明,并用合适的作用域将他们关联起来。
JS执行的流程图大致如下:
所以总结来说,包括变量和函数在内的所有声明在编译阶段都会首先被处理,然后才是代码被执行的阶段。
当我们看到var a = 'haha'
时候认为它是一个声明,但其实在JS中它会被看做两个声明,var a
和a = 'haha'
。var a
是定义声明,是在编译阶段进行;a = 'haha'
是赋值声明,会留在原地等待执行阶段。
2.2 解释上面问题
接着我们再看回第一个例子:
a = 'haha'
var a
console.log(a)
通过上面说明,可以知道会先处理定义声明,所以整个代码会以如下形式进行处理:
var a //在编译阶段进行变量提升
a = 'haha'
console.log(a)
第二个例子也是相同处理
console.log(a)
var a = 'haha'
在这个例子中也会进行变量提升,所以整个代码会以如下形式进行处理:
var a
console.log(a)
a = 'haha'
3.函数提升
3.1 基础用法
看完变量提升,再看一下函数声明如何进行提升
foo()
function foo() {
console.log('haha')
}
看完上面的例子,我想大家也能猜到了结果,就是’haha’。
因为foo函数的声明被提升了,所以第一行中的调用可以正常执行。
3.2 作用域提升
接下来再看一个例子
foo()
function foo() {
console.log(a)
var a = 'haha'
}
这个会输出什么呢?
答案是:undefined
在这个例子中,不只有函数提升,还有变量提升。要注意的是,每个作用域都会进行提升操作,所以foo()函数自身也会在内部对var a进行提升,但是只能在这个作用域中进行提升,并不能提升到整个代码的最上方。
因此这段代码会以如下形式进行处理:
function foo() {
var a
console.log(a)
a = 'haha'
}
foo()
3.3 函数表达式
foo()
var foo = function bar() {
console.log('haha')
}
这个代码执行结果是:TypeError: foo is not a function
这是因为变量标识foo被提升并分配给所在作用域,所以不会出现ReferenceError的错误,但是foo此时没有赋值,也就是此时foo是undefined,所以执行foo()是对undefined值进行函数调用,因此结果为TypeError。
这个例子证明了:函数声明会被提升,但是函数表达式不会被提升
3.4 具名函数表达式
foo()
bar()
var foo = function bar() {
console.log('haha')
}
从上面我们知道,执行foo()会是typeError,那执行bara()呢?
答案是:ReferenceError: bar is not defined
这是因为:即使是具名的函数表达式,名称标识符在赋值之前也无法在所在作用域中使用。
因此这段代码会以如下形式进行处理:
var foo
foo() //typeError
bar() //ReferenceError
foo = function bar() {
console.log('haha')
}
4. 函数优先
函数声明和变量声明都会提升,但是在处理声明中是 函数首先被提升,然后才是变量。
foo()
var foo
function foo() {
console.log(1)
}
foo = function () {
console.log(2)
}
这个例子的结果是1。
这个代码会以近似如下形式进行处理:
function foo() {
console.log(1)
}
foo()
foo = function () {
console.log(2)
}
注意:var foo
虽然会出现在foo()之前,但是因为重复的声明,所以被忽略了。
5.实战练习
经过上面说明,应该对变量提升有了大致了解,那么让我们再看几个例子,看看是否真正理解
5.1 例子1:
var getName = function () {
console.log('4');
};
function getName() {
console.log(5);
}
getName();
想想这个结果是什么?
让我们分析一下这个提升过程,这个代码会以近似如下形式进行处理:
function getName() {
console.log(5);
}
var getName //被忽略
getName = function () {
console.log('4');
};
getName();
getName
函数和变量getName
被提升- 然后
var getName
因为重复声明被忽略 - 最后函数表达式
getName
会覆盖函数getName
所以最后实际执行的是函数表达式getName = function () { console.log('4'); };
,结果为’4’
5.2 例子2
function showName() {
console.log('name1');
}
showName();
function showName() {
console.log('name2');
}
showName();
想想这个结果是什么?
有了上面经验,这个就更好理解了,这个代码会以近似如下形式进行处理:
function showName() {
console.log('name1');
}
function showName() {
console.log('name2');
}
showName();
showName();
所以结果一目了然,结果为 name2 name2
6.总结
通过上面的内容,我们可以进行一个简单的总结:
- 变量提升,是指在 JavaScript 代码执行过程中,JavaScript 引擎把变量的声明部分和函数的声明部分提升到代码开头的“行为”。变量被提升后,会给变量设置默认值,这个默认值就是我们熟悉的 undefined。
- JS的代码执行顺序是先进行声明处理,然后进行赋值等其他操作。
- 提升的过程就像是把变量和函数声明从他们在代码中出现的位置“移动”到了最上面。另外只有定义的声明本身会被提升,而赋值或其他运行逻辑会留在原地
- 函数声明和变量声明在一起时,函数首先被提升,然后才是变量。