“让” 和 “让” 和有什么不一样? 和“var”?

发布于 2024-07-17 02:00:44 字数 284 浏览 12 评论 0 原文

ECMAScript 6 引入了 let 语句

我听说它被描述为局部变量,但我仍然不太确定它的行为与 var 关键字有何不同。

有什么区别? 什么时候应该使用 let 而不是 var

ECMAScript 6 introduced the let statement.

I've heard that it's described as a local variable, but I'm still not quite sure how it behaves differently than the var keyword.

What are the differences? When should let be used instead of var?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

魔法唧唧 2024-07-24 02:00:44

范围规则

主要区别在于范围规则。 由 var 关键字声明的变量的作用域为直接函数体(因此是函数作用域),而 let 变量的作用域为直接封闭块,表示为通过 { } (因此是块作用域)。

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

语言中引入 let 关键字的原因是函数作用域令人困惑,并且是 JavaScript 中错误的主要来源之一。

看一下另一个 Stack Overflow 问题中的示例:

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

每次调用 funcs[j](); 时,My value: 3 都会输出到控制台,因为匿名函数绑定到同一变量。

人们必须创建立即调用的函数来从循环中捕获正确的值,但这也很麻烦。

提升使用

var 关键字声明的变量提升和初始化,这意味着即使在声明它们之前它们也可以在其封闭范围内访问,但是在到达声明语句之前它们的值是未定义

function checkHoisting() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

let 变量已被提升,但未提升初始化直到它们的定义被评估。 在初始化之前访问它们会导致 ReferenceError。 据说该变量从开始时就位于时间死区阻塞直到声明语句被处理。

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

创建全局对象属性

在顶层,letvar 不同,不会在全局对象上创建属性:

var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped but not part of the global object

console.log(window.foo); // Foo
console.log(window.bar); // undefined

重新声明

在严格模式下,var 将允许您在同一范围内重新声明相同的变量,而 let 会引发 SyntaxError。

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.

let bar = "bar1"; 
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

Scoping rules

The main difference is scoping rules. Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).

function run() {
  var foo = "Foo";
  let bar = "Bar";

  console.log(foo, bar); // Foo Bar

  {
    var moo = "Mooo"
    let baz = "Bazz";
    console.log(moo, baz); // Mooo Bazz
  }

  console.log(moo); // Mooo
  console.log(baz); // ReferenceError
}

run();

The reason why let keyword was introduced to the language was function scope is confusing and was one of the main sources of bugs in JavaScript.

Take a look at this example from another Stack Overflow question:

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i++) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: " + i);
  };
}
for (var j = 0; j < 3; j++) {
  // and now let's run each one to see
  funcs[j]();
}

My value: 3 was output to console each time funcs[j](); was invoked since anonymous functions were bound to the same variable.

People had to create immediately invoked functions to capture correct values from the loops but that was also hairy.

Hoisting

Variables declared with var keyword are hoisted and initialized which means they are accessible in their enclosing scope even before they are declared, however their value is undefined before the declaration statement is reached:

