TM.*_*TM. 4199 javascript scope var let ecmascript-6
ECMAScript 6介绍了该let声明.我听说它被描述为"本地"变量,但我仍然不太确定它与var关键字的行为有何不同.
有什么区别?何时应该let使用var?
Thi*_*iff 5681
区别在于范围界定.var范围限定为最近的功能块,let并且范围限定为最近的封闭块,该封闭块可以小于功能块.如果在任何区域之外,两者都是全局
此外,声明的变量{ }在它们的封闭块中声明之前是不可访问的.如演示中所示,这将引发ReferenceError异常.
演示:
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar);
{
let baz = "Bazz";
console.log(baz);
}
console.log(baz); // ReferenceError
}
run();
Run Code Online (Sandbox Code Playgroud)
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]();
}
Run Code Online (Sandbox Code Playgroud)
在功能块之外使用它们非常相似.
function run() {
console.log(foo); // undefined
var foo = "Foo";
console.log(foo); // Foo
}
run();
Run Code Online (Sandbox Code Playgroud)
但是,定义的全局变量let不会作为属性添加到全局My value: 3对象上,就像定义的那样funcs[j]();.
function checkHoisting() {
console.log(foo); // ReferenceError
let foo = "Foo";
console.log(foo); // Foo
}
checkHoisting();
Run Code Online (Sandbox Code Playgroud)
在功能块中使用时它们是相同的.
var foo = "Foo"; // globally scoped
let bar = "Bar"; // globally scoped
console.log(window.foo); // Foo
console.log(window.bar); // undefined
Run Code Online (Sandbox Code Playgroud)
这是区别.var只在ReferenceError循环let中可见,并且对整个函数可见.
'use strict';
var foo = "foo1";
var foo = "foo2"; // No problem, 'foo' is replaced.
let bar = "bar1";
let bar = "bar2"; // SyntaxError: Identifier 'bar' has already been declared
Run Code Online (Sandbox Code Playgroud)
假设严格模式,var将允许您在同一范围内重新声明相同的变量.另一方面,var不会:
function run() {
var foo = "Foo";
let bar = "Bar";
console.log(foo, bar);
{
let baz = "Bazz";
console.log(baz);
}
console.log(baz); // ReferenceError
}
run();
Run Code Online (Sandbox Code Playgroud)
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]();
}
Run Code Online (Sandbox Code Playgroud)
Gur*_*ngh 578
let也可用于避免闭包问题.它绑定了新的价值而不是保留旧的参考,如下面的例子所示.
for(var i=1; i<6; i++) {
$("#div" + i).click(function () { console.log(i); });
}Run Code Online (Sandbox Code Playgroud)
上面的代码演示了一个典型的JavaScript闭包问题 对i变量的引用存储在单击处理程序闭包中,而不是实际值i.
每个单击处理程序都会引用同一个对象,因为只有一个计数器对象可以容纳6个,所以每次点击就会得到6个.
一般的解决方法是将它包装在匿名函数中并i作为参数传递.现在也可以通过使用let替代var方法来避免这些问题,如下面的代码所示.
演示(在Chrome和Firefox 50中测试)
<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>Run Code Online (Sandbox Code Playgroud)
Joh*_*ers 163
let和之间有什么区别var?要了解其中的差异,请考虑以下代码:
// 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
Run Code Online (Sandbox Code Playgroud)
在这里,我们可以看到我们的变量j只在第一个for循环中被知道,而不是在之前和之后.然而,我们的变量i在整个函数中是已知的.
另外,请考虑块范围变量在声明之前是未知的,因为它们不会被提升.您也不允许在同一块中重新声明相同的块范围变量.这使得块范围变量比全局或功能范围变量更不容易出错,这些变量被提升并且在多个声明的情况下不会产生任何错误.
let今天使用安全吗?有些人会争辩说,将来我们只会使用let语句,而var语句将会过时.JavaScript大师凯尔辛普森写了一篇非常精细的文章,说明为什么不是这样.
然而,今天绝对不是这样.事实上,我们实际上需要问自己,使用该let陈述是否安全.这个问题的答案取决于您的环境:
如果您正在编写服务器端JavaScript代码(Node.js),则可以安全地使用该let语句.
如果您正在编写客户端JavaScript代码并使用转换器(如Traceur),则可以安全地使用该let语句,但是您的代码在性能方面可能不是最优的.
如果您正在编写客户端JavaScript代码而不使用转换器,则需要考虑浏览器支持.
今天,2018年6月8日,仍然有一些浏览器不支持let!
有关let在阅读本答案时哪些浏览器支持该语句的最新概述,请参阅此let页面.
(*)全局和功能范围的变量可以在声明之前初始化和使用,因为JavaScript变量是悬挂的.这意味着声明始终位于范围的顶部.
(**)未提升块范围变量
Ben*_*n S 144
让我的工作非常像var.主要区别在于var变量的范围是整个封闭函数
维基百科上的这个表显示了哪些浏览器支持Javascript 1.7.
请注意,只有Mozilla和Chrome浏览器支持它.IE,Safari和其他可能没有.
Lcf*_*.vs 108
接受的答案缺少一点:
{
let a = 123;
};
console.log(a); // ReferenceError: a is not defined
Run Code Online (Sandbox Code Playgroud)
Has*_*alp 82
for (let i = 0; i < 5; i++) {
// i accessible ??
}
// i not accessible ?
Run Code Online (Sandbox Code Playgroud)
for (var i = 0; i < 5; i++) {
// i accessible ??
}
// i accessible ??
Run Code Online (Sandbox Code Playgroud)
?? 玩沙盒↓
Mic*_*ski 72
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
Run Code Online (Sandbox Code Playgroud)
在函数内部(但在块之外),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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
使用letin循环声明的变量只能在该循环内引用.
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.
Run Code Online (Sandbox Code Playgroud)
如果您使用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);
}
Run Code Online (Sandbox Code Playgroud)
由于时间死区,声明使用的变量在声明let之前无法访问.尝试这样做会引发错误.
console.log(noTDZ); // undefined
var noTDZ = 43;
console.log(hasTDZ); // ReferenceError: hasTDZ is not defined
let hasTDZ = 42;
Run Code Online (Sandbox Code Playgroud)
您不能多次声明同一个变量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
Run Code Online (Sandbox Code Playgroud)
constconst与let-it的块范围非常相似,并且具有TDZ.然而,有两件事是不同的.
声明使用的变量const无法重新分配.
const a = 42;
a = 43; // TypeError: Assignment to constant variable.
Run Code Online (Sandbox Code Playgroud)
请注意,这并不意味着该值是不可变的.它的属性仍然可以改变.
const obj = {};
obj.a = 42;
console.log(obj.a); // 42
Run Code Online (Sandbox Code Playgroud)
如果你想拥有一个不可变对象,你应该使用Object.freeze().
在使用声明变量时,始终必须指定一个值const.
const a; // SyntaxError: Missing initializer in const declaration
Run Code Online (Sandbox Code Playgroud)
vli*_*o20 48
以下是两者之间差异的示例(支持刚启动的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);Run Code Online (Sandbox Code Playgroud)
oll*_*iej 46
存在一些微妙的差异 - let范围行为更像是变量范围确实在或多或少任何其他语言中.
例如它适用于封闭块,它们在声明之前不存在,等等.
但值得注意的是,这let只是新Javascript实现的一部分,并且具有不同程度的浏览器支持.
Ali*_*eza 25
主要区别在于范围差异,而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
}`
Run Code Online (Sandbox Code Playgroud)
在程序和功能的顶级水平,让不像VAR,不创建全局对象上的属性.例如:
var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
Run Code Online (Sandbox Code Playgroud)
在块内使用时,将变量的范围限制为该块.请注意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
Run Code Online (Sandbox Code Playgroud)
另外不要忘记它的ECMA6功能,所以它还没有完全支持,所以最好总是使用Babel等将它转换为ECMA5 ...有关访问babel网站的更多信息
Ran*_*ner 25
解释摘自我在Medium写的文章:
\n\n\n提升是一种 JavaScript 机制,其中变量和函数声明被解析器移动到其作用域的顶部,解析器在 JavaScript 解释器开始执行实际代码之前将源代码读取为中间表示形式。因此,实际上\n\n\xe2\x80\x99 在哪里声明变量或函数并不重要,无论它们的作用域是全局的还是局部的,它们都将被移动到其作用域的顶部。这意味着
\nRun Code Online (Sandbox Code Playgroud)\nconsole.log (hi); \nvar hi = "say hi";\n实际上被解释为
\nRun Code Online (Sandbox Code Playgroud)\nvar hi = undefined;\nconsole.log (hi);\nhi = "say hi";\n因此,正如我们刚才所看到的,
\nvar变量被提升到其作用域的顶部,并使用未定义的值进行初始化,这意味着我们可以在代码中实际声明它们之前分配它们的值,如下所示:Run Code Online (Sandbox Code Playgroud)\nhi = \xe2\x80\x9csay hi\xe2\x80\x9d\nconsole.log (hi); // say hi\nvar hi;\n关于函数声明,我们可以在实际声明它们之前调用它们,如下所示:
\nRun Code Online (Sandbox Code Playgroud)\nsayHi(); // Hi\n\nfunction sayHi() {\n console.log(\'Hi\');\n};\n另一方面,函数表达式不会被提升,因此我们\xe2\x80\x99将得到以下错误:
\nRun Code Online (Sandbox Code Playgroud)\nsayHi(); //Output: "TypeError: sayHi is not a function\n\nvar sayHi = function() {\n console.log(\'Hi\');\n}; \nES6 为 JavaScript 开发人员引入了
\nlet和const关键字。而let和const是块作用域而不是函数作用域,因为var它在讨论它们的提升行为时不应该\xe2\x80\x99 产生影响。我们\xe2\x80\x99将从末尾开始,JavaScript提升let\nandconst。Run Code Online (Sandbox Code Playgroud)\nconsole.log(hi); // Output: Cannot access \'hi\' before initialization \nlet hi = \'Hi\';\n正如我们在上面看到的,
\nlet\xe2\x80\x99不允许我们使用未声明的\n变量,因此解释器显式输出一个引用错误\表明该变量hi在初始化之前\n无法访问。let如果我们将上面的\n改为constRun Code Online (Sandbox Code Playgroud)\nconsole.log(hi); // Output: Cannot access \'hi\' before initialization\nconst hi = \'Hi\';\n因此,最重要的是,JavaScript 解析器搜索变量声明和函数,并在代码执行之前将它们提升到作用域的顶部,并在内存中为它们赋值,以便解释器在执行代码时遇到它们。 \n将识别它们并能够使用它们指定的值执行代码。使用 声明的变量
\n\nlet或const在执行开始时保持\n未初始化状态,而使用 声明的变量\nvar正在使用值 进行初始化undefined。
zan*_*ngw 23
变量不挂起
let它不会提升到它们出现的整个范围.相比之下,var可能会如下所示提升.
{
console.log(cc); // undefined. Caused by hoisting
var cc = 23;
}
{
console.log(bb); // ReferenceError: bb is not defined
let bb = 23;
}
Run Code Online (Sandbox Code Playgroud)
其实,每@Bergi,两者var并let高挂.
垃圾收集
块范围let很有用,涉及闭包和垃圾收集以回收内存.考虑,
function process(data) {
//...
}
var hugeData = { .. };
process(hugeData);
var btn = document.getElementById("mybutton");
btn.addEventListener( "click", function click(evt){
//....
});
Run Code Online (Sandbox Code Playgroud)
该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){
//....
});
Run Code Online (Sandbox Code Playgroud)let 循环
let在循环中可以将它重新绑定到循环的每次迭代,确保从前一循环迭代的末尾重新赋值.考虑,
// print '5' 5 times
for (var i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Run Code Online (Sandbox Code Playgroud)
但是,替换var为let
// print 1, 2, 3, 4, 5. now
for (let i = 0; i < 5; ++i) {
setTimeout(function () {
console.log(i);
}, 1000);
}
Run Code Online (Sandbox Code Playgroud)
因为let创建一个新的词法环境,其中包含a)初始化表达式b)每次迭代(主要用于评估增量表达式),这里有更多细节.
abr*_*roz 18
这是一个添加其他人已经写过的例子.假设您要创建一个函数数组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
Run Code Online (Sandbox Code Playgroud)
上面的过程不会生成所需的函数数组,因为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
Run Code Online (Sandbox Code Playgroud)
这一次,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
Run Code Online (Sandbox Code Playgroud)
不要让这件事发生在你身上.使用短绒.
注意:这是一个教学示例,旨在演示循环中的
var/let行为和函数闭包,这也很容易理解.这将是添加数字的可怕方式.但是在匿名函数闭包中捕获数据的一般技术可能在其他环境中的现实世界中遇到.因人而异.
mor*_*gil 15
区别在于每个变量声明的变量范围.
在实践中,范围差异有许多有用的后果:
let变量仅在最近的封闭块({ ... })中可见.let变量只能在声明变量后出现的代码行中使用(即使它们被挂起!).let变量可能不会被后续的var或者重新声明let.let变量不会添加到全局window对象中.let变量易于使用闭包(它们不会导致竞争条件).通过let降低变量的可见性并增加早期发现意外名称冲突的可能性而施加的限制.这使得更容易跟踪和推理变量,包括它们的可达性(帮助回收未使用的内存).
因此,let当在大型程序中使用变量时,或者当独立开发的框架以新的和意外的方式组合时,变量不太可能导致问题.
var如果您确定在循环中使用闭包(#5)或在代码中声明外部可见的全局变量(#4)时确定需要单绑定效果,则可能仍然有用.var如果export从转换器空间迁移到核心语言,则可以取代用于导出的用途.
1.在最近的封闭块之外没有使用:
这个代码块将抛出一个引用错误,因为第二次使用x发生在声明它的块之外let:
{
let x = 1;
}
console.log(`x is ${x}`); // ReferenceError during parsing: "x is not defined".
Run Code Online (Sandbox Code Playgroud)
相比之下,同样的例子与var作品.
2.声明之前没有用:
这个代码块会ReferenceError在代码运行之前抛出,因为x在声明它之前使用它:
{
x = x + 1; // ReferenceError during parsing: "x is not defined".
let x;
console.log(`x is ${x}`); // Never runs.
}
Run Code Online (Sandbox Code Playgroud)
相比之下,使用varparses和run 的相同示例没有抛出任何异常.
3.无重新声明:
以下代码演示了声明的变量let以后可能不会重新声明:
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
5.易于使用闭包:
声明的变量var不适用于循环内的闭包.这是一个简单的循环,它输出变量i在不同时间点的值序列:
for (let i = 0; i < 5; i++) {
console.log(`i is ${i}`), 125/*ms*/);
}
Run Code Online (Sandbox Code Playgroud)
具体来说,这输出:
i is 0
i is 1
i is 2
i is 3
i is 4
Run Code Online (Sandbox Code Playgroud)
在JavaScript中,我们经常在比创建变量时更晚的时间使用变量.当我们通过将输出延迟传递给setTimeout:
for (let i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
Run Code Online (Sandbox Code Playgroud)
...只要我们坚持,输出就会保持不变let.相反,如果我们使用了var i:
for (var i = 0; i < 5; i++) {
setTimeout(_ => console.log(`i is ${i}`), 125/*ms*/);
}
Run Code Online (Sandbox Code Playgroud)
...循环意外地输出"我是5"五次:
i is 5
i is 5
i is 5
i is 5
i is 5
Run Code Online (Sandbox Code Playgroud)
Kas*_*tha 15
var --> Function scope
let --> Block scope
const --> Block scope
Run Code Online (Sandbox Code Playgroud)
变量
在此代码示例中,变量i是使用 声明的var。因此,它有一个函数作用域。这意味着您i只能从function x. 您无法从外部读取它function x
var --> Function scope
let --> Block scope
const --> Block scope
Run Code Online (Sandbox Code Playgroud)
在此示例中,您可以看到在块i内声明if。但它是使用var. 因此,它获得了函数作用域。这意味着您仍然可以访问i内部变量function x。因为var总是将范围限定在函数上。即使变量i是在if块内声明的,由于它正在使用var它,因此它的作用域为parent function x。
function x(){
var i = 100;
console.log(i); // 100
}
console.log(i); // Error. You can't do this
x();Run Code Online (Sandbox Code Playgroud)
现在变量i在function y. 因此,i范围为function y. 就可以进入i里面了function y。但不是从外面来的function y。
function x(){
if(true){
var i = 100;
}
console.log(i);
}
x();Run Code Online (Sandbox Code Playgroud)
function x(){
function y(){
var i = 100;
console.log(i);
}
y();
}
x();Run Code Online (Sandbox Code Playgroud)
让,常量
let 和 const 具有块作用域。
const并且let行为相同。但不同的是,当你给它赋值时const就不能再重新赋值了。但您可以使用 重新分配值let。
在此示例中,变量在块i内声明if。因此只能从该if块内部访问它。我们无法从该if块之外访问它。(这里的const工作与 相同let)
function x(){
function y(){
var i = 100;
}
console.log(i); // ERROR
}
x();Run Code Online (Sandbox Code Playgroud)
if(true){
let i = 100;
console.log(i); // Output: 100
}
console.log(i); // ErrorRun Code Online (Sandbox Code Playgroud)
(let, const)与vs的另一个区别var是您可以在声明变量之前访问var已定义的变量。它会给你undefined。但是,如果您使用let或const定义的变量执行此操作,则会出现错误。
function x(){
if(true){
let i = 100;
console.log(i); // Output: 100
}
console.log(i); // Error
}
x();Run Code Online (Sandbox Code Playgroud)
console.log(x);
var x = 100;Run Code Online (Sandbox Code Playgroud)
Sri*_*hna 14
ES6 引入了两个新的关键字(let和const)来替代var。
当您需要块级减速时,您可以使用 let 和 const 而不是 var。
下表总结了 var、let 和 const 之间的区别
Abd*_*UMI 13
可能以下两个函数显示了不同之处:
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
}
Run Code Online (Sandbox Code Playgroud)
Dmi*_*try 12
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++);
}
}
}
})();
Run Code Online (Sandbox Code Playgroud)
这导致计数[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++);
}
}
}
})();
Run Code Online (Sandbox Code Playgroud)
只计算[0,1].
Wil*_*een 12
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
Run Code Online (Sandbox Code Playgroud)
变量var:
当第一个函数testVar被调用时,声明的变量foo var仍可在if语句外部访问.这个变量foo将可无处不在的范围内的testVar 功能.
变量let:
当testLet调用第二个函数时,声明为的变量bar let只能在if语句中访问.因为与声明的变量let是块作用域(其中块是大括号中的代码例如if{},for{},function{}).
let 变量不会被挂起:var和之间的另一个区别let是声明的变量let 不会被提升.一个示例是说明此行为的最佳方法:
let 不挂起的变量:
console.log(letVar);
let letVar = 10;
// referenceError, the variable doesn't get hoisted
Run Code Online (Sandbox Code Playgroud)
带有var do的变量被挂起:
console.log(varVar);
var varVar = 10;
// logs undefined, the variable gets hoisted
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
何时应该
let使用var?
尽可能使用let,var因为它的范围更加具体.这减少了在处理大量变量时可能发生的潜在命名冲突.var当你想要一个显式的全局变量在window对象上时可以使用(如果真的有必要,请务必仔细考虑).
小智 8
看来,至少在Visual Studio 2015中,TypeScript 1.5中,"var"允许在块中对同一个变量名进行多次声明,而"let"则不允许.
这不会产生编译错误:
var x = 1;
var x = 2;
Run Code Online (Sandbox Code Playgroud)
这将:
let x = 1;
let x = 2;
Run Code Online (Sandbox Code Playgroud)
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 definedRun Code Online (Sandbox Code Playgroud)
一些技巧let:
1.
let statistics = [16, 170, 10];
let [age, height, grade] = statistics;
console.log(height)
Run Code Online (Sandbox Code Playgroud)
2.
let x = 120,
y = 12;
[x, y] = [y, x];
console.log(`x: ${x} y: ${y}`);
Run Code Online (Sandbox Code Playgroud)
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"
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
使用时 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
Run Code Online (Sandbox Code Playgroud)
使用时 var
var ES5中的变量在函数中具有作用域,这意味着变量在函数内部有效,而在函数本身外部无效。
var变量可以在window对象中访问,因为它们不能全局访问。
function a(){ // this is the Max Scope for var variable
{
var x = 12;
}
console.log(x);
}
a(); // 12
Run Code Online (Sandbox Code Playgroud)
如果您想了解更多,请继续阅读下面的内容
对范围最有名的面试问题,人们也可以足够精确的使用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);
}
Run Code Online (Sandbox Code Playgroud)
这是因为使用时 let,对于每次循环迭代,变量都具有作用域并具有自己的副本。
使用时 var
for (var i = 0; i < 10 ; i++) {
setTimeout(
function a() {
console.log(i); //print 10 times 10
},
100 * i);
}
Run Code Online (Sandbox Code Playgroud)
这是因为使用时var,对于每次循环迭代,变量都具有作用域并具有共享副本。
下面显示了 '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
Run Code Online (Sandbox Code Playgroud)
的gfoo,通过定义let最初是在全球范围内,而当我们声明gfoo里面又if clause其范围变化,当一个新的值赋给变量该范围内它不会影响全局范围。
而hfoo,由 定义var最初是在全局范围内,但是当我们再次在 中声明它时if clause,它会考虑全局范围 hfoo,尽管再次使用了 var 来声明它。当我们重新分配它的值时,我们看到全局范围 hfoo 也受到影响。这是主要的区别。
如果我没看错规范,那么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
Run Code Online (Sandbox Code Playgroud)
请参阅“ 模拟专用接口 ”
小智 6
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
}
Run Code Online (Sandbox Code Playgroud)
让与 var。这都是关于范围的。
var 变量是全局的,基本上可以在任何地方访问,而let 变量不是全局的,只有在右括号杀死它们之前才存在。
请参阅我下面的示例,并注意 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.
Run Code Online (Sandbox Code Playgroud)
我刚刚遇到一个用例,我必须使用它var来let引入新变量。这是一个案例:
我想创建一个具有动态变量名称的新变量。
let variableName = 'a';
eval("let " + variableName + '= 10;');
console.log(a); // this doesn't work
Run Code Online (Sandbox Code Playgroud)
var variableName = 'a';
eval("var " + variableName + '= 10;');
console.log(a); // this works
Run Code Online (Sandbox Code Playgroud)
上面的代码不起作用,因为eval引入了一个新的代码块。声明 usingvar将在该代码块之外声明一个变量,因为var在函数范围内声明一个变量。
let另一方面,在块作用域中声明变量。因此,a变量仅在块中可见eval。
| 归档时间: |
|
| 查看次数: |
1114640 次 |
| 最近记录: |