区别在于范围界定。 var
的范围是最近的功能块,let
的范围是最近的封闭块,它可以小于功能块。如果在任何区域之外,两者都是全局
此外,使用let
声明的变量在它们的封闭块中声明之前是不可访问的。如演示中所示,这将引发ReferenceError异常。
var html = '';
write('#### global ####\n');
write('globalVar: ' + globalVar); //undefined, but visible
try {
write('globalLet: ' + globalLet); //undefined, *not* visible
} catch (exception) {
write('globalLet: exception');
}
write('\nset variables');
var globalVar = 'globalVar';
let globalLet = 'globalLet';
write('\nglobalVar: ' + globalVar);
write('globalLet: ' + globalLet);
function functionScoped() {
write('\n#### function ####');
write('\nfunctionVar: ' + functionVar); //undefined, but visible
try {
write('functionLet: ' + functionLet); //undefined, *not* visible
} catch (exception) {
write('functionLet: exception');
}
write('\nset variables');
var functionVar = 'functionVar';
let functionLet = 'functionLet';
write('\nfunctionVar: ' + functionVar);
write('functionLet: ' + functionLet);
}
function blockScoped() {
write('\n#### block ####');
write('\nblockVar: ' + blockVar); //undefined, but visible
try {
write('blockLet: ' + blockLet); //undefined, *not* visible
} catch (exception) {
write('blockLet: exception');
}
for (var blockVar = 'blockVar', blockIndex = 0; blockIndex < 1; blockIndex++) {
write('\nblockVar: ' + blockVar); // visible here and whole function
};
for (let blockLet = 'blockLet', letIndex = 0; letIndex < 1; letIndex++) {
write('blockLet: ' + blockLet); // visible only here
};
write('\nblockVar: ' + blockVar);
try {
write('blockLet: ' + blockLet); //undefined, *not* visible
} catch (exception) {
write('blockLet: exception');
}
}
function write(line) {
html += (line ? line : '') + '<br />';
}
functionScoped();
blockScoped();
document.getElementById('results').innerHTML = html;
<pre id="results"></pre>
在功能块之外使用它们非常相似。
let me = 'go'; // globally scoped
var i = 'able'; // globally scoped
但是,使用let
定义的全局变量不会作为属性添加到全局window
对象上,就像使用var
定义的那样。
console.log(window.me); // undefined
console.log(window.i); // 'able'
在功能块中使用时它们是相同的。
function ingWithinEstablishedParameters() {
let terOfRecommendation = 'awesome worker!'; //function block scoped
var sityCheerleading = 'go!'; //function block scoped
}
这是区别。 let
仅在for()
循环中可见,var
对整个函数可见。
function allyIlliterate() {
//tuce is *not* visible out here
for( let tuce = 0; tuce < 5; tuce++ ) {
//tuce is only visible in here (and in the for() parentheses)
//and there is a separate tuce variable for each iteration of the loop
}
//tuce is *not* visible out here
}
function byE40() {
//nish *is* visible out here
for( var nish = 0; nish < 5; nish++ ) {
//nish is visible to the whole function
}
//nish *is* visible out here
}
假设严格模式,var
将允许您在同一范围内重新声明相同的变量。
'use strict';
var me = 'foo';
var me = 'bar'; // No problem, 'me' is replaced.
另一方面,let
不会:
'use strict';
let me = 'foo';
let me = 'bar'; // SyntaxError: Identifier 'me' has already been declared
主要区别在于范围差异,而let只能在其声明的范围内可用,例如在for循环中,var可以在循环外部访问,例如。来自MDN的文档(也来自MDN的例子):
let允许您将范围有限的变量声明为使用它的块,语句或表达式。这与var关键字不同,var关键字全局定义变量,或者与整个函数本地定义,而不管块范围如何。
let声明的变量的范围是定义它们的块,以及任何包含的子块。这样,让我的工作非常像var。主要区别在于var变量的范围是整个封闭函数:
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
}`
在程序和函数的顶层,与var不同,let不会在全局对象上创建属性。例如:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
在块内使用时,将变量的范围限制为该块。请注意var之间的区别,其范围在声明它的函数内。
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
另外不要忘记它的ECMA6功能,所以它还没有完全支持,所以最好总是使用Babel等将它转换为ECMA5 ...有关访问babel website的更多信息
这是一个添加其他人已经写过的例子。假设您要创建一个函数数组adderFunctions
,其中每个函数都使用一个Number参数,并返回参数和函数索引在数组中的总和。尝试使用adderFunctions
关键字生成带有循环的var
将不会像某人可能天真期望的那样工作:
// 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
上面的过程不会生成所需的函数数组,因为i
的范围超出了创建每个函数的for
块的迭代。相反,在循环结束时,每个函数闭包中的i
指的是i
中每个匿名函数的循环结束时的adderFunctions
值(1000)。这根本不是我们想要的:我们现在在内存中有一个包含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
这一次,i
在for
循环的每次迭代中都会反弹。现在,每个函数都会在函数创建时保持i
的值,并且adderFunctions
的行为与预期的一致。
现在,图像混合这两种行为,你可能会明白为什么不建议在同一个脚本中将较新的let
和const
与较旧的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
行为以及函数闭包,这也很容易理解。这将是添加数字的可怕方式。但是在匿名函数闭包中捕获数据的一般技术可能在其他环境中的现实世界中遇到。因人而异。
区别在于每个声明的变量的scope。
在实践中,范围差异有许多有用的后果:
let
变量仅在最近的封闭区域({ ... }
)中可见。let
变量只能在声明变量后出现的代码行中使用(即使they are hoisted!)。let
变量可能不会被随后的var
或let
重新声明。let
变量未添加到全局window
对象中。let
变量易于使用闭包(它们不会导致race conditions)。let
施加的限制降低了变量的可见性,并增加了早期发现意外名称冲突的可能性。这使得更容易跟踪和推理变量,包括他们的reachability(帮助回收未使用的内存)。
因此,let
变量在大型程序中使用时或者以独立开发的框架以新的和意外的方式组合时不太可能导致问题。
如果在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时确定需要单绑定效果,var
可能仍然有用。如果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
相同的示例解析并运行时没有抛出任何异常。
3.无重新声明:以下代码演示了使用let
声明的变量以后可能不会重新声明:
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
4.没有附加到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.易于使用闭包:使用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*/);
}
...循环意外地输出“我是5”五次:
i is 5
i is 5
i is 5
i is 5
i is 5
可能以下两个函数显示了不同之处:
function varTest() {
var x = 31;
if (true) {
var x = 71; // Same variable!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // Different variable
console.log(x); // 71
}
console.log(x); // 31
}
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++);
}
}
}
})();
这导致计数[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]。
var
和let
之间的主要区别在于用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
被调用时,使用var
声明的变量foo仍然可以在if
语句之外访问。这个变量foo
将在testVar
函数的范围内随处可用。
let
的变量:
当第二个函数testLet
被调用时,使用let
声明的变量bar只能在if
语句中访问。因为使用let
声明的变量是块作用域(其中块是大括号之间的代码,例如if{}
,for{}
,function{}
)。
let
变量不会被提升:var
和let
之间的另一个区别是用let
声明的变量不会被提升。一个示例是说明此行为的最佳方法:
let
的变量不会被挂起:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
var
的变量确实被挂起:
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
,因为它的范围更加具体。这减少了在处理大量变量时可能发生的潜在命名冲突。当您希望明确地将全局变量放在var
对象上时,可以使用window
(如果确实需要,请务必仔细考虑)。
看来,至少在Visual Studio 2015中,TypeScript 1.5中,“var”允许在块中对同一个变量名进行多次声明,而“let”则不允许。
这不会产生编译错误:
var x = 1;
var x = 2;
这将:
let x = 1;
let x = 2;
var
是全球范围(可提升)变量。
let
和const
是块范围。
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
使用let
时
let
关键字将变量声明附加到它所包含的任何块(通常是{ .. }
对)的范围内。换句话说,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
如果您想了解更多,请继续阅读以下内容
关于范围的最着名的访谈问题之一也足以满足let
和var
的确切用法如下;
使用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
时,对于每个循环迭代,变量都是作用域并具有共享副本。
如果我正确阅读规范,那么let
还可以利用它来避免用于模拟私有成员的self invoking functions - 一种流行的设计模式,降低代码可读性,使调试变得复杂,不会增加真正的代码保护或其他好处 - 除了可能满足某人的愿望对于语义,所以停止使用它。 /咆哮
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
let
也可用于避免闭包问题。它绑定了新的价值而不是保留旧的参考,如下面的例子所示。
for(var i = 1; i < 6; i++) {
document.getElementById('my-element' + i)
.addEventListener('click', function() { alert(i) })
}
上面的代码演示了一个典型的JavaScript闭包问题对i
变量的引用存储在click处理程序闭包中,而不是i
的实际值。
每个单击处理程序都将引用同一个对象,因为只有一个计数器对象可以容纳6,因此每次单击时会得到6个。
一般的解决方法是将其包装在匿名函数中并将i
作为参数传递。现在也可以通过使用let
而不是var
来避免这些问题,如下面的代码所示。
Demo(在Chrome和Firefox 50中测试过)
'use strict';
for(let i = 1; i < 6; i++) {
document.getElementById('my-element' + i)
.addEventListener('click', function() { alert(i) })
}
一些hazz与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
: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)
让vs var。这都是关于范围的。
var变量是全局的,基本上可以在任何地方访问,而变量不是全局的,只有在右括号杀死变量时才存在。
请参阅下面的示例,并注意lion(let)变量在两个console.logs中的行为方式不同;它在第二个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是es6的一部分。这些功能将以简单的方式解释差异。
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
}
以前在JavaScript中只有两个范围,即功能范围和全局范围。使用'let
'关键字JavaScript现在已经引入了block-level
变量。
要完全理解'let'关键字, ES6: ‘let’ keyword to declare variable in JavaScript会有所帮助。
现在我认为使用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,以便他们在JavaScript中具有类似的范围,如其他语言,Java,C#等。
对JavaScript中的范围界定不清楚的人过去常犯错误。
使用let
不支持吊装。
使用此方法,JavaScript中出现的错误将被删除。
请参阅ES6 In Depth: let and const以更好地理解它。
本文明确定义了var,let和const之间的区别
const
是一个信号,标识符不会被重新分配。
let
,是一个可以重新分配变量的信号,例如循环中的计数器,或算法中的值交换。它还表示该变量仅在其定义的块中使用,并不总是包含整个函数。
var
现在是您在JavaScript中定义变量时可用的最弱信号。变量可以重新分配,也可以不重新分配,并且变量可以用于或不用于整个函数,或仅用于块或循环的目的。
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75#.esmkpbg9b
正如刚才提到的:
区别在于范围界定。
var
的范围是最近的功能块,let
的范围是最近的封闭块,它可以小于功能块。如果在任何区块之外,两者都是全局的。让我们看一个例子:
例1:
在我的两个例子中,我有一个函数myfunc
。 myfunc
包含一个变量myvar
等于10.在我的第一个例子中,我检查myvar
是否等于10(myvar==10
)。如果是,我使用myvar
关键字声明变量var
(现在我有两个myvar变量),并为其赋值一个新值(20)。在下一行中,我在我的控制台上打印它的值。在条件块后,我再次在我的控制台上打印myvar
的值。如果你看一下myfunc
的输出,myvar
的值等于20。
示例2:在我的第二个示例中,不是在条件块中使用var
关键字,而是使用myvar
关键字声明let
。现在,当我打电话给myfunc
时,我得到了两个不同的输出:myvar=20
和myvar=10
。
所以区别很简单,即范围。
我认为这些术语和大多数例子都有点压倒性,我个人认识到的主要问题是理解“块”是什么。在某些时候我意识到,除了IF
语句之外,一个块将是任何大括号。函数或循环的开括号{
将定义一个新块,在其中使用let
定义的任何东西,在相同的东西(函数或循环)的右括号}
之后将不可用;考虑到这一点,它更容易理解:
let msg = "Hello World";
function doWork() { // msg will be available since it was defined above this opening bracket!
let friends = 0;
console.log(msg);
// with VAR though:
for (var iCount2 = 0; iCount2 < 5; iCount2++) {} // iCount2 will be available after this closing bracket!
console.log(iCount2);
for (let iCount1 = 0; iCount1 < 5; iCount1++) {} // iCount1 will not be available behind this closing bracket, it will return undefined
console.log(iCount1);
} // friends will no be available after this closing bracket!
doWork();
console.log(friends);
由于我目前正在努力深入了解JavaScript,因此我将分享我的简短研究,其中包含已经讨论过的一些重要内容以及其他一些不同视角的细节。
如果我们理解函数和块范围之间的区别,那么理解var和let之间的区别会更容易。
让我们考虑以下情况:
(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 value for each call
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
and var
?var
语句定义的变量在the function中是已知的,它从函数的开头定义。*let
语句定义的变量仅在the block中已知,它从定义的时刻开始定义。**要了解其中的差异,请考虑以下代码:
// 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
today?有些人会争辩说,将来我们只会使用let语句,而var语句将会过时。 JavaScript大师Kyle Simpson写了a very elaborate article on why he believes that won't be the case。
然而,今天绝对不是这样。事实上,我们实际上需要问自己,使用let
语句是否安全。这个问题的答案取决于您的环境:
let
语句。let
语句,但是您的代码在性能方面可能不是最优的。let
语句。而且因为您的浏览器只会知道转换后的代码,所以应该限制性能缺陷。let
:有关哪些浏览器在阅读本答案时支持let
语句的最新概述,请参阅this Can I Use
page。
*全局和功能范围的变量可以在声明之前初始化和使用,因为JavaScript变量是hoisted。这意味着声明始终位于范围的顶部。
**不会挂起块范围变量
我想将这些关键字链接到执行上下文,因为执行上下文在所有这些中都很重要。执行上下文有两个阶段:创建阶段和执行阶段。此外,每个执行上下文都有一个可变环境和外部环境(它的词汇环境)。
在执行上下文的创建阶段,var,let和const仍将在给定执行上下文的变量环境中将其变量存储在具有未定义值的内存中。不同之处在于执行阶段。如果使用引用为var定义的变量,则在赋值之前,它将是未定义的。不会有例外。
但是,在声明之前,不能引用用let或const声明的变量。如果在声明之前尝试使用它,则会在执行上下文的执行阶段引发异常。现在变量仍将在内存中,由执行上下文的创建阶段提供,但引擎将不允许您使用它:
function a(){
b;
let b;
}
a();
> Uncaught ReferenceError: b is not defined
对于使用var定义的变量,如果Engine无法在当前执行上下文的变量环境中找到变量,那么它将向上移动作用域链(外部环境)并检查外部环境的变量环境中的变量。如果在那里找不到它,它将继续搜索范围链。 let和const不是这种情况。
let的第二个特性是它引入了块范围。块由花括号定义。示例包括功能块,if块,块等。当您在块内部声明变量时,该变量仅在块内部可用。事实上,每次运行块时,例如在for循环中,它都会在内存中创建一个新变量。
ES6还引入了const关键字来声明变量。 const也是块作用域。 let和const之间的区别在于const变量需要使用初始化程序声明,否则会产生错误。
最后,当涉及执行上下文时,使用var定义的变量将附加到'this'对象。在全局执行上下文中,它将成为浏览器中的窗口对象。 let或const不是这种情况。
这是一个带有一些例子的explanation of the let
keyword。
let
非常像var
。主要区别在于var
变量的范围是整个封闭函数
维基百科上的This table显示哪些浏览器支持Javascript 1.7。
请注意,只有Mozilla和Chrome浏览器支持它。 IE,Safari和其他可能没有。
接受的答案缺少一点:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
let
使用let
关键字声明的变量是块范围的,这意味着它们仅在声明它们的block中可用。
在顶层,使用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
在函数内部(但在块之外),let
与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
在块中使用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);
}
由于the temporal dead zone,使用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
const
与let
非常相似 - 它是块状的并且有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
声明变量时,始终必须指定一个值。
const a; // SyntaxError: Missing initializer in const declaration
以下是两者之间差异的示例(支持刚启动的chrome):
正如您所看到的,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);
有一些微妙的差异 - let
范围表现更像是变量范围确实或多或少任何其他语言。
例如它适用于封闭块,它们在声明之前不存在,等等。
然而值得注意的是,let
只是较新的Javascript实现的一部分,并且具有不同程度的browser support。
let
不会提升到它们出现的整个范围。相比之下,var
可能会如下所示。
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
实际上,Per @Bergi,Both var
and let
are hoisted。let
的块范围与闭包和垃圾收集有关,以回收内存。考虑,
function process(data) {
//...
}
var hugeData = { .. };
process(hugeData);
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
click
处理程序回调根本不需要hugeData
变量。从理论上讲,在process(..)
运行之后,巨大的数据结构hugeData
可能被垃圾收集。但是,有些JS引擎可能仍然需要保留这个庞大的结构,因为click
函数在整个范围内都有一个闭包。
但是,块范围可以使这个庞大的数据结构被垃圾收集。
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
循环
循环中的let
可以将它重新绑定到循环的每次迭代,确保从前一循环迭代结束时重新赋值。考虑,
// print '5' 5 times
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
但是,用var
取代let
// print 1, 2, 3, 4, 5. now
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
因为let
创建了一个新的词汇环境,其中包含a)初始化表达式b)每次迭代(主要用于评估增量表达式),更多细节是here。