function checkHoisting() {
  console.log(foo); // undefined
  var foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

let variables are hoisted but not initialized until their definition is evaluated. Accessing them before the initialization results in a ReferenceError. The variable is said to be in the temporal dead zone from the start of the block until the declaration statement is processed.

function checkHoisting() {
  console.log(foo); // ReferenceError
  let foo = "Foo";
  console.log(foo); // Foo
}

checkHoisting();

Creating global object property

At the top level, let, unlike var, does not create a property on the global object:

var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped but not part of the global object

console.log(window.foo); // Foo
console.log(window.bar); // undefined

Redeclaration

In strict mode, var will let you re-declare the same variable in the same scope while let raises a SyntaxError.

'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo1' is replaced with 'foo2'.

let bar = "bar1"; 
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared

若有似无的小暗淡 2024-07-24 02:00:44

let 也可以用来避免闭包问题。 它绑定新值而不是保留旧引用,如下例所示。

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

上面的代码演示了一个经典的 JavaScript 闭包问题。 对 i 变量的引用存储在点击处理程序闭包中,而不是 i 的实际值。

每个单击处理程序都将引用同一个对象,因为只有一个计数器对象保存 6,因此每次单击都会获得 6 个值。

一般的解决方法是将其包装在匿名函数中并将 i 作为参数传递。 现在也可以通过使用 let 代替 var 来避免此类问题,如下面的代码所示。

(在 Chrome 和 Firefox 50 中测试)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

let can also be used to avoid problems with closures. It binds fresh value rather than keeping an old reference as shown in examples below.

for(var i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

Code above demonstrates a classic JavaScript closure problem. Reference to the i variable is being stored in the click handler closure, rather than the actual value of i.

Every single click handler will refer to the same object because there’s only one counter object which holds 6 so you get six on each click.

A general workaround is to wrap this in an anonymous function and pass i as an argument. Such issues can also be avoided now by using let instead var as shown in the code below.

(Tested in Chrome and Firefox 50)

for(let i=1; i<6; i++) {
  $("#div" + i).click(function () { console.log(i); });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>Clicking on each number will log to console:</p> 
<div id="div1">1</div>
<div id="div2">2</div>
<div id="div3">3</div>
<div id="div4">4</div>
<div id="div5">5</div>

若沐 2024-07-24 02:00:44

letvar 有什么区别?

  • 使用 var 语句定义的变量在整个 函数定义它,从函数的开头开始。 (*)
  • 使用 let 语句定义的变量仅在 定义它的块,从定义它的那一刻起。 (**)

要理解其中的差异,请考虑以下代码:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

在这里,我们可以看到我们的变量 j 仅在第一个 for 循环中已知,而在 和 之前不已知。后。 然而,我们的变量 i 在整个函数中是已知的。

另外,请考虑块作用域变量在声明之前是未知的,因为它们没有被提升。 您也不允许在同一块内重新声明同一块作用域变量。 这使得块作用域变量比全局或函数作用域变量更不容易出错,全局或函数作用域变量被提升并且在多个声明的情况下不会产生任何错误。


今天使用 let 安全吗?

有些人会争辩说,将来我们将只使用 let 语句,而 var 语句将变得过时。 JavaScript 大师 Kyle Simpson 撰写了 一篇非常详尽的文章,解释了为什么他认为情况不会如此

然而今天,情况绝对不是这样了。 事实上,我们实际上需要问自己使用 let 语句是否安全。 该问题的答案取决于您的环境:

  • 如果您正在编写服务器端 JavaScript 代码 (Node .js),您可以安全地使用 let 语句。

  • 如果您正在编写客户端 JavaScript 代码并使用基于浏览器的转译器(例如 Traceur babel-standalone),您可以安全地使用 let 语句,但是您的代码在性能方面可能并非最佳。

  • 如果您正在编写客户端 JavaScript 代码并使用基于 Node 的转译器(例如 traceur shell 脚本Babel ),您可以安全地使用 let 语句。 而且,由于您的浏览器只会了解转译的代码,因此性能缺陷应该是有限的。

  • 如果您正在编写客户端 JavaScript 代码并且不使用转译器,则需要考虑浏览器支持。

    仍然有一些浏览器根本不支持 let

在此处输入图像描述


如何跟踪浏览器支持

有关哪些浏览器支持的最新概述>let 语句在您阅读此答案时,请参阅我可以吗使用页面


(*) 全局和功能范围的变量可以在声明之前初始化和使用,因为 JavaScript 变量是 提升这意味着声明始终移动到范围的顶部。

(**) 块作用域变量未提升

What's the difference between let and var?

  • A variable defined using a var statement is known throughout the function it is defined in, from the start of the function. (*)
  • A variable defined using a let statement is only known in the block it is defined in, from the moment it is defined onward. (**)

To understand the difference, consider the following code:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Here, we can see that our variable j is only known in the first for loop, but not before and after. Yet, our variable i is known in the entire function.

Also, consider that block scoped variables are not known before they are declared because they are not hoisted. You're also not allowed to redeclare the same block scoped variable within the same block. This makes block scoped variables less error prone than globally or functionally scoped variables, which are hoisted and which do not produce any errors in case of multiple declarations.


Is it safe to use let today?

Some people would argue that in the future we'll ONLY use let statements and that var statements will become obsolete. JavaScript guru Kyle Simpson wrote a very elaborate article on why he believes that won't be the case.

Today, however, that is definitely not the case. In fact, we need actually to ask ourselves whether it's safe to use the let statement. The answer to that question depends on your environment:

  • If you're writing server-side JavaScript code (Node.js), you can safely use the let statement.

  • If you're writing client-side JavaScript code and use a browser based transpiler (like Traceur or babel-standalone), you can safely use the let statement, however your code is likely to be anything but optimal with respect to performance.

  • If you're writing client-side JavaScript code and use a Node based transpiler (like the traceur shell script or Babel), you can safely use the let statement. And, because your browser will only know about the transpiled code, performance drawbacks should be limited.

  • If you're writing client-side JavaScript code and don't use a transpiler, you need to consider browser support.

    There are still some browsers that don't support let at all :

enter image description here


How to keep track of browser support

For an up-to-date overview of which browsers support the let statement at the time of your reading this answer, see this Can I Use page.


(*) Globally and functionally scoped variables can be initialized and used before they are declared because JavaScript variables are hoisted. This means that declarations are always moved to the top of the scope.

(**) Block scoped variables are not hoisted

所谓喜欢 2024-07-24 02:00:44

以下是let 关键字的说明以及一些示例。

let 的工作方式与 var 非常相似。 主要区别在于 var 变量的作用域是整个封闭函数

维基百科上的此表显示了哪些浏览器支持 Javascript 1.7。

请注意,只有 Mozilla 和 Chrome 浏览器支持它。 IE、Safari 和其他可能的浏览器都没有。

Here's an explanation of the let keyword with some examples.

let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function

This table on Wikipedia shows which browsers support Javascript 1.7.

Note that only Mozilla and Chrome browsers support it. IE, Safari, and potentially others don't.

这个俗人 2024-07-24 02:00:44

let

块作用域

使用 let 关键字声明的变量是块作用域的,这意味着它们仅在 声明它们的块

在顶层(函数外部)

在顶层,使用 let 声明的变量不会在全局对象上创建属性。

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

在函数内部

在函数内部(但在块之外),letvar 具有相同的作用域。

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

块内部 在

块内部使用 let 声明的变量无法在该块外部访问。

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

循环内部 在循环

中使用 let 声明的变量只能在该循环内部引用。

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

带闭包的循环

如果您在循环中使用 let 而不是 var,则每次迭代都会获得一个新变量。 这意味着您可以安全地在循环内使用闭包。

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

临时死区

由于临时死区,无法访问使用let声明的变量在宣布之前。 尝试这样做会引发错误。

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

无需重新声明

您不能使用 let 多次声明同一变量。 您也不能使用 let 声明的变量与使用 var 声明的另一个变量具有相同的标识符。

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

constlet 非常相似——它是块作用域的并且有 TDZ。 然而,有两点是不同的。

不可重新分配

使用 const 声明的变量无法重新分配。

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

请注意,这并不意味着该值是不可变的。 它的属性仍然可以改变。

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

如果你想要一个不可变的对象,你应该使用 Object.freeze()

const obj = Object.freeze({a: 40});
obj.a = 42;
console.log(obj.a); // 40
console.log(obj.b); // undefined

需要初始化程序

在使用 const 声明变量时,您始终必须指定一个值。

const a; // SyntaxError: Missing initializer in const declaration

let

Block scope

Variables declared using the let keyword are block-scoped, which means that they are available only in the block in which they were declared.

At the top level (outside of a function)

At the top level, variables declared using let don't create properties on the global object.

var globalVariable = 42;
let blockScopedVariable = 43;

console.log(globalVariable); // 42
console.log(blockScopedVariable); // 43

console.log(this.globalVariable); // 42
console.log(this.blockScopedVariable); // undefined

Inside a function

Inside a function (but outside of a block), let has the same scope as var.

(() => {
  var functionScopedVariable = 42;
  let blockScopedVariable = 43;

  console.log(functionScopedVariable); // 42
  console.log(blockScopedVariable); // 43
})();

console.log(functionScopedVariable); // ReferenceError: functionScopedVariable is not defined
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Inside a block

Variables declared using let inside a block can't be accessed outside that block.

{
  var globalVariable = 42;
  let blockScopedVariable = 43;
  console.log(globalVariable); // 42
  console.log(blockScopedVariable); // 43
}

console.log(globalVariable); // 42
console.log(blockScopedVariable); // ReferenceError: blockScopedVariable is not defined

Inside a loop

Variables declared with let in loops can be referenced only inside that loop.

for (var i = 0; i < 3; i++) {
  var j = i * 2;
}
console.log(i); // 3
console.log(j); // 4

for (let k = 0; k < 3; k++) {
  let l = k * 2;
}
console.log(typeof k); // undefined
console.log(typeof l); // undefined
// Trying to do console.log(k) or console.log(l) here would throw a ReferenceError.

Loops with closures

If you use let instead of var in a loop, with each iteration you get a new variable. That means that you can safely use a closure inside a loop.

// Logs 3 thrice, not what we meant.
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 0);
}

// Logs 0, 1 and 2, as expected.
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 0);
}

Temporal dead zone

Because of the temporal dead zone, variables declared using let can't be accessed before they are declared. Attempting to do so throws an error.

console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;

No re-declaring

You can't declare the same variable multiple times using let. You also can't declare a variable using let with the same identifier as another variable which was declared using var.

var a;
var a; // Works fine.

let b;
let b; // SyntaxError: Identifier 'b' has already been declared

var c;
let c; // SyntaxError: Identifier 'c' has already been declared

const

const is quite similar to let—it's block-scoped and has TDZ. There are, however, two things which are different.

No re-assigning

Variable declared using const can't be re-assigned.

const a = 42;
a = 43; // TypeError: Assignment to constant variable.

Note that it doesn't mean that the value is immutable. Its properties still can be changed.

const obj = {};
obj.a = 42;
console.log(obj.a); // 42

If you want to have an immutable object, you should use Object.freeze().

const obj = Object.freeze({a: 40});
obj.a = 42;
console.log(obj.a); // 40
console.log(obj.b); // undefined

Initializer is required

You always must specify a value when declaring a variable using const.

const a; // SyntaxError: Missing initializer in const declaration
喜爱纠缠 2024-07-24 02:00:44

接受的答案遗漏了一点:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined

The accepted answer is missing a point:

{
  let a = 123;
};

console.log(a); // ReferenceError: a is not defined
梦中楼上月下 2024-07-24 02:00:44

用最基本的术语来说,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️ 沙盒可以玩 ↓

编辑 let 与 var

In most basic terms,

for (let i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i not accessible ❌

for (var i = 0; i < 5; i++) {
  // i accessible ✔️
}
// i accessible ✔️

⚡️ Sandbox to play around ↓

Edit let vs var

暖心男生 2024-07-24 02:00:44

以下是两者之间差异的示例:
在此处输入图像描述

正如您所看到的,var j 变量仍然具有超出for 循环作用域(块作用域),但 let i 变量在 for 循环作用域之外未定义。

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);

Here is an example of the difference between the two:
enter image description here

As you can see, the var j variable still has a value outside the for loop scope (Block Scope), but the let i variable is undefined outside of the for loop scope.

"use strict";
console.log("var:");
for (var j = 0; j < 2; j++) {
  console.log(j);
}

console.log(j);

console.log("let:");
for (let i = 0; i < 2; i++) {
  console.log(i);
}

console.log(i);

盗心人 2024-07-24 02:00:44

主要区别在于范围差异,而let只能在其声明的范围内可用,就像在for循环中一样,var例如,可以在循环外部访问。 来自 MDN 中的文档(示例还来自 MDN):

let 允许您声明范围仅限于使用该变量的块、语句或表达式的变量。 这与 var 关键字不同,后者全局定义变量,或者在整个函数本地定义变量,而不管块作用域如何。

