《ES6标准入门》笔记 | #chap2 变量提升和函数提升

第二章地址

let 和 const 命令

第二章思维导图

第二章读得比较模糊的部分是变量提升和块级作用域。因此查了额外的资料进行补充总结,略过的部分书内都有详细的解释。

变量声明方式

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; //报错,也是属于死区

重复声明

  1. var & function: 可以重复声明。前面的声明均会被最后声明的那行覆盖
  2. 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?
什么是立即执行函数,它有什么作用?

posted @ 2020-11-10 16:30  Ueeei  阅读(120)  评论(0编辑  收藏  举报