在抖音刷到了几个变量提升的题后,闲来无事翻了MDN的变量提升的解释,基于国人属性,熟练地切到了中文版,不知道为啥切到英文版,结果发现多了好多东西,有点开到“盲盒”的感觉,记录下。
中文版变量提升
从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。
MDN的这块解释还是挺简短的,主要包含一下内容(简单回顾下):
- 强调了var的声明被提升(默认初始化为undefined)
- 译者注函数和变量相比,会被优先提升。这意味着函数会被提升到更靠前的位置。
中文版结束。
对的,没了……译者很良心,提了下函数声明的提升优先于变量声明,来个例子当回顾了:
console.log(a) // f a(){}
var a = 1
console.log(a) // 1
var a = function () {}
console.log(a) // f () {}
function a() {}
console.log(a) // f () {}
var b = 2;
//非严格模式
(function(a, b){
console.log(a, b) //(f(){}, 2)
var a = 3, b = 4;
console.log(a, b) // (3, 4)
//function a() {} //注释打开 IFFE第一行打印(f a(){}, 2)
})(a, b)
英文版 Hositing
函数提升
强调了在函数声明前可以使用函数(都知道,略过)的优点,否则你得先声明函数,再执行。
变量提升(有干货)
- 强调了js只提升了变量声明,而不是初始化!阐述了即使你在变量声明前初始化,或者在同一行声明并赋值,并不会改变变量提升(中文网言简意赅...)
从概念上讲,变量提升通常是指解释器将代码声明和初始化分离,并将声明移到代码顶部
var提升
- 在使用后声明默认值
undefined;在使用后同时声明和初始化依旧如此; - 如果未使用
var声明,没有提升作用,在初始化前使用该变量则抛异常;初始化后可正常使用
let/const提升
这里可能和红宝书以及其他地方看到的不一样
let声明的变量不会在作用域中被提升...在解析代码时,JavaScript引擎会注意出现在块后面的let声明,只不过在此之前不能以任何方式引用未声明的变量... ——《JavaScript高级程序设计(第4版)》
mdn英文版翻译归来意思是
用
let和const声明的变量也会被提升,但与var不同的是,它们不会用默认值(undefined)初始化。如果在初始化之前读取用let或const声明的变量,则会引发异常。Hoisting works with variables too, so you can use a variable in code before it is declared and/or initialized.
并且强调,重要的是代码的执行顺序,而不是在源文件中写入代码的顺序。只要初始化变量的代码行在读取变量的代码行之前执行,代码就会成功。比如以下代码正常执行:
{
// TDZ starts at beginning of scope
const func = () => console.log(letVar); // OK
// Within the TDZ letVar access throws `ReferenceError`
let letVar = 3; // End of TDZ (for letVar)
func(); // Called outside TDZ!
}
并且给到了let的暂时性死区的相关链接(中文版加点油)
趁热打铁,来看下暂时性死区
暂时性死区——Temporal dead zone (TDZ)
针对let或const变量
let或const变量从代码块开始到声明前的这段时间一直处于“暂时性死区”(TDZ),这段时间内任何形式的访问或引用该变量将导致ReferenceError;直到代码执行到声明(和初始化变量,只声明会默认赋值undefined)的那一行后可用。
与var不同,var在声明之前访问会返回undefined。
temporal ——“暂时性”,是指该区域取决于执行的顺序(时间),而不是代码写入的顺序(位置)。复用上例:
//尽管letvar写入位置在前,但是执行位置或顺序在声明初始化之后,因此代码正常运行
{
// TDZ starts at beginning of scope
const func = () => console.log(letVar); // OK
// Within the TDZ letVar access throws `ReferenceError`
let letVar = 3; // End of TDZ (for letVar)
func(); // Called outside TDZ!
}
暂时性死区和词法作用域相结合的时候:
function test() {
var foo = 33;
//foo 为33 布尔值为true进入if
if (foo) {
//foo被let重新声明为块状作用域变量,但foo+55执行会报错
//因为此时块状作用域的foo还未完成声明和初始化
let foo = foo + 55; // ReferenceError
}
}
test();
同理let n of n.a也不能正常运行。
var与let/const
- 对于
let/const,我们无法使用条件先判断后声明:- 处于暂时性死区的变量使用
typeof会报错 - 不能使用单独的
let/const声明作为块状作用域的主体,因为无法访问到变量 var是函数作用域,可以提升到块状作用域外
- 处于暂时性死区的变量使用
var可以重复声明变量,let/const不可以,两者也不可以混合重复声明
类的提升
使用类声明定义的类,意味着JavaScript具有对类的引用。但是,默认情况下不初始化类,因此在执行其初始化的行之前使用它的任何代码都会抛出异常。例如:
console.log(Rectangle) // ReferenceError
const p = new Rectangle(); // ReferenceError
class Rectangle {}
函数声明和类声明之间的一个重要区别在于,虽然可以在定义之前出现的代码中调用函数,但必须在构造这些函数之前定义类(所以是不是认为没有提升...文档下就直接给了解释)
类声明:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
函数表达式和类表达式提升
函数表达式和类表达式没有提升。
表达式分别求值为函数或类。它们通常被赋值给一个变量或传递给其他函数,通常是变量声明被提升,表达式是它的初始化。因此,直到执行相关的行,表达式才会被求值。
函数表达式:
const getRectArea = function(width, height) {
return width * height;
};
类表达式:
Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
中对英,缩水好多啊,不过看Cache-Control各个字段以及状态码解析还算是全乎的。
文章基本翻译总结自:developer.mozilla.org/en-US/docs/… 有兴趣可以直接去官网看