《ES6标准入门》笔记 | #chap2 变量提升和函数提升
第二章地址
第二章思维导图
第二章读得比较模糊的部分是变量提升和块级作用域。因此查了额外的资料进行补充总结,略过的部分书内都有详细的解释。
变量声明方式
ES5 | ES6 |
---|---|
var, function | var, function, let, const, import, class |
首先很重要的一点,需要分清的两个概念是:1)函数声明;2)函数表达式。
其实这两个概念都是声明函数对象的方法,最表面上的区别是使用的关键字不同。
函数声明:使用function声明一个函数对象
function foo() {};
函数表达式:使用var, let, const声明一个函数对象
var foo1 = function(){};
let foo2 = function(){};
const foo3 = function(){};
为什么需要分清楚这两个概念呢?因为之后要提到的变量提升会出现三种不同的情况,而这决定变量以哪种方式被提升的是声明的关键字。也就是说,虽然它们都是声明函数对象,但是一个使用function,另一个使用var,还有一个用let,结果会发现被声明的三个函数对象的提升表现均不相同。
一句话总结:变量以何种方式提升,取决于声明的方式(关键字是什么),与变量的类型无关。
变量提升 hoist
变量bar用let命令声明,不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。
摘自“let命令 - 不存在变量提升”。
与书中所讲不同,看了stackoverflow中的第一个回答后,发现六种声明方式其实都有变量提升。阮大神认为,暂时性死区表明了变量此时不存在;而stackoverflow的回答却将暂时性死区作为变量存在的证明。我比较认同后者的理解。
第一种提升方式:var
使用var声明的变量的提升表现为,在声明行之前可以使用,打印出来是undefined
console.log(a); //undefined
console.log(foo); //undefined
foo(); // TypeError: foo is not a function
var a = 1;
var foo = function (){
console.log("foo");
};
上段代码其实相当于:
var a;
var foo;
console.log(a);
console.log(foo);
foo();
a = 1;
foo = function (){
console.log("foo");
};
第二种提升方式:function(函数提升)
直接把函数的代码移到了作用域最顶部,查资料的过程中发现有时候这会被称为函数提升。
console.log(foo); //[Function: foo]
foo(); // i am foo
function foo(){
console.log("i am foo");
};
其实相当于:
function foo(){
console.log("i am foo");
}
console.log(foo);
foo();
第三种提升方式:let & const
let和const的表现一样,仅以let为例,会抛出ReferenceError
console.log(typeof a); //ReferenceError: Cannot access 'a' before initialization
console.log(a); //ReferenceError: Cannot access 'a' before initialization
console.log(foo); //ReferenceError: Cannot access 'foo' before initialization
let a = 1;
let foo = function(){
console.log("i am foo");
}
这说明在声明行之前的作用域,形成了一个暂时性死区。本质就是一进入死区(存在let/const声明的作用域),let和const所声明的变量就已经存在了,也就是说其实存在变量提升,只是这些变量在被声明之前不能使用。
关于暂时性死区,书里有两个挺坑的例子:
function bar(x = y, y = 2){
return [x, y];
}
bar(); //报错:x=y的时候,y还没有声明,属于死区
// 如果用bar(x=2, y=x)就可以
let x = x; //报错,也是属于死区
重复声明
- var & function: 可以重复声明。前面的声明均会被最后声明的那行覆盖
- let & const:不可以重复声明,会报错
var a = 1;
var a = 2; // ok
console.log(a); //2
//---
function foo(){ console.log(1); }
function foo(){ console.log(2); } //ok
foo(); //2
//---
let a = 10;
var a = 1;
console.log(a); // error: Identifier 'a' has already been declared
//---
let a = 10;
let a = 1;
console.log(a); // error: 同上
疑问:为什么块级作用域使得匿名立即执行函数表达式不再必要了?
书里有句话是:
块级作用域的出现,实际上使得获得广泛应用的匿名立即执行函数表达式(匿名 IIFE)不再必要了。
刚读到这一句话的时候我很疑惑,但是看了下面这篇博文就懂了:
上文中有一句话:“立即执行函数会形成一个单独的作用域,我们可以封装一些临时变量或者局部变量,避免污染全局变量”,其实这不就是块级作用域的作用嘛,所以书里才会说块级作用域可以替代匿名立即执行函数。
参考资料
浅谈JS变量提升
js中的变量提升和函数提升
Are variables declared with let or const hoisted?
什么是立即执行函数,它有什么作用?