变量提升即将变量声明提升到它所在
作用域的最开始
的部分
在 js 中只有两种作用域,全局作用域和函数作用域,在ES6之前,js 是没有块级作用域
JavaScript 代码的执行分为两个阶段,第一个阶段在当前词法环境中注册所有的变量和函数声明,简单说就是,解析完成之后,第二个阶段的 JavaScript 执行就开始了
变量提升
num = 6;
var num = 7;
var num;
console.log(num); // 7,也证明了同名变量只会声明一次,其他的会被忽略
// 浏览器引擎会编译为:
var num;
num = 6;
num = 7;
console.log(num); // 7
console.log(a);
var a = 1;
// 浏览器引擎会编译为:
var a;
console.log(a); // undefined
a = 1;
函数提升
1、函数声明提升
foo(); // js 在执行之前,会把 foo 函数提升到最前面,所以我们在 foo 函数定义之前就可以使用 foo 函数
function foo(){
console.log(1)
}
// 浏览器引擎会编译为:
function foo(){
console.log(1)
}
foo();
2、函数表达式提升
foo();
var foo = function(){
console.log(1)
}
// 此种声明方式我们可以理解为一个普通变量的提升,
//在 js 代码执行之前会把 foo 提升带最前面,在函数赋值之前,foo 是 undefined,如果调用 foo(),将会报错
// 浏览器引擎会编译为:
var foo;
foo(); // Uncaught TypeError: foo is not a function
foo = function(){
console.log(1)
}
在定义的函数名字和变量名相同的情况下,函数提升优先级高于变量的例子
func();
var func;
function func() {
console.log(1);
}
func = function() {
console.log(2);
}
// 函数声明和变量声明都会被提升,但是需要注意的是函数会先被提升,然后才是变量
// var func; 尽管出现在 function func() 之前,但它是重复的声明,会被忽略,因为函数声明会被提升到普通变量之前
// 浏览器引擎会编译为:
function func() {
console.log(1);
}
func(); // 1
func = function() {
console.log(2);
}
牢记这三点:
- 只有声明本身会被提升,而赋值操作不会被提升
- 变量会提升到其所在函数的最上面,而不是整个程序的最上面
- 函数声明会被提升,但函数表达式不会被提升
面试题
第一题
var a = 4;
function fun(){
console.log(a) // undefined
var a = 5;
}
fun();
说明: 在上面这段代码中有两个作用域,window 全局作用域和 fun 函数作用域,
在打印变量 a 时,会先在 fun 函数作用域里面查找,因为在执行 fun 函数时,
在函数内部也会先进行变量提升,所以最终的打印结果为 undefined
// 浏览器引擎会编译为:
var a = 4;
function fun(){
var a;
console.log(a) // undefined
a = 5;
}
fun();
这里涉及到一个查找的知识点
考虑以下代码:
function foo(a){
var b = a * 2;
function bar(c){
console.log(a, b, c) // 2, 4, 12
}
bar(b * 3)
}
foo(2)
说明:引擎执行 console.log(...) 声明,并查找 a, b 和 c 三个变量的引用,它首先从最内部的作用域,也就是bar(...) 函数的作用域开始查找,
引擎无法在这里找到 a,因此会去上一级到所嵌套的 foo(...) 的作用域中继续查找,在这里找到了 a,因此引擎使用了这个引用,
对 b 来讲也是一样的,而对于 c 来说,引擎在 bar(...) 中就找到了它
如果 bar(...) 和 foo(...) 的内部都存在一个 c,console.log(...) 就可以直接使用 bar(...) 中的变量,而无需到外面的 foo(...) 中查找
`作用域查找会在找到第一个匹配的标识符时停止`。
在多层的嵌套作用域中可以定义同名的标识符,这叫做 “遮蔽效应”(内部的标识符 “遮蔽” 了外部的标识符)。
抛开遮蔽效应,`作用域查找始终从运行时所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止`。
第二题
function a(){}
var a;
console.log(typeof a) // function
var b = 1;
function b(){}
console.log(typeof b) // number
说明:函数名字和变量名相同的情况下,函数提升优先级高于变量,变量重复的声明,会被忽略
// 浏览器引擎会编译为:
function a(){}
console.log(typeof a) // function
function b(){}
b = 1;
console.log(typeof b) // number
第三题
console.log(typeof a) // function
function a(){}
var a = 1;
说明: 函数提升的优先级高于变量提升,忽略重复声明
// 浏览器引擎会编译为:
function a(){}
console.log(typeof a) // function
a = 1;
第四题
console.log(v1);
var v1 = 100;
function foo(){
console.log(v1);
var v1 = 200;
console.log(v1);
}
foo();
console.log(v1);
// 浏览器引擎会编译为:
var v1;
console.log(v1); // undefined
v1 = 100;
function foo(){
var v1; // undefined
console.log(v1);
v1 = 200;
console.log(v1); // 200
}
foo();
console.log(v1); // 100
网友评论