JS进阶-作用域-声明提升
声明提升(hoisting)分为变量声明提升和函数声明提升。声明从它们在代码中出现的位置被“移动”到最上面的过程,被称作声明提升。每个作用域都会有声明提升。
在介绍作用域的内部原理时有提过,引擎在解释JS代码前首先进行编译,编译过程中会找到所有的声明,并用合适的作用域将它们关联起来。
以var a = 1;
说明,这行代码包含了两步操作:var a
和a = 1
。定义声明是在编译阶段进行的,赋值操作是在执行阶段进行的。
// 示例1
var a = 1;
var a;
console.log(a) // 1;
// 示例2
console.log(a) // undefined
var a = 1;
//等价于
var a;
console.log(a);
a = 1;
上面两个示例的奇怪行为,如果从声明提升的角度去看就很好理解了。
示例1声明了两次变量a,它们都在编译阶段完成声明,JS允许声明同名变量,到了执行代码时,对a进行赋值操作,执行到第3不自然打印结果就是1。
示例2也是同理,声明完成后a的值默认是undefined,到了执行阶段,赋值操作是在打印操作前完成的,所以结果就是undefined。
注意: 每个作用域都会进行声明提升。
console.log(a);
var a = 1;
function foo() {
console.log(b);
var b = 2;
function foo2() {
console.log(c);
var c = 3;
}
foo2();
}
foo(); // undefined undefined undefined
声明提升后是下面这样
var a;
console.log(a);
a = 1;
function foo() {
var b;
console.log(b);
b = 2;
function foo2() {
var c;
console.log(c);
c = 3;
}
foo2();
}
foo(); // undefined undefined undefined
函数声明提升
// 提升前
foo();
function foo(){
console.log(1);//1
}
// 提升后
function foo(){
console.log(1);// 1
}
foo();
函数的调用在函数声明前也可以正常执行,就是因为函数声明提升的原因。
函数声明可以提升,但是函数表达式不会提升。
foo();
var foo = function() {
console.log(1); // foo is not a function
}
// 等价于
var foo;
foo();
foo = function() {
console.log(1); // foo is not a function
}
函数覆盖
既然函数声明和变量声明都会提升,如果出现了同名的函数或变量,谁的优先级更高呢?以下是我个人总结的一套简单的记忆规则,仅供参考。
把函数的权重默认为1,只声明未赋值的变量权重为1,声明且赋值的变量权重为2。高权重覆盖低权重,同权重后者覆盖前者。
// 测试1 权重相等,后者覆盖前者
var a;
function a() {};
console.log(a); function a(){}'
// 测试2 变量权重大于函数,输出变量值
var a = 1;
function a(){}
console.log(a);//1
// 测试3 第一行权重大于第二行,输出1
var a = 1;
var a;
console.log(a);//1
// 测试4 权重相等,后者覆盖前者
a();//2
function a(){
console.log(1);
}
function a(){
console.log(2);
}
养成良好的开发习惯,避免在同一作用域中定义同名的变量或函数。
胖胖熊技术网,基于人工智能的编程学习网站