是用let或const声明的变量
Are variables declared with let or const hoisted?
我玩ES6已经有一段时间了,我注意到虽然用var
声明的变量按预期被提升。。。
console.log(typeof name); // undefined
var name = "John";
用let
或const
声明的变量似乎在提升方面存在一些问题:
console.log(typeof name); // ReferenceError
let name = "John";
和
console.log(typeof name); // ReferenceError
const name = "John";
这是否意味着用let
或const
声明的变量不会被提升?这里到底发生了什么?let
和const
在这个问题上有什么不同吗?
@thefortheye说这些变量在声明之前不能访问是正确的。然而,这比这要复杂一些。
用
let
或const
声明的变量是否未被提升?这里到底发生了什么?
所有声明(var
、let
、const
、function
、function*
、class
)都是";"吊装"。这意味着,如果在一个作用域中声明了一个名称,那么在该作用域中,标识符将始终引用该特定变量:
x = "global";
// function scope:
(function() {
x; // not "global"
var/let/… x;
}());
// block scope (not for `var`s):
{
x; // not "global"
let/const/… x;
}
对于函数和块作用域1都是如此。
var
/function
/function*
声明和let
/const
/class
声明之间的区别在于初始化
前者在作用域顶部创建绑定时使用undefined
或(生成器)函数进行初始化。然而,词法声明的变量保持未初始化。这意味着当您尝试访问ReferenceError
异常时,它会被抛出。只有当let
/const
/class
语句被求值时,它才会被初始化,之前(上面)的所有内容都被称为时间死区。
x = y = "global";
(function() {
x; // undefined
y; // Reference error: y is not defined
var x = "local";
let y = "local";
}());
注意,let y;
语句使用undefined
初始化变量,就像let y = undefined;
一样。
时态死区不是句法位置,而是变量(范围)创建和初始化之间的时间。只要代码没有执行(例如,函数体或简单的死代码),引用声明上方代码中的变量就不是错误,如果您在初始化之前访问变量,即使访问代码在声明下方(例如,在过早调用的提升函数声明中),也会引发异常。
let
和const
在这个问题上有什么不同吗?
不,就吊装而言,它们的工作原理是一样的。它们之间唯一的区别是,const
蚂蚁必须并且只能在声明的初始化器部分分配(const one = 1;
,const one;
和后来的重新分配(如one = 2
)都是无效的)。
1:var
声明仍然只在函数级上工作,当然
引用ECMAScript 6(ECMAScript2015)规范的let
和const
声明部分,
变量是在实例化其包含的词法环境时创建的,但在评估变量的词法绑定之前,不能以任何方式访问。
因此,为了回答您的问题,是的,let
和const
会提升,但在运行时评估实际声明之前,您无法访问它们。
ES6
引入了Let
变量,该变量产生了block level scoping
。在ES5
之前,我们没有block level scoping
,所以在块内声明的变量对于函数级作用域总是hoisted
。
基本上,Scope
指的是您的变量在程序中的可见位置,它决定了您可以在哪里使用您声明的变量。在ES5
中,我们有global scope,function scope and try/catch scope
,在ES6
中,我们还使用Let获得块级范围。
- 当您用
var
关键字定义一个变量时,从定义它的那一刻起,它就知道了整个函数 当您用
let
语句定义一个变量时,它只在定义的块中是已知的。function doSomething(arr){ //i is known here but undefined //j is not known here console.log(i); console.log(j); for(var i=0; i<arr.length; i++){ //i is known here } //i is known here //j is not known here console.log(i); console.log(j); for(let j=0; j<arr.length; j++){ //j is known here } //i is known here //j is not known here console.log(i); console.log(j); } doSomething(["Thalaivar", "Vinoth", "Kabali", "Dinesh"]);
如果您运行该代码,您可以看到变量j
仅在loop
中已知,而在之前和之后都不知道。然而,我们的变量i
从被定义的那一刻起就在entire function
中已知。
使用let还有另一个很大的优势,因为它创建了一个新的词汇环境,还绑定了新的值,而不是保留旧的引用
for(var i=1; i<6; i++){
setTimeout(function(){
console.log(i);
},1000)
}
for(let i=1; i<6; i++){
setTimeout(function(){
console.log(i);
},1000)
}
第一个for
循环总是打印最后一个值,使用let
,它会创建一个新的范围并绑定新的值,打印我们的1, 2, 3, 4, 5
。
到了constants
,它的工作原理基本上和let
一样,唯一的区别是它们的值不能改变。在常量中,允许突变,但不允许重新分配
const foo = {};
foo.bar = 42;
console.log(foo.bar); //works
const name = []
name.push("Vinoth");
console.log(name); //works
const age = 100;
age = 20; //Throws Uncaught TypeError: Assignment to constant variable.
console.log(age);
如果一个常量引用object
,它将始终引用object
,但object
本身可以更改(如果它是可变的)。如果您想要一个不可变的object
,您可以使用Object.freeze([])
根据ECMAScript®2021
Let和Const声明
- let和const声明定义了作用域为正在运行的执行上下文的LexicalEnvironment的变量
- 变量是在实例化其包含的环境记录时创建的,但在评估变量的LexicalBinding之前,可能无法以任何方式访问这些变量
- 当评估LexicalBinding时,由具有Initializer的Lexicalbind定义的变量将被分配其Initializer赋值表达式的值,而不是在创建变量时被分配
- 如果let声明中的LexicalBinding没有Initializer,则在评估Lexicalbind时会为变量分配未定义的值
块声明实例化
- 当评估块或CaseBlock时,将创建一个新的声明性环境记录,并在环境记录中实例化块中声明的每个块范围的变量、常量、函数或类的绑定
- 无论控件如何离开块,LexicalEnvironment都将始终恢复到以前的状态
顶级词汇声明名称
在函数或脚本的顶层,函数声明被视为var声明,而不是词法声明。
结论
- let和const被提升但未初始化。
在变量声明之前引用块中的变量会导致ReferenceError;时间死区"从块开始直到处理声明为止。
下面的例子清楚地说明了";设";变量在词法作用域/嵌套词法作用域中表现。
示例1
var a;
console.log(a); //undefined
console.log(b); //undefined
var b;
let x;
console.log(x); //undefined
console.log(y); // Uncaught ReferenceError: y is not defined
let y;
变量"y"给出了一个referenceError,这并不意味着它没有被提升。该变量是在实例化包含环境时创建的。但是它可能无法访问,因为它处于不可访问的"状态";时间死区";。
示例2
let mylet = 'my value';
(function() {
//let mylet;
console.log(mylet); // "my value"
mylet = 'local value';
})();
示例3
let mylet = 'my value';
(function() {
let mylet;
console.log(mylet); // undefined
mylet = 'local value';
})();
在实施例3中;聚酯薄膜";函数内部的变量在log语句之前没有Initializer,因此值";未定义";。
来源
ECMAMDN
来自MDN web文档:
在ECMAScript 2015中,let
和const
被提升但未初始化。在变量声明之前引用块中的变量会导致ReferenceError
,因为从块开始到处理声明,变量都处于"时间死区"中。
console.log(x); // ReferenceError
let x = 3;
在es6中,当我们使用let或const时,我们必须在使用它们之前声明变量。例如1-
// this will work
u = 10;
var u;
// this will give an error
k = 10;
let k; // ReferenceError: Cannot access 'k' before initialization.
例如。2-
// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9
- 与var不同,它们在提升时不会使用默认值进行初始化
- 在完全初始化之前,无法读取/写入它们
- 使用同一对象中的其他变量声明变量
- 如何将变量声明为全局变量
- 如何将变量声明为全局变量?用case foreach?Javascript
- 理解bootstrap.js变量声明
- 将 Javascript 变量声明为 PHP 形式
- 单个逗号分隔的变量声明和多个声明之间的区别是什么
- Do变量声明初始化函数
- Javascript变量声明:什么是“;var myVariable={}”;
- javascript中不同类型的变量声明
- 为什么在循环的Javascript中包含变量声明
- for 循环中必需的变量声明
- JavaScript 中的变量声明
- 在 Javascript 变量声明中使用逗号
- 你能在变量声明中添加条件吗?
- JavaScript 全局变量声明
- 节点.js变量声明和作用域
- 具有多个逗号分隔值的变量声明是什么意思(例如 var a = b,c,d;)
- 移动图像 ..JavaScript 将变量声明为字符串
- 让 javascript 将(默认情况下)未声明的变量声明到当前本地范围(基于首次使用)而不是全局范围
- 循环中的变量声明