变量声明提升
通过var定义(声明)的变量,在定义语句之前就可以访问到,而值为undefined
。
<script type="text/javascript">
console.log(a);// undefined
var a = 5;
</script>
因为实际上是这样处理的:
<script type="text/javascript">
var a;
console.log(a);// undefined
a = 5;
</script>
或者在局部变量提升的情况:
<script type="text/javascript">
var a = 5;
function fn() {
console.log(a);// undefined
var a = 10;
}
fn();
</script>
实际上是这样处理的:
<script type="text/javascript">
var a = 5;
function fn() {
var a;
console.log(a);// undefined
a = 10;
}
fn();
</script>
注意,如果局部变量和全局变量同名,那么局部变量的优先级更高。
这就是提升,把变量的声明提升到了作用域的最前端。
函数声明提升
通过function
声明的函数,在函数定义之前就可以直接调用,值就是函数对象。
<script type="text/javascript">
// 在函数定义之前就可以调用函数
hello();
function hello() {
console.log('hello world');
}
</script>
来看看这个情况:
<script type="text/javascript">
var a = true;
hello();
function hello() {
if (a) {
var a = 10;
}
console.log(a);// undefined
}
</script>
实际上是按照下面这样来进行处理的,所以最终的结果为undefined:
<script type="text/javascript">
function hello() {
var a;
if (a) {
a = 10;
}
console.log(a);// undefined
}
var a;
a = true;
hello();
</script>
因为JavaScript没有块级作用域,所以if语句中的var a=10
中的var a;
会提升到函数作用域的顶端,并且局部变量和全局变量同名,因为局部变量的优先级更高,所以a为undefined,在if判断中为false,所以打印结果为undefined。
但需要注意的是,函数声明(即function fn() {}
)会被提升,而函数表达式(即var fn = function () {}
)却不会提升,如下:
<script type="text/javascript">
fn();
var fn = function () {
console.log('hello world');
}
</script>
发现它报错了:Uncaught TypeError: fn is not a function
。因为就是变量提升,而这个变量还没有赋为函数:
<script type="text/javascript">
var fn;
fn();
fn = function () {
console.log('hello world');
}
</script>
注意:函数提升的优先级比变量提升优先级高。
<script type="text/javascript">
// 证明方式一
function a() {
}
var a;
console.log(typeof a) // 'function'
// 证明方式二
f1();// 1
function f1() {
console.log('1');
}
var f1 = function () {
console.log('2');
}
</script>
看看下面这个复杂的情况:
<script type="text/javascript">
var c = 1;
function c(c) {
console.log(c);
var c = 3;
}
c(2); // 报错 Uncaught TypeError: c is not a function
</script>
发现报错Uncaught TypeError: c is not a function
,如果我们在c(2);
之前打印c
的值(即console.log(c);
)发现结果为1。它等价于如下:
<script type="text/javascript">
function c(c) {
var c;
console.log(c);
c = 3;
}
var c;
c = 1;
c(2);
</script>
因为发生了变量声明提升和函数声明提升,它们同时出现在代码中,那么函数声明会优先提升,而又遇到一个需要提升的同名变量声明,但会被忽略,我们可以在下面的打印中看出结果:
<script type="text/javascript">
function c(c) {
var c;
console.log(c);
c = 3;
}
console.log(c);// function c(c)
var c;// 会被忽略
console.log(c);// function c(c)
c = 1;
console.log(c);// 1
c(2);// Uncaught TypeError: c is not a function
</script>
总结
- 在ES6之前,JavaScript没有块级作用域(即
{}
),只有全局作用域和函数作用域。 - 在函数作用域中,局部变量的优先级比全局变量的优先级高。
- 无论变量声明还是函数声明都会在执行之前处理,进行声明提升,提升到各自作用域的最前端。
- 函数声明会被提升,但是函数表达式也不会被提升。
- 如果同时存在函数声明提升和变量声明提升,那么函数声明提升的优先级更高。
- 如果存在多个函数声明,则出现在后面的函数声明会覆盖前面的。