let 声明的变量的作用域是定义它们的块以及任何包含的子块。 这样,let 的工作方式与 var 非常相似。 主要区别在于 var 变量的作用域是整个封闭函数:

函数 varTest() { 
    var x = 1; 
    如果属实) { 
      var x = 2;   // 相同的变量! 
      控制台.log(x);   // 2 
    } 
    控制台.log(x);   // 2 
  } 

  函数让测试(){ 
    设 x = 1; 
    如果属实) { 
      设 x = 2;   // 不同的变量 
      控制台.log(x);   // 2 
    } 
    控制台.log(x);   // 1 
  }` 
  

在程序和函数的顶层,letvar 不同,不会在全局对象上创建属性。 例如:

var x = '全局'; 
  让 y = '全局'; 
  console.log(this.x);   // “全球的” 
  console.log(this.y);   // 不明确的 
  

当在块内使用时,let 将变量的范围限制为该块。 请注意 var 之间的区别,其作用域位于声明它的函数内部。

var a = 1; 
  var b = 2; 

  如果(a === 1){ 
    var a = 11;   // 作用域是全局的 
    令 b = 22;   // 范围在 if 块内 

    控制台.log(a);   // 11 
    控制台.log(b);   // 22 
  }  

  控制台.log(a);   // 11 
  控制台.log(b);   // 2 
  

另外不要忘记它是 ECMA6 功能,因此尚未完全支持,因此最好始终使用 Babel 等将其转换为 ECMA5...有关访问 babel 网站

The main difference is the scope difference, while let can be only available inside the scope it's declared, like in for loop, var can be accessed outside the loop for example. From the documentation in MDN (examples also from MDN):

let allows you to declare variables that are limited in scope to the block, statement, or expression on which it is used. This is unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Variables declared by let have as their scope the block in which they are defined, as well as in any contained sub-blocks. In this way, let works very much like var. The main difference is that the scope of a var variable is the entire enclosing function:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  }
  console.log(x);  // 2
}

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  }
  console.log(x);  // 1
}`

At the top level of programs and functions, let, unlike var, does not create a property on the global object. For example:

var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined

When used inside a block, let limits the variable's scope to that block. Note the difference between var whose scope is inside the function where it is declared.

var a = 1;
var b = 2;

if (a === 1) {
  var a = 11; // the scope is global
  let b = 22; // the scope is inside the if-block

  console.log(a);  // 11
  console.log(b);  // 22
} 

console.log(a); // 11
console.log(b); // 2

Also don't forget it's ECMA6 feature, so it's not fully supported yet, so it's better always transpiles it to ECMA5 using Babel etc... for more info about visit babel website

南街女流氓 2024-07-24 02:00:44

有一些细微的差异 - let 作用域的行为或多或少与其他语言中的变量作用域的行为相似。

例如,它的作用域为封闭块,它们在声明之前并不存在,等等。

但是值得注意的是 let 只是较新的 Javascript 实现的一部分,并且具有不同程度的 浏览器支持

There are some subtle differences — let scoping behaves more like variable scoping does in more or less any other languages.

e.g. It scopes to the enclosing block, They don't exist before they're declared, etc.

However it's worth noting that let is only a part of newer Javascript implementations and has varying degrees of browser support.

不知所踪 2024-07-24 02:00:44
  • 变量未提升

    let不会提升到它们出现的块的整个范围。相比之下,var 可以如下提升.

    <前><代码>{
    控制台.日志(抄送); // 不明确的。 吊装引起的
    var CC = 23;
    }

    {
    控制台.log(bb); // ReferenceError: bb 未定义
    设 bb = 23;
    }

    实际上,根据@Bergi,两者varlet 被提升

  • 垃圾收集

    let 的块作用域对于回收内存的闭包和垃圾收集很有用。 考虑一下,

    函数处理(数据){ 
          //... 
      } 
    
      var 巨大数据 = { .. }; 
    
      处理(大数据); 
    
      var btn = document.getElementById("mybutton"); 
      btn.addEventListener(“点击”, 函数点击(evt){ 
          //.... 
      }); 
      

    click 处理程序回调根本不需要 hugeData 变量。 理论上,在 process(..) 运行后,巨大的数据结构 hugeData 可以被垃圾收集。 但是,某些 JS 引擎可能仍然需要保留这个庞大的结构,因为 click 函数在整个范围内都有一个闭包。

    但是,块作用域可能会使这个巨大的数据结构被垃圾收集。

    函数处理(数据){ 
          //... 
      } 
    
      { // 该块内声明的任何内容都可以被垃圾收集 
          让巨大的数据 = { .. }; 
          处理(大数据); 
      } 
    
      var btn = document.getElementById("mybutton"); 
      btn.addEventListener(“点击”, 函数点击(evt){ 
          //.... 
      }); 
      
  • let 循环

    循环中的

    let 可以将其重新绑定到循环的每次迭代,确保为其重新分配上一个循环迭代结束时的值。 考虑一下,

    // 打印 '5' 5 次 
      for (var i = 0; i < 5; ++i) { 
          设置超时(函数(){ 
              控制台.log(i); 
          }, 1000);   
      } 
      

    但是,请将 var 替换为 let

    // 现在打印 1, 2, 3, 4, 5 
      for (令 i = 0; i < 5; ++i) { 
          设置超时(函数(){ 
              控制台.log(i); 
          }, 1000);   
      } 
      

    因为 let 使用这些名称为 a) 初始化表达式 b) 每次迭代(之前是评估增量表达式)创建一个新的词法环境,更多详细信息为 这里

  • Variable Not Hoisting

    let will not hoist to the entire scope of the block they appear in. By contrast, var could hoist as below.

    {
       console.log(cc); // undefined. Caused by hoisting
       var cc = 23;
    }
    
    {
       console.log(bb); // ReferenceError: bb is not defined
       let bb = 23;
    }
    

    Actually, Per @Bergi, Both var and let are hoisted.

  • Garbage Collection

    Block scope of let is useful relates to closures and garbage collection to reclaim memory. Consider,

    function process(data) {
        //...
    }
    
    var hugeData = { .. };
    
    process(hugeData);
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    

    The click handler callback does not need the hugeData variable at all. Theoretically, after process(..) runs, the huge data structure hugeData could be garbage collected. However, it's possible that some JS engine will still have to keep this huge structure, since the click function has a closure over the entire scope.

    However, the block scope can make this huge data structure to garbage collected.

    function process(data) {
        //...
    }
    
    { // anything declared inside this block can be garbage collected
        let hugeData = { .. };
        process(hugeData);
    }
    
    var btn = document.getElementById("mybutton");
    btn.addEventListener( "click", function click(evt){
        //....
    });
    
  • let loops

    let in the loop can re-binds it to each iteration of the loop, making sure to re-assign it the value from the end of the previous loop iteration. Consider,

    // print '5' 5 times
    for (var i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    However, replace var with let

    // print 1, 2, 3, 4, 5. now
    for (let i = 0; i < 5; ++i) {
        setTimeout(function () {
            console.log(i);
        }, 1000);  
    }
    

    Because let create a new lexical environment with those names for a) the initialiser expression b) each iteration (previosly to evaluating the increment expression), more details are here.

胡渣熟男 2024-07-24 02:00:44

区别在于每个变量声明的范围

在实践中,作用域的差异会产生许多有用的结果:

  1. let 变量仅在其最近的封闭块 中可见({ ... }< /代码>)。
  2. let 变量只能在声明变量之后出现的代码行中使用(即使它们已被提升!)。
  3. let 变量不能由后续的 varlet 重新声明。
  4. 全局 let 变量不会添加到全局 window 对象中。
  5. let 变量易于使用与闭包(它们不会导致竞争条件)。

let 施加的限制降低了变量的可见性,并增加了尽早发现意外名称冲突的可能性。 这使得跟踪和推理变量变得更加容易,包括它们的可达性(帮助回收未使用的内存) 。

因此,当在大型程序中使用或以新的和意想不到的方式组合独立开发的框架时,let 变量不太可能引起问题。

如果您确定在循环中使用闭包 (#5) 或在代码中声明外部可见的全局变量 (#4) 时想要单绑定效果,则 var 可能仍然有用。 如果 export 从转译器空间迁移到核心语言中。

示例

1. 在最近的封闭块之外不使用:
此代码块将引发引用错误,因为第二次使用 x 发生在用 let 声明它的块之外:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

相反,使用 var 的相同示例有效。

2. 声明前不使用:
此代码块将在代码运行之前抛出 ReferenceError,因为 x 在声明之前使用:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

相反,使用 var 的相同示例code> 解析并运行,不会引发任何异常。

3. 无需重新声明:
以下代码演示了用 let 声明的变量以后不能重新声明:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4. 全局变量未附加到窗口

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5. 易于使用闭包:
var 声明的变量不能很好地与循环内的闭包配合使用。 这是一个简单的循环,它输出变量 i 在不同时间点所具有的值序列:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

具体来说,此输出:

i is 0
i is 1
i is 2
i is 3
i is 4

在 JavaScript 中,我们经常在比创建变量晚得多的时间使用变量。 当我们通过传递给 setTimeout 的闭包来延迟输出来演示这一点时:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

...只要我们坚持使用 let,输出就保持不变。 相反,如果我们使用 var i 来代替:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

...循环意外地输出“i is 5”五次:

i is 5
i is 5
i is 5
i is 5
i is 5

The difference is in the scope of the variables declared with each.

In practice, there are a number of useful consequences of the difference in scope:

  1. let variables are only visible in their nearest enclosing block ({ ... }).
  2. let variables are only usable in lines of code that occur after the variable is declared (even though they are hoisted!).
  3. let variables may not be redeclared by a subsequent var or let.
  4. Global let variables are not added to the global window object.
  5. let variables are easy to use with closures (they do not cause race conditions).

The restrictions imposed by let reduce the visibility of the variables and increase the likelihood that unexpected name collisions will be found early. This makes it easier to track and reason about variables, including their reachability(helping with reclaiming unused memory).

Consequently, let variables are less likely to cause problems when used in large programs or when independently-developed frameworks are combined in new and unexpected ways.

var may still be useful if you are sure you want the single-binding effect when using a closure in a loop (#5) or for declaring externally-visible global variables in your code (#4). Use of var for exports may be supplanted if export migrates out of transpiler space and into the core language.

Examples

1. No use outside nearest enclosing block:
This block of code will throw a reference error because the second use of x occurs outside of the block where it is declared with let:

{
    let x = 1;
}
console.log(`x is ${x}`);  // ReferenceError during parsing: "x is not defined".

In contrast, the same example with var works.

2. No use before declaration:
This block of code will throw a ReferenceError before the code can be run because x is used before it is declared:

{
    x = x + 1;  // ReferenceError during parsing: "x is not defined".
    let x;
    console.log(`x is ${x}`);  // Never runs.
}

In contrast, the same example with var parses and runs without throwing any exceptions.

3. No redeclaration:
The following code demonstrates that a variable declared with let may not be redeclared later:

let x = 1;
let x = 2;  // SyntaxError: Identifier 'x' has already been declared

4. Globals not attached to window:

var button = "I cause accidents because my name is too common.";
let link = "Though my name is common, I am harder to access from other JS files.";
console.log(link);  // OK
console.log(window.link);  // undefined (GOOD!)
console.log(window.button);  // OK

5. Easy use with closures:
Variables declared with var do not work well with closures inside loops. Here is a simple loop that outputs the sequence of values that the variable i has at different points in time:

for (let i = 0; i < 5; i++) {
    console.log(`i is ${i}`), 125/*ms*/);
}

Specifically, this outputs:

i is 0
i is 1
i is 2
i is 3
i is 4

In JavaScript we often use variables at a significantly later time than when they are created. When we demonstrate this by delaying the output with a closure passed to setTimeout:

for (let i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... the output remains unchanged as long as we stick with let. In contrast, if we had used var i instead:

for (var i = 0; i < 5; i++) {
    setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}

... the loop unexpectedly outputs "i is 5" five times:

i is 5
i is 5
i is 5
i is 5
i is 5
夜清冷一曲。 2024-07-24 02:00:44

这是一个添加到其他人已经写过的内容的示例。 假设您想要创建一个函数数组 adderFunctions,其中每个函数采用单个 Number 参数,并返回该参数与函数在数组中的索引之和。 尝试使用 var 关键字通过循环生成 adderFunctions 不会像人们天真的期望的那样工作:

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

上面的过程不会生成所需的函数数组,因为 < code>i 的范围超出了创建每个函数的 for 块的迭代范围。 相反,在循环结束时,每个函数闭包中的 i 引用 i 中每个匿名函数在循环结束时的值 (1000)。代码>adderFunctions。 这根本不是我们想要的:现在内存中有 1000 个不同函数的数组,它们的行为完全相同。 如果我们随后更新i的值,则突变将影响所有adderFunctions

不过,我们可以使用 let 关键字重试:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

这一次,ifor 循环的每次迭代中都会反弹。 现在,每个函数都保留创建函数时的 i 值,并且 adderFunctions 的行为符合预期。

现在,将这两种行为进行图像混合,您可能会明白为什么不建议将较新的 letconst 与较旧的 var 混合在一起相同的脚本。 这样做可能会导致一些极其混乱的代码。

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

不要让这种事发生在你身上。 使用短绒棉。

注意:这是一个教学示例,旨在演示循环中的 var/let 行为以及函数闭包,这也很容易去理解。 这将是一种糟糕的数字相加方式。 但是在匿名函数闭包中捕获数据的一般技术可能会在现实世界的其他上下文中遇到。 YMMV。

Here's an example to add on to what others have already written. Suppose you want to make an array of functions, adderFunctions, where each function takes a single Number argument and returns the sum of the argument and the function's index in the array. Trying to generate adderFunctions with a loop using the var keyword won't work the way someone might naïvely expect:

// An array of adder functions.
var adderFunctions = [];

for (var i = 0; i < 1000; i++) {
  // We want the function at index i to add the index to its argument.
  adderFunctions[i] = function(x) {
    // What is i bound to here?
    return x + i;
  };
}

var add12 = adderFunctions[12];

// Uh oh. The function is bound to i in the outer scope, which is currently 1000.
console.log(add12(8) === 20); // => false
console.log(add12(8) === 1008); // => true
console.log(i); // => 1000

// It gets worse.
i = -8;
console.log(add12(8) === 0); // => true

The process above doesn't generate the desired array of functions because i's scope extends beyond the iteration of the for block in which each function was created. Instead, at the end of the loop, the i in each function's closure refers to i's value at the end of the loop (1000) for every anonymous function in adderFunctions. This isn't what we wanted at all: we now have an array of 1000 different functions in memory with exactly the same behavior. And if we subsequently update the value of i, the mutation will affect all the adderFunctions.

However, we can try again using the let keyword:

// Let's try this again.
// NOTE: We're using another ES6 keyword, const, for values that won't
// be reassigned. const and let have similar scoping behavior.
const adderFunctions = [];

for (let i = 0; i < 1000; i++) {
  // NOTE: We're using the newer arrow function syntax this time, but 
  // using the "function(x) { ..." syntax from the previous example 
  // here would not change the behavior shown.
  adderFunctions[i] = x => x + i;
}

const add12 = adderFunctions[12];

// Yay! The behavior is as expected. 
console.log(add12(8) === 20); // => true

// i's scope doesn't extend outside the for loop.
console.log(i); // => ReferenceError: i is not defined

This time, i is rebound on each iteration of the for loop. Each function now keeps the value of i at the time of the function's creation, and adderFunctions behaves as expected.

Now, image mixing the two behaviors and you'll probably see why it's not recommended to mix the newer let and const with the older var in the same script. Doing so can result is some spectacularly confusing code.

const doubleAdderFunctions = [];

for (var i = 0; i < 1000; i++) {
    const j = i;
    doubleAdderFunctions[i] = x => x + i + j;
}

const add18 = doubleAdderFunctions[9];
const add24 = doubleAdderFunctions[12];

// It's not fun debugging situations like this, especially when the
// code is more complex than in this example.
console.log(add18(24) === 42); // => false
console.log(add24(18) === 42); // => false
console.log(add18(24) === add24(18)); // => false
console.log(add18(24) === 2018); // => false
console.log(add24(18) === 2018); // => false
console.log(add18(24) === 1033); // => true
console.log(add24(18) === 1030); // => true

Don't let this happen to you. Use a linter.

NOTE: This is a teaching example intended to demonstrate the var/let behavior in loops and with function closures that would also be easy to understand. This would be a terrible way to add numbers. But the general technique of capturing data in anonymous function closures might be encountered in the real world in other contexts. YMMV.

柠檬色的秋千 2024-07-24 02:00:44

该解释摘自我在 Medium 上写的文章:

提升是一种 JavaScript 机制,其中变量和函数
声明被解析器移动到其范围的顶部
在之前将源代码读入中间表示
实际的代码执行由 JavaScript 解释器开始。 所以,实际上
无论变量或函数在哪里声明,它们都会
移动到其范围的顶部,无论其范围是否为
全球或本地。 这意味着

console.log (嗨);      
  var hi = "打个招呼"; 
  

实际上被解释为

var hi = 未定义; 
  控制台.log(嗨); 
  hi =“打个招呼”; 
  

因此,正如我们刚才所看到的,var 变量被提升到顶部
其范围并正在使用 undefined 值进行初始化
这意味着我们可以在实际之前分配它们的值
在代码中声明它们,如下所示:

hi = “打个招呼” 
  控制台.log(嗨);   // 打招呼 
  瓦尔嗨; 
  

关于函数声明,我们可以在实际声明它们之前调用它们,如下所示:

sayHi();   // 你好 

  函数 sayHi() { 
     console.log('嗨'); 
  }; 
  

另一方面,函数表达式不会被提升,因此我们会得到以下错误:

sayHi();   //输出:“TypeError:sayHi 不是函数 

  var sayHi = 函数() { 
    console.log('嗨'); 
  };  
  

ES6 向 JavaScript 开发人员引入了 letconst 关键字。 而 letconst 是块作用域的而不是函数
范围为 var 在讨论它们时,它不应该产生影响
吊装行为。 我们将从最后开始,JavaScript 提升 let
const

console.log(hi);   // 输出:初始化之前无法访问“hi”  
  让 hi = '嗨'; 
  

正如我们在上面看到的,let 不允许我们使用未声明的
变量,因此解释器显式输出引用错误
表示之前无法访问 hi 变量
初始化。 如果我们改变上面的let也会出现同样的错误
常量

console.log(hi);   // 输出:初始化之前无法访问“hi” 
  const hi = '嗨'; 
  

所以,最重要的是,JavaScript 解析器搜索变量
声明和函数并将它们提升到其作用域的顶部
在代码执行之前并在内存中为它们赋值
如果解释器在执行代码时会遇到它们
将识别它们并能够用它们执行代码
指定的值。 使用 letconst 声明的变量保留
在执行开始时未初始化,而该变量
使用 var 声明的值将被初始化为 undefined

我添加了这个视觉插图,以更好地帮助理解如何吊装
变量和函数被保存在内存中 输入图片 
  此处描述

The explanation is taken from the article I wrote at Medium:

Hoisting is a JavaScript mechanism where variables and function
declarations are moved to the top of their scope by the parser which
reads the source code into an intermediate representation before the
actual code execution starts by the JavaScript interpreter. So, it actually
doesn’t matter where variables or functions are declared, they will be
moved to the top of their scope regardless of whether their scope is
global or local. This means that

console.log (hi);     
var hi = "say hi";

is actually interpreted to

var hi = undefined;
console.log (hi);
hi = "say hi";

So, as we saw just now, var variables are being hoisted to the top
of their scope and are being initialized with the value of undefined
which means that we can actually assign their value before actually
declaring them in the code like so:

hi = “say hi”
console.log (hi); // say hi
var hi;

Regarding function declarations, we can invoke them before actually declaring them like so:

sayHi(); // Hi

function sayHi() {
   console.log('Hi');
};

Function expressions, on the other hand, are not hoisted, so we’ll get the following error:

sayHi(); //Output: "TypeError: sayHi is not a function

var sayHi = function() {
  console.log('Hi');
}; 

ES6 introduced JavaScript developers the let and const keywords. While let and const are block-scoped and not function
scoped as var it shouldn’t make a difference while discussing their
hoisting behavior. We’ll start from the end, JavaScript hoists let
and const.

console.log(hi); // Output: Cannot access 'hi' before initialization 
let hi = 'Hi';

As we can see above, let doesn’t allow us to use undeclared
variables, hence the interpreter explicitly output a reference error
indicating that the hi variable cannot be accessed before
initialization. The same error will occur if we change the above let
to const

console.log(hi); // Output: Cannot access 'hi' before initialization
const hi = 'Hi';

So, bottom line, the JavaScript parser searches for variable
declarations and functions and hoists them to the top of their scope
before code execution and assign values to them in the memory so in
case the interpreter will encounter them while executing the code he
will recognize them and will be able to execute the code with their
assigned values. Variables declared with let or const remain
uninitialized at the beginning of execution while that variables
declared with var are being initialized with a value of undefined.

I added this visual illustration to better help understanding of how are the hoisted
variables and function are being saved in the memory enter image
description here

千笙结 2024-07-24 02:00:44

函数 VS 块作用域:

varlet 之间的主要区别在于,用 var 声明的变量是函数作用域。 而用 let 声明的函数是块作用域。 例如:

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

变量与 var

当第一个函数 testVar 被调用时,变量 foo 是用 var 声明的,仍然可以在 if 语句之外访问。 此变量 footestVar 函数 的范围内任何地方都可用。

带有 let 的变量:

当第二个函数 testLet 被调用时,用 let 声明的变量 bar 只能被访问在 if 语句内。 因为用 let 声明的变量是块作用域(其中块是大括号之间的代码,例如 if{}for{} 函数{})。

let 变量不会被提升:

varlet 之间的另一个区别是用 let 声明的变量 不要被吊起。 一个例子是说明这种行为的最佳方式:

带有 let dont 的变量被提升:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

带有 var do 被提升:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

全局 let 不会附加到 window

在全局作用域中使用 let 声明的变量(即以下代码)不在函数中)不会作为属性添加到全局 window 对象上。 例如(此代码在全局范围内):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object

什么时候应该使用let而不是var

使用 let 而不是 var因为它的范围更具体。 这减少了处理大量变量时可能发生的潜在命名冲突。 当您希望显式地将全局变量放在 window 对象上时,可以使用 var(如果确实有必要,请务必仔细考虑)。

Function VS block scope:

The main difference between var and let is that variables declared with var are function scoped. Whereas functions declared with let are block scoped. For example:

function testVar () {
  if(true) {
    var foo = 'foo';
  }

  console.log(foo);
}

testVar();  
// logs 'foo'


function testLet () {
  if(true) {
    let bar = 'bar';
  }

  console.log(bar);
}

testLet(); 
// reference error
// bar is scoped to the block of the if statement 

variables with var:

When the first function testVar gets called the variable foo, declared with var, is still accessible outside the if statement. This variable foo would be available everywhere within the scope of the testVar function.

variables with let:

When the second function testLet gets called the variable bar, declared with let, is only accessible inside the if statement. Because variables declared with let are block scoped (where a block is the code between curly brackets e.g if{} , for{}, function{}).

let variables don't get hoisted:

Another difference between var and let is variables with declared with let don't get hoisted. An example is the best way to illustrate this behavior:

variables with let don't get hoisted:

console.log(letVar);

let letVar = 10;
// referenceError, the variable doesn't get hoisted

variables with var do get hoisted:

console.log(varVar);

var varVar = 10;
// logs undefined, the variable gets hoisted

Global let doesn't get attached to window:

A variable declared with let in the global scope (which is code that is not in a function) doesn't get added as a property on the global window object. For example (this code is in global scope):

var bar = 5;
let foo  = 10;

console.log(bar); // logs 5
console.log(foo); // logs 10

console.log(window.bar);  
// logs 5, variable added to window object

console.log(window.foo);
// logs undefined, variable not added to window object

When should let be used over var?

Use let over var whenever you can because it is simply scoped more specific. This reduces potential naming conflicts which can occur when dealing with a large number of variables. var can be used when you want a global variable explicitly to be on the window object (always consider carefully if this is really necessary).

为你拒绝所有暧昧 2024-07-24 02:00:44

ES6 引入了两个新关键字(letconst)来替代 var

当您需要块级减速时,您可以使用 let 和 const 而不是 var。

下表总结了 var、let 和 const

在此处输入图像描述

ES6 introduced two new keyword(let and const) alternate to var.

When you need a block level deceleration you can go with let and const instead of var.

The below table summarize the difference between var, let and const

enter image description here

帅气称霸 2024-07-24 02:00:44

let 很有趣,因为它允许我们做这样的事情:

(() => {
    var count = 0;

    for (let i = 0; i < 2; ++i) {
        for (let i = 0; i < 2; ++i) {
            for (let i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();
.as-console-wrapper { max-height: 100% !important; }

结果计数为 [0, 7]。

然而

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

只计算 [0, 1]。

let is interesting, because it allows us to do something like this:

(() => {
    var count = 0;

    for (let i = 0; i < 2; ++i) {
        for (let i = 0; i < 2; ++i) {
            for (let i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();
.as-console-wrapper { max-height: 100% !important; }

Which results in counting [0, 7].

Whereas

(() => {
    var count = 0;

    for (var i = 0; i < 2; ++i) {
        for (var i = 0; i < 2; ++i) {
            for (var i = 0; i < 2; ++i) {
                console.log(count++);
            }
        }
    }
})();

Only counts [0, 1].

吻风 2024-07-24 02:00:44

至少在 Visual Studio 2015、TypeScript 1.5 中,“var”似乎允许在一个块中多次声明相同的变量名,而“let”则不允许。

这不会产生编译错误:

var x = 1;
var x = 2;

这将:

let x = 1;
let x = 2;

It also appears that, at least in Visual Studio 2015, TypeScript 1.5, "var" allows multiple declarations of the same variable name in a block, and "let" doesn't.

This won't generate a compile error:

var x = 1;
var x = 2;

This will:

let x = 1;
let x = 2;
策马西风 2024-07-24 02:00:44
var   --> Function scope  
let   --> Block scope
const --> Block scope

var

在此代码示例中,变量 i 使用 var 声明。 因此,它有一个函数范围。 这意味着您只能从function x内部访问i。 您无法从 function x 外部读取它

function x(){
  var i = 100;
  console.log(i); // 100
}
 
console.log(i); // Error. You can't do this

x();

在此示例中,您可以看到 i 是在 if 块内声明的。 但它是使用 var 声明的。 因此,它获得了函数作用域。 这意味着您仍然可以访问function x内的变量i。 因为 var 的作用域始终是函数。 尽管变量 i 是在 if 块内声明的,但由于它使用 var,它的作用域为父 function x

function x(){
  if(true){
    var i = 100;
  }
  console.log(i); 
}

x();

现在变量i函数y内部声明。 因此,i 的作用域为函数 y。 您可以在function y 中访问i。 但不是来自function y外部。

function x(){
  function y(){
    var i = 100;
    console.log(i);
  }
  
  y();
}

x();

function x(){
  function y(){
    var i = 100;
  }
  console.log(i); // ERROR
}

x();

let、const

let 和 const 具有块作用域。

constlet 行为相同。 但不同的是,当你给 const 赋值时,你不能重新赋值。 但您可以使用 let 重新分配值。

在此示例中,变量 iif 块内声明。 因此只能从 if 块内部访问它。 我们无法从 if 块外部访问它。 (这里 constlet 的工作方式相同)

if(true){
  let i = 100;
  console.log(i); // Output: 100
}

console.log(i); // Error

function x(){
  if(true){
    let i = 100;
    console.log(i); // Output: 100
  }
  console.log(i); // Error
}

x();

(let, const)var 的另一个区别是您可以在声明之前访问 var 定义的变量。 它会给你未定义。 但是,如果您使用 letconst 定义的变量来执行此操作,则会出现错误。

console.log(x);
var x = 100;

console.log(x); // ERROR
let x = 100;

var   --> Function scope  
let   --> Block scope
const --> Block scope

var

In this code sample, variable i is declared using var. Therefore, it has a function scope. It means you can access i from only inside the function x. You can't read it from outside the function x

function x(){
  var i = 100;
  console.log(i); // 100
}
 
console.log(i); // Error. You can't do this

x();

In this sample, you can see i is declared inside a if block. But it's declared using var. Therefore, it gets function scope. It means still you can access variable i inside function x. Because var always get scoped to functions. Even though variable i is declared inside if block, because of it's using var it get scoped to parent function x.

function x(){
  if(true){
    var i = 100;
  }
  console.log(i); 
}

x();

Now variable i is declared inside the function y. Therefore, i scoped to function y. You can access i inside function y. But not from outside function y.

function x(){
  function y(){
    var i = 100;
    console.log(i);
  }
  
  y();
}

x();

function x(){
  function y(){
    var i = 100;
  }
  console.log(i); // ERROR
}

x();

let, const

let and const has block scope.

const and let behave same. But the difference is, when you assign value to const you can't re-assign. But you can re-assign values with let.

In this example, variable i is declared inside an if block. So it can be only accessed from inside that if block. We can't access it from outside that if block. (here const work same as let)

if(true){
  let i = 100;
  console.log(i); // Output: 100
}

console.log(i); // Error

function x(){
  if(true){
    let i = 100;
    console.log(i); // Output: 100
  }
  console.log(i); // Error
}

x();

Another difference with (let, const) vs var is you can access var defined variable before declaring it. It will give you undefined. But if you do that with let or const defined variable it will give you an error.

console.log(x);
var x = 100;

console.log(x); // ERROR
let x = 100;

独孤求败 2024-07-24 02:00:44

var 是全局范围(可提升)变量。

letconst 是块作用域。

测试.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined

var is global scope (hoist-able) variable.

let and const is block scope.

test.js

{
    let l = 'let';
    const c = 'const';
    var v = 'var';
    v2 = 'var 2';
}

console.log(v, this.v);
console.log(v2, this.v2);
console.log(l); // ReferenceError: l is not defined
console.log(c); // ReferenceError: c is not defined

软甜啾 2024-07-24 02:00:44

如果我正确阅读了规范,那么 let 谢天谢地 也可以用来避免 自调用函数用于模拟仅私有成员 - 一种流行的设计模式,会降低代码可读性,使调试复杂化,这没有增加真正的代码保护或其他好处 - 除了可能满足某人对语义的渴望,所以停止使用它。 /rant

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

请参阅“模拟私有接口'

If I read the specs right then let thankfully can also be leveraged to avoid self invoking functions used to simulate private only members - a popular design pattern that decreases code readability, complicates debugging, that adds no real code protection or other benefit - except maybe satisfying someone's desire for semantics, so stop using it. /rant

var SomeConstructor;

{
    let privateScope = {};

    SomeConstructor = function SomeConstructor () {
        this.someProperty = "foo";
        privateScope.hiddenProperty = "bar";
    }

    SomeConstructor.prototype.showPublic = function () {
        console.log(this.someProperty); // foo
    }

    SomeConstructor.prototype.showPrivate = function () {
        console.log(privateScope.hiddenProperty); // bar
    }

}

var myInstance = new SomeConstructor();

myInstance.showPublic();
myInstance.showPrivate();

console.log(privateScope.hiddenProperty); // error

See 'Emulating private interfaces'

勿挽旧人 2024-07-24 02:00:44

使用 let

let 关键字将变量声明附加到任何块的范围(通常是 { .. }pair) 它包含在其中。换句话说,let 隐式劫持任何块的变量声明范围。

let 变量无法在 window 对象中访问,因为它们无法全局访问。

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

当使用 var

var 和 ES5 中的变量在函数中具有作用域,这意味着变量在函数内有效,而不是在函数本身之外有效。

var 变量可以在 window 对象中访问,因为它们不能全局访问。

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

如果您想了解更多信息,请继续阅读下面

关于范围的最著名的面试问题之一也足以满足 letvar 的确切使用:以下;

使用let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

这是因为使用let时,对于每个循环迭代,变量都有作用域并拥有自己的副本。

使用var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

这是因为使用var时,对于每个循环迭代,变量都有作用域并具有共享副本。

When Using let

The let keyword attaches the variable declaration to the scope of whatever block (commonly a { .. } pair) it's contained in. In other words,let implicitly hijacks any block's scope for its variable declaration.

let variables cannot be accessed in the window object because they cannot be globally accessed.

function a(){
    { // this is the Max Scope for let variable
        let x = 12;
    }
    console.log(x);
}
a(); // Uncaught ReferenceError: x is not defined

When Using var

var and variables in ES5 has scopes in functions meaning the variables are valid within the function and not outside the function itself.

var variables can be accessed in the window object because they cannot be globally accessed.

function a(){ // this is the Max Scope for var variable
    { 
        var x = 12;
    }
    console.log(x);
}
a(); // 12

If you want to know more continue reading below

one of the most famous interview questions on scope also can suffice the exact use of let and var as below;

When using let

for (let i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 0 to 9, that is literally AWW!!!
        }, 
        100 * i);
}

This is because when using let, for every loop iteration the variable is scoped and has its own copy.

When using var

for (var i = 0; i < 10 ; i++) {
    setTimeout(
        function a() {
            console.log(i); //print 10 times 10
        }, 
        100 * i);
}

This is because when using var, for every loop iteration the variable is scoped and has shared copy.

甜警司 2024-07-24 02:00:44

使用 let 的一些技巧:

1.

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2.

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3.

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

使用 let 进行 getter 和 setter:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)

Some hacks with let:

1.

    let statistics = [16, 170, 10];
    let [age, height, grade] = statistics;

    console.log(height)

2.

    let x = 120,
    y = 12;
    [x, y] = [y, x];
    console.log(`x: ${x} y: ${y}`);

3.

    let node = {
                   type: "Identifier",
                   name: "foo"
               };

    let { type, name, value } = node;

    console.log(type);      // "Identifier"
    console.log(name);      // "foo"
    console.log(value);     // undefined

    let node = {
        type: "Identifier"
    };

    let { type: localType, name: localName = "bar" } = node;

    console.log(localType);     // "Identifier"
    console.log(localName);     // "bar"

Getter and setter with let:

let jar = {
    numberOfCookies: 10,
    get cookies() {
        return this.numberOfCookies;
    },
    set cookies(value) {
        this.numberOfCookies = value;
    }
};

console.log(jar.cookies)
jar.cookies = 7;

console.log(jar.cookies)
酸甜透明夹心 2024-07-24 02:00:44

下面显示了 'let' 和 'var' 在作用域中的不同之处:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

gfoo 最初由 let 定义,位于全局作用域 ,当我们在 if 子句 中再次声明 gfoo 时,它的范围发生了变化,并且当一个新值被分配给该范围内的变量不会影响全局范围。

而由 var 定义的 hfoo 最初位于全局范围,但当我们在 if 子句,它考虑全局范围 hfoo,尽管再次使用 var 来声明它。 当我们重新分配它的值时,我们发现全局范围 hfoo 也受到影响。 这是主要的区别。

The below shows how 'let' and 'var' are different in the scope:

let gfoo = 123;
if (true) {
    let gfoo = 456;
}
console.log(gfoo); // 123

var hfoo = 123;
if (true) {
    var hfoo = 456;
}
console.log(hfoo); // 456

The gfoo, defined by let initially is in the global scope, and when we declare gfoo again inside the if clause its scope changed and when a new value is assigned to the variable inside that scope it does not affect the global scope.

Whereas hfoo, defined by var is initially in the global scope, but again when we declare it inside the if clause, it considers the global scope hfoo, although var has been used again to declare it. And when we re-assign its value we see that the global scope hfoo is also affected. This is the primary difference.

小巷里的女流氓 2024-07-24 02:00:44

我刚刚遇到一个用例,我必须使用 var 而不是 let 来引入新变量。 这是一个案例:

我想创建一个具有动态变量名称的新变量。

let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a);   // this doesn't work
var variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a);   // this works

上面的代码不起作用,因为 eval 引入了一个新的代码块。 使用 var 声明将在该代码块之外声明一个变量,因为 var 在函数作用域中声明一个变量。

另一方面,let 在块作用域中声明变量。 因此,a 变量仅在 eval 块中可见。

I just came across one use case that I had to use var over let to introduce new variable. Here's a case:

I want to create a new variable with dynamic variable names.

let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a);   // this doesn't work
var variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a);   // this works

The above code doesn't work because eval introduces a new block of code. The declaration using var will declare a variable outside of this block of code since var declares a variable in the function scope.

let, on the other hand, declares a variable in a block scope. So, a variable will only be visible in eval block.

滥情空心 2024-07-24 02:00:44

let 与 var. 这一切都与范围有关。

var 变量是全局变量并且基本上可以在任何地方访问,而let 变量不是全局变量并且仅存在直到右括号杀死它们为止。

请参阅下面的示例,并注意 lion (let) 变量在两个 console.log 中的不同行为; 它超出了第二个 console.log 的范围。

var cat = "cat";
let dog = "dog";

var animals = () => {
  var giraffe = "giraffe";
  let lion = "lion";

  console.log(cat); //will print 'cat'.
  console.log(dog); //will print 'dog', because dog was declared outside this function (like var cat).

  console.log(giraffe); //will print 'giraffe'.
  console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.

let vs var. It's all about scope.

var variables are global and can be accessed basically everywhere, while let variables are not global and only exist until a closing parenthesis kills them.

See my example below, and note how the lion (let) variable acts differently in the two console.logs; it becomes out of scope in the 2nd console.log.

var cat = "cat";
let dog = "dog";

var animals = () => {
  var giraffe = "giraffe";
  let lion = "lion";

  console.log(cat); //will print 'cat'.
  console.log(dog); //will print 'dog', because dog was declared outside this function (like var cat).

  console.log(giraffe); //will print 'giraffe'.
  console.log(lion); //will print 'lion', as lion is within scope.
}

console.log(giraffe); //will print 'giraffe', as giraffe is a global variable (var).
console.log(lion); //will print UNDEFINED, as lion is a 'let' variable and is now out of scope.

软甜啾 2024-07-24 02:00:44

现在我认为使用 let 对语句块有更好的变量作用域:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

我认为人们会开始在此处使用 let,这样他们就会像其他语言、Java、C# 一样在 JavaScript 中具有类似的作用域 对 JavaScript 中的作用域没有清晰了解的人

过去常常犯这样的错误。

使用 let 不支持提升。

通过这种方法,JavaScript 中存在的错误将被消除。

请参阅ES6 深入了解:let 和const 以便更好地理解它。

Now I think there is better scoping of variables to a block of statements using let:

function printnums()
{
    // i is not accessible here
    for(let i = 0; i <10; i+=)
    {
       console.log(i);
    }
    // i is not accessible here

    // j is accessible here
    for(var j = 0; j <10; j++)
    {
       console.log(j);
    }
    // j is accessible here
}

I think people will start using let here after so that they will have similar scoping in JavaScript like other languages, Java, C#, etc.

People with not a clear understanding about scoping in JavaScript used to make the mistake earlier.

Hoisting is not supported using let.

With this approach errors present in JavaScript are getting removed.

Refer to ES6 In Depth: let and const to understand it better.

留一抹残留的笑 2024-07-24 02:00:44

正如刚才提到的:

区别在于范围。 var 的作用域为最近的函数
block
let 的作用域为最近的封闭块,其中
可以小于功能块。 如果在任何外部的话,两者都是全局的
块。让我们看一个例子:

示例1:

在我的两个示例中,我都有一个函数myfuncmyfunc 包含一个等于 10 的变量 myvar
在第一个示例中,我检查 myvar 是否等于 10 (myvar==10) 。 如果是,我再次使用 var 关键字声明一个变量 myvar (现在我有两个 myvar 变量)并为其分配一个新值 (20)。 在下一行中,我在控制台上打印它的值。 在条件块之后,我再次在控制台上打印 myvar 的值。 如果您查看 myfunc 的输出,myvar 的值等于 20。

let 关键字

示例2:
在我的第二个示例中,我没有在条件块中使用 var 关键字,而是使用 let 关键字声明 myvar 。 现在,当我调用 myfunc 时,我会得到两个不同的输出:myvar=20myvar=10

所以区别很简单,即范围。

As mentioned above:

The difference is scoping. var is scoped to the nearest function
block
and let is scoped to the nearest enclosing block, which
can be smaller than a function block. Both are global if outside any
block.Lets see an example:

Example1:

In my both examples I have a function myfunc. myfunc contains a variable myvar equals to 10.
In my first example I check if myvar equals to 10 (myvar==10) . If yes, I agian declare a variable myvar (now I have two myvar variables)using var keyword and assign it a new value (20). In next line I print its value on my console. After the conditional block I again print the value of myvar on my console. If you look at the output of myfunc, myvar has value equals to 20.

let keyword

Example2:
In my second example instead of using var keyword in my conditional block I declare myvar using let keyword . Now when I call myfunc I get two different outputs: myvar=20 and myvar=10.

So the difference is very simple i.e its scope.

苏辞 2024-07-24 02:00:44

我想将这些关键字链接到执行上下文,因为执行上下文在所有这一切中都很重要。 执行上下文有两个阶段:创建阶段和执行阶段。 此外,每个执行上下文都有一个变量环境和外部环境(其词法环境)。

在执行上下文的创建阶段,var、let 和 const 仍会将其变量存储在内存中,并在给定执行上下文的变量环境中使用未定义的值。 区别在于执行阶段。 如果在赋值之前使用引用 var 定义的变量,则该变量将是未定义的。 不会出现任何异常。

但是,在声明之前不能引用用 let 或 const 声明的变量。 如果您尝试在声明之前使用它,则在执行上下文的执行阶段将引发异常。 现在,由于执行上下文的创建阶段,该变量仍将位于内存中,但引擎将不允许您使用它:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

使用 var 定义的变量,如果引擎在当前执行上下文的变量中找不到该变量环境,然后它将沿着作用域链(外部环境)向上查找该变量的外部环境的变量环境。 如果在那里找不到它,它将继续搜索作用域链。 let 和 const 的情况并非如此。

let 的第二个特性是它引入了块作用域。 块由花括号定义。 示例包括函数块、if 块、for 块等。当您在块内部使用 let 声明变量时,该变量仅在块内部可用。 事实上,每次运行该块时(例如在 for 循环中),它都会在内存中创建一个新变量。

ES6还引入了const关键字来声明变量。 const 也是块作用域的。 let 和 const 之间的区别在于 const 变量需要使用初始值设定项来声明,否则会产生错误。

最后,当涉及到执行上下文时,用 var 定义的变量将附加到“this”对象。 在全局执行上下文中,这将是浏览器中的窗口对象。 let 或 const 则不是这种情况。

I want to link these keywords to the Execution Context, because the Execution Context is important in all of this. The Execution Context has two phases: a Creation Phase and Execution Phase. In addition, each Execution Context has a Variable Environment and Outer Environment (its Lexical Environment).

During the Creation Phase of an Execution Context, var, let and const will still store its variable in memory with an undefined value in the Variable Environment of the given Execution Context. The difference is in the Execution Phase. If you use reference a variable defined with var before it is assigned a value, it will just be undefined. No exception will be raised.

However, you cannot reference the variable declared with let or const until it is declared. If you try to use it before it is declared, then an exception will be raised during the Execution Phase of the Execution Context. Now the variable will still be in memory, courtesy of the Creation Phase of the Execution Context, but the Engine will not allow you to use it:

function a(){
    b;
    let b;
}
a();
> Uncaught ReferenceError: b is not defined

With a variable defined with var, if the Engine cannot find the variable in the current Execution Context's Variable Environment, then it will go up the scope chain (the Outer Environment) and check the Outer Environment's Variable Environment for the variable. If it cannot find it there, it will continue searching the Scope Chain. This is not the case with let and const.

The second feature of let is it introduces block scope. Blocks are defined by curly braces. Examples include function blocks, if blocks, for blocks, etc. When you declare a variable with let inside of a block, the variable is only available inside of the block. In fact, each time the block is run, such as within a for loop, it will create a new variable in memory.

ES6 also introduces the const keyword for declaring variables. const is also block scoped. The difference between let and const is that const variables need to be declared using an initializer, or it will generate an error.

And, finally, when it comes to the Execution Context, variables defined with var will be attached to the 'this' object. In the global Execution Context, that will be the window object in browsers. This is not the case for let or const.

巴黎盛开的樱花 2024-07-24 02:00:44

由于我目前正在尝试深入了解 JavaScript,因此我将分享我的简短研究,其中包含一些已经讨论过的精彩文章以及不同角度的其他一些细节。

如果我们了解函数块作用域之间的区别,那么理解varlet之间的区别会更容易。

让我们考虑以下情况:

(function timer() {
    for(var i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();


   Stack            VariableEnvironment //one VariablEnvironment for timer();
                                       // when the timer is out - the value will be the same for each iteration
5. [setTimeout, i]  [i=5] 
4. [setTimeout, i]  
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]

####################    

(function timer() {
    for (let i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();

   Stack           LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i]  [i=5]       
                      LexicalEnvironment 
4. [setTimeout, i]    [i=4]     
                        LexicalEnvironment 
3. [setTimeout, i]      [i=3]       
                         LexicalEnvironment 
2. [setTimeout, i]       [i=2]
                           LexicalEnvironment 
1. [setTimeout, i]         [i=1]
                             LexicalEnvironment 
0. [setTimeout, i]           [i=0]

当调用 timer() 时,会创建一个 ExecutionContext,其中包含 VariableEnvironment 和所有 LexicalEnvironments 对应于每次迭代。

一个更简单的例子

函数作用域

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

块作用域

function test() {
    for(let z = 0; z < 69; z++) {
        //todo
    }
    //z is not defined :(
}

简而言之,let 和 var 之间的区别在于 var 是函数作用域,而 let 是块作用域。

As I am currently trying to get an in depth understanding of JavaScript I will share my brief research which contains some of the great pieces already discussed plus some other details in a different perspective.

Understanding the difference between var and let can be easier if we understand the difference between function and block scope.

Let's consider the following cases:

(function timer() {
    for(var i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();


   Stack            VariableEnvironment //one VariablEnvironment for timer();
                                       // when the timer is out - the value will be the same for each iteration
5. [setTimeout, i]  [i=5] 
4. [setTimeout, i]  
3. [setTimeout, i]
2. [setTimeout, i]
1. [setTimeout, i]
0. [setTimeout, i]

####################    

(function timer() {
    for (let i = 0; i <= 5; i++) {
        setTimeout(function notime() { console.log(i); }, i * 1000);
    }
})();

   Stack           LexicalEnvironment - each iteration has a new lexical environment
5. [setTimeout, i]  [i=5]       
                      LexicalEnvironment 
4. [setTimeout, i]    [i=4]     
                        LexicalEnvironment 
3. [setTimeout, i]      [i=3]       
                         LexicalEnvironment 
2. [setTimeout, i]       [i=2]
                           LexicalEnvironment 
1. [setTimeout, i]         [i=1]
                             LexicalEnvironment 
0. [setTimeout, i]           [i=0]

when timer() gets called an ExecutionContext is created which will contain both the VariableEnvironment and all the LexicalEnvironments corresponding to each iteration.

And a simpler example

Function Scope

function test() {
    for(var z = 0; z < 69; z++) {
        //todo
    }
    //z is visible outside the loop
}

Block Scope

function test() {
    for(let z = 0; z < 69; z++) {
        //todo
    }
    //z is not defined :(
}

Briefly the difference between let and var is that var is function scoped and let is block scoped.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文