JS-变量提升与暂时性死区概念
变量提升(Hoisting)
变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。在 ECMAScript® 2015 Language Specification 之前的JavaScript文档中找不到变量提升(Hoisting)这个词。例如,从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。
变量声明提升
JavaScript 只会提升声明,不会提升其初始化。如果一个变量先被使用再被声明和赋值的话,使用时的值是 undefined。参见例子:
console.log(num); // Returns undefined var num; num = 6;
变量num提升后代码相当于:
var num; console.log(num); // Returns undefined num = 6;
如果你先赋值、再使用、最后声明该变量,使用时能获取到所赋的值
num = 6; console.log(num); // returns 6 var num;
函数声明提升
JavaScript 在执行任何代码段之前,将函数声明放入内存中的优点之一是,你可以在声明一个函数之前使用该函数。例如:
// 正确的方式:先声明函数,再调用函数 (最佳实践) function catName(name) {console.log("我的猫名叫 " + name); } catName("Tigger"); // 以上代码的执行结果是: "我的猫名叫 Tigger"
上面的代码片按照是你的正常思维(先声明,后调用)去书写的。现在,我们来看看当我们在写这个函数之前调用这个函数会发生什么:
// 不推荐的方式:先调用函数,再声明函数 catName("Chloe"); function catName(name) {console.log("我的猫名叫 " + name); } // 代码执行的结果是: "我的猫名叫 Chloe"
即使我们在定义这个函数之前调用它,函数仍然可以工作。这是因为在 JavaScript 中执行上下文的工作方式造成的。变量提升也适用于其他数据类型和变量。变量可以在声明之前进行初始化和使用。但是如果没有初始化,就不能使用它们。
注:函数和变量相比,会被优先提升。这意味着函数会被提升到更靠前的位置。
函数表达式声明的变量提升
console.log(func); // undefined func() // Uncaught TypeError: func is not a function var func = function() {console.log('hello'); } func(); // output: 'hello'
总结
记住只有声明被提升!!!
-
变量声明提升:var 声明语句,在声明和赋值之前可以访问到,返回值是undefined。
-
函数声明提升:function 声明的函数,可以在声明之前调用该函数(不推荐使用)。
-
函数表达式声明的变量提升:等同于变量提升,声明语句之后可以调用该函数。
解决方法
在JavaScript严格模式(strict mode)下工作(只需要添加 "use strict"; 在源码的最上面),使web浏览器更容易的解析代码。
使用let/const声明变量,这是ES6的一个新特性,而let声明的变量是不存在变量提升的作用。在函数作用域和块作用域(ES6之前无此概念)中实例化时,其中用let和const声明的变量会先被创建,但由于此时还未进行词法绑定,所以无论如何都不能被外部访问,如果试图访问则会抛出错误。而我们所说的暂时性死区(Temporal Dead Zone(TDZ))就是从程序执行作用域内创建变量,直至变量可以被访问这段时间。但是TDZ名词并没有明确地写在ES6的标准文件中,一开始是出现在ES Discussion讨论区中,是对于某些遇到在区块作用域绑定早于声明语句时的状况时,所使用的专用术语。
let/const声明
ES6之前,JavaScript的作用域(scope)分为全局作用域和函数作用域两种,通过var来声明变量。ES6退推出后,就引入了let/const和块级作用域的概念。let/const与var的主要不同有两个地
变量提升(Hoisting)
变量提升(Hoisting)被认为是, Javascript中执行上下文 (特别是创建和执行阶段)工作方式的一种认识。在 ECMAScript® 2015 Language Specification 之前的JavaScript文档中找不到变量提升(Hoisting)这个词。例如,从概念的字面意义上说,“变量提升”意味着变量和函数的声明会在物理层面移动到代码的最前面,但这么说并不准确。实际上变量和函数声明在代码里的位置是不会动的,而是在编译阶段被放入内存中。
变量声明提升
JavaScript 只会提升声明,不会提升其初始化。如果一个变量先被使用再被声明和赋值的话,使用时的值是 undefined。参见例子:
console.log(num); // Returns undefined var num; num = 6;
变量num提升后代码相当于:
var num; console.log(num); // Returns undefined num = 6;
如果你先赋值、再使用、最后声明该变量,使用时能获取到所赋的值
num = 6; console.log(num); // returns 6 var num;
函数声明提升
JavaScript 在执行任何代码段之前,将函数声明放入内存中的优点之一是,你可以在声明一个函数之前使用该函数。例如:
// 正确的方式:先声明函数,再调用函数 (最佳实践) function catName(name) {console.log("我的猫名叫 " + name); } catName("Tigger"); // 以上代码的执行结果是: "我的猫名叫 Tigger"
上面的代码片按照是你的正常思维(先声明,后调用)去书写的。现在,我们来看看当我们在写这个函数之前调用这个函数会发生什么:
// 不推荐的方式:先调用函数,再声明函数 catName("Chloe"); function catName(name) {console.log("我的猫名叫 " + name); } // 代码执行的结果是: "我的猫名叫 Chloe"
即使我们在定义这个函数之前调用它,函数仍然可以工作。这是因为在 JavaScript 中执行上下文的工作方式造成的。变量提升也适用于其他数据类型和变量。变量可以在声明之前进行初始化和使用。但是如果没有初始化,就不能使用它们。
注:函数和变量相比,会被优先提升。这意味着函数会被提升到更靠前的位置。
函数表达式声明的变量提升
console.log(func); // undefined func() // Uncaught TypeError: func is not a function var func = function() {console.log('hello'); } func(); // output: 'hello'
总结
记住只有声明被提升!!!
-
变量声明提升:var 声明语句,在声明和赋值之前可以访问到,返回值是undefined。
-
函数声明提升:function 声明的函数,可以在声明之前调用该函数(不推荐使用)。
-
函数表达式声明的变量提升:等同于变量提升,声明语句之后可以调用该函数。
解决方法
在JavaScript严格模式(strict mode)下工作(只需要添加 "use strict"; 在源码的最上面),使web浏览器更容易的解析代码。
使用let/const声明变量,这是ES6的一个新特性,而let声明的变量是不存在变量提升的作用。在函数作用域和块作用域(ES6之前无此概念)中实例化时,其中用let和const声明的变量会先被创建,但由于此时还未进行词法绑定,所以无论如何都不能被外部访问,如果试图访问则会抛出错误。而我们所说的暂时性死区(Temporal Dead Zone(TDZ))就是从程序执行作用域内创建变量,直至变量可以被访问这段时间。但是TDZ名词并没有明确地写在ES6的标准文件中,一开始是出现在ES Discussion讨论区中,是对于某些遇到在区块作用域绑定早于声明语句时的状况时,所使用的专用术语。
let/const声明
ES6之前,JavaScript的作用域(scope)分为全局作用域和函数作用域两种,通过var来声明变量。ES6退推出后,就引入了let/const和块级作用域的概念。let/const与var的主要不同有两个地方,下面通过几个示例来感受:
// undefinedconsole.log(name1); // Uncaught ReferenceError: Cannot access 'name2' before initializationconsole.log(name2); // Uncaught ReferenceError: Cannot access 'name2' before initializationconsole.log(name3);var name1 = "Jack";let name2 = "Alice";const name3 = "Bob";
在let/const声明之前访问对应的变量或常量,会报错Uncaught ReferenceError;var声明之前访问变量,则会得到undefined。
var a = 8;if(true) { // Uncaught ReferenceError: Cannot access 'a' before initializationa = 88; let a; }
这里的if语句中a明明上边var声明并赋值了,为什么它还报个不能在初始化前访问呢?ES6明确规定如果区块中存在let/const命令,这个区块就对这些命令声明的变量,形成了一个封闭作用域。说白了就是在代码块内,在变量声明之前使用都是不可取的,会抛出错误。
if (true) {// TDZ开始tmp = 'abc'; // ReferenceErrorconsole.log(tmp); // ReferenceError let tmp; // TDZ结束console.log(tmp); // undefined tmp = 123;console.log(tmp); // 123 }运行结果:
参考:Hoisting(变量提升) - 术语表 | MDN | 理解ES6中的TDZ(暂时性死区) - 掘金