2019-04-21 11:50:48 +08:00

360 lines
19 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<article id="wikiArticle">
<div></div>
<div> </div>
<div><strong>let</strong> 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。</div>
<h2 id="语法">语法</h2>
<pre><code class="language-javascript">let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];</code></pre>
<h3 id="参数">参数</h3>
<dl>
<dt><code>var1</code>, <code>var2</code>, …, <code>varN</code></dt>
<dd>变量名。必须是合法的标识符。</dd>
<dt><code>value1</code>, <code>value2</code>, …, <code>valueN</code></dt>
<dd>变量的初始值。可以是任意合法的表达式。</dd>
</dl>
<h2 id="描述">描述</h2>
<p><strong><code>let</code></strong>允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与<strong><code>var</code></strong>关键字不同的是,<code><strong>var</strong></code>声明的变量只能是全局或者整个函数块的。</p>
<p><a class="external" href="https://stackoverflow.com/questions/37916940/js-why-let-have-this-name" rel="noopener">这里</a>可以明白我们为什么选取“<strong>let</strong>”这个名字。</p>
<h3 id="作用域规则">作用域规则</h3>
<p><code><strong>let</strong></code>声明的变量只在其声明的块或子块中可用,这一点,与<code><strong>var</strong></code>相似。二者之间最主要的区别在于<code><strong>var</strong></code>声明的变量的作用域是整个封闭函数。</p>
<pre><code class="language-javascript">function varTest() {
var x = 1;
if (true) {
var x = 2; // 同样的变量!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
if (true) {
let x = 2; // 不同的变量
console.log(x); // 2
}
console.log(x); // 1
}</code></pre>
<h3 id="简化内部函数代码">简化内部函数代码</h3>
<p>当用到内部函数的时候,<code><strong>let</strong></code>会让你的代码更加简单。</p>
<pre><code class="language-js">var list = document.getElementById("list");
for (let i = 1; i &lt;= 5; i++) {
var item = document.createElement("LI");
item.appendChild(document.createTextNode("Item " + i));
  let j = i;
item.onclick = function (ev) {
console.log("Item " + j + " is clicked.");
};
list.appendChild(item);
}
</code></pre>
<p>上面这段代码的意图是创建5个li,点击不同的li能够打印出当前li的序号。如果不用<strong><code>let</code></strong>,而改用<code><strong>var</strong></code>的话,将总是打印出 <code>Item 5 is Clicked</code>,因为 j 是函数级变量5个内部函数都指向了同一个 j ,而 j 最后一次赋值是5。用了<strong><code>let</code></strong>j 变成块级域(也就是花括号中的块,每进入一次花括号就生成了一个块级域),所以 5 个内部函数指向了不同的 j 。</p>
<p>在程序或者函数的顶层,<code><strong>let</strong></code>并不会像<strong><code>var</code></strong>一样在全局对象上创造一个属性,比如</p>
<pre><code class="language-js">var x = 'global';
let y = 'global';
console.log(this.x); // "global"
console.log(this.y); // undefined
</code></pre>
<h3 id="模仿私有接口">模仿私有接口</h3>
<p>在处理<a href="https://developer.mozilla.org/en-US/docs/Glossary/Constructor">构造函数</a>的时候,可以通过<code><strong>let</strong></code>声明而不是闭包来创建私有接口。</p>
<pre><code class="language-javascript">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</code></pre>
<h3 id="let_的暂存死区与错误">let 的暂存死区与错误</h3>
<p>在(<s>同一个函数或</s>)同一个作用域中用let重复定义一个变量将引起 <code><a class="new" href="/zh-CN/docs/JavaScript/Reference/Global_Objects/TypeError" rel="nofollow" title="TypeError">TypeError</a></code>.</p>
<pre><code class="language-javascript">if (x) {
let foo;
let foo; // TypeError thrown.
}
</code></pre>
<p>在 ECMAScript 2015 中,<strong>let </strong>绑定不受变量提升的约束,这意味着 <strong>let</strong> <strong> </strong>声明<strong>不会</strong>被提升到当前执行上下文的顶部。在块中的变量初始化之前,引用它将会导致 <a href="Reference/Global_Objects/ReferenceError">ReferenceError</a>(而使用 var 声明变量则恰恰相反该变量的值是 undefined )。这个变量处于从块开始到 <strong>let</strong> 初始化处理的”暂存死区“之中。</p>
<pre><code class="language-javascript">function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined
var bar = 1;
let foo = 2;
}</code></pre>
<p>在 <a class="new" href="/zh-CN/docs/JavaScript/Reference/Statements/switch" rel="nofollow" title="switch"><code>switch</code></a> 声明中你可能会遇到这样的错误因为一个switch只有一个作用块。</p>
<pre><code class="language-javascript">switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // SyntaxError for redeclaration.
break;
}</code></pre>
<p>但是需要指出在case语句后面可以创建新的作用域块形成新的词法环境这样就不会产生上述重复声明<code>redeclaration</code>)的语法错误。</p>
<pre><code class="language-javascript">let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}</code></pre>
<p><code><strong>let</strong></code>后跟一个函数传递的参数时将导致循环内部报错。</p>
<pre><code class="language-javascript">function go(n){
for (let n of n.a) { // ReferenceError: n is not defined
console.log(n);
}
}
go({a:[1,2,3]});
</code></pre>
<h3 id="循环定义中的let作用域"><font face="Consolas, Liberation Mono, Courier, monospace">循环定义中的let作用域</font></h3>
<p>循环体中是可以引用在for声明时用let定义的变量尽管let不是出现在大括号之间.(注:该方法在 火狐 45.4.0 ,Centos7 下,报错 <span class="hasBreakSwitch objectBox objectBox-errorMessage"><span class="errorMessage">ReferenceError: can't access lexical declaration `i' before initialization</span></span>)</p>
<pre><code class="language-js">var i = 0;
for (let i = i; i &lt; 10; i++) {
console.log(i);
}
</code></pre>
<div class="note">
<p>注:以上 let 声明的 i 将会变成 undefinedchrome 版本50.0.2661.102 (64-bit);推荐以下写法:</p>
</div>
<pre>var i = 0;
for (let l = i; l &lt; 10; l++) {
 console.log(l);
}</code></pre>
<h4 id="域作用规则">域作用规则</h4>
<pre class="eval">for (let <var>expr1</var>; <var>expr2</var>; <var>expr3</var>) <var>statement</var>
</code></pre>
<p>在这个例子中,<var>expr2</var>, <var>expr3</var>, 和 <var>statement</var> 都是包含在一个隐含域块中,其中也包含了 expr1.</p>
<h2 id="例子">例子</h2>
<h3 id="let_对比_var"><code>let</code>  对比 <code>var</code></h3>
<p>let的作用域是块而var的作用域是函数</p>
<pre><code class="language-javascript">var a = 5;
var b = 10;
if (a === 5) {
let a = 4; // The scope is inside the if-block
var b = 1; // The scope is inside the function
console.log(a); // 4
console.log(b); // 1
}
console.log(a); // 5
console.log(b); // 1</code></pre>
<h3 id="let_在循环中"><code>let</code> 在循环中</h3>
<p>可以用 let 来代替 var ,在 for 定义块中使用块级变量.</p>
<pre><code class="language-javascript">for (let i = 0; i &lt; 10; i++) {
console.log(i); // 0, 1, 2, 3, 4 ... 9
}
console.log(i); // i is not defined</code></pre>
<h2 id="非标准的_let_扩展">非标准的 let 扩展</h2>
<h3 id="let块let_block"><code>let块<strong></strong></code><strong><code>let</code> block</strong></h3>
<div class="warning">
<p>let blocks 在 Gecko 44 中已经废除( <a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1167029" rel="noopener" title="FIXED: Remove SpiderMonkey support for let blocks">bug 1167029</a></p>
</div>
<p><strong><code>let块</code></strong>提供了一种在块的范围内获取变量的值,而不会影响块外面名字相同的变量的值的方法。</p>
<h4 id="语法_2">语法</h4>
<pre><code class="language-javascript">let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) block;</code></pre>
<h4 id="描述_2">描述</h4>
<p>let 语句块为变量提供了局部作用域。它的作用是在单一代码块的词法范围内绑定零个或多个变量; 此外与普通语句块没有任何区别。需要特别注意的是, 在 <code>let </code>语句块内使用 var 声明的变量它的作用域与在 let 语句块之外声明没有区别;这样的变量仍然具有函数作用域。在使用 <code>let</code> 语句块时,必须使用花括号,否则会导致语法错误。</p>
<h4 id="例子_2">例子</h4>
<pre><code class="language-js">var x = 5;
var y = 0;
let (x = x + 10, y = 12) {
console.log(x + y); // 27
}
console.log(x + y); // 5
</code></pre>
<p>let 代码块的规则与 JavaScript 中其他类型的代码块相同。允许在块内通过 let 关键字声明局部变量。</p>
<h4 id="作用域规则_2">作用域规则</h4>
<p>使用 <code>let</code> 语句块绑定的变量,其作用域是 <code>let</code> 语句块本身,与任何其内部语句块的作用域一样,除非在这些内部语句块内又定义了同名的变量。</p>
<h3 id="let_表达式_let_expression"><code>let </code><strong>表达式(</strong> let expression</h3>
<div class="warning">
<p><code>let</code> expression 在 Gecko 41 已经废除(<a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1023609" rel="noopener" title="FIXED: Remove SpiderMonkey support for let expressions">bug 1023609</a>)。</p>
</div>
<p><strong><code>let</code>表达式</strong> 可以将变量的作用域仅作用于一条语句。</p>
<h4 id="语法_3">语法</h4>
<pre><code class="language-javascript">let (var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]]) expression;</code></pre>
<h4 id="例子_3">例子</h4>
<p>你可以在一条语句的范围中使用 <code>let</code> 关键字来设立变量:</p>
<pre><code class="language-javascript">var a = 5;
let(a = 6) console.log(a); // 6
console.log(a); // 5</code></pre>
<h4 id="作用域规则_3">作用域规则</h4>
<p>给定一个 <code>let</code> 表达式:</p>
<pre class="eval">let (<var>decls</var>) <var>expr</var>
</code></pre>
<p>这里隐式创建了一个包围 <var>expr</var> 的语句块。</p>
<h2 id="规范">规范</h2>
<table class="standard-table">
<tbody>
<tr>
<th scope="col">Specification</th>
<th scope="col">Status</th>
<th scope="col">Comment</th>
</tr>
<tr>
<td><a class="external" href="https://www.ecma-international.org/ecma-262/6.0/#sec-let-and-const-declarations" hreflang="en" lang="en" rel="noopener">ECMAScript 2015 (6th Edition, ECMA-262)<br/><small lang="zh-CN">Let and Const Declarations</small></a></td>
<td><span class="spec-Standard">Standard</span></td>
<td>Initial definition. Does not specify let expressions or let blocks.</td>
</tr>
<tr>
<td><a class="external" href="https://tc39.github.io/ecma262/#sec-let-and-const-declarations" hreflang="en" lang="en" rel="noopener">ECMAScript Latest Draft (ECMA-262)<br/><small lang="zh-CN">Let and Const Declarations</small></a></td>
<td><span class="spec-Draft">Draft</span></td>
<td> </td>
</tr>
</tbody>
</table>
<h2 id="浏览器兼容性">浏览器兼容性</h2>
<p></p><div class="blockIndicator warning"><strong><a class="external" href="https://github.com/mdn/browser-compat-data" rel="noopener">We're converting our compatibility data into a machine-readable JSON format</a></strong>.
This compatibility table still uses the old format,
because we haven't yet converted the data it contains.
<strong><a class="new" href="/zh-CN/docs/MDN/Contribute/Structures/Compatibility_tables" rel="nofollow">Find out how you can help!</a></strong></div>
<div class="htab">
<a id="AutoCompatibilityTable" name="AutoCompatibilityTable"></a>
<ul>
<li class="selected"><a>Desktop</a></li>
<li><a>Mobile</a></li>
</ul>
</div><p></p>
<div id="compat-desktop">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Chrome</th>
<th>Edge</th>
<th>Firefox (Gecko)</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari</th>
</tr>
<tr>
<td>Basic support</td>
<td>41.0</td>
<td><span style="color: #888;" title="Please update this with the earliest version of support.">(Yes)</span></td>
<td><a href="/en-US/Firefox/Releases/44" title="Released on 2016-01-26.">44</a> (44)</td>
<td>11</td>
<td>17</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
</tr>
<tr>
<td>Temporal dead zone</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: #888;" title="Please update this with the earliest version of support.">(Yes)</span></td>
<td><a href="/en-US/Firefox/Releases/35" title="Released on 2015-01-13.">35</a> (35)</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
</tr>
<tr>
<td><code>let</code> expression <span class="icon-only-inline" title="This API has not been standardized."><i class="icon-warning-sign"> </i></span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
</tr>
<tr>
<td><code>let</code> block <span class="icon-only-inline" title="This API has not been standardized."><i class="icon-warning-sign"> </i></span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
</tr>
</tbody>
</table>
</div>
<div id="compat-mobile">
<table class="compat-table">
<tbody>
<tr>
<th>Feature</th>
<th>Android</th>
<th>Chrome for Android</th>
<th>Firefox Mobile (Gecko)</th>
<th>IE Mobile</th>
<th>Opera Mobile</th>
<th>Safari Mobile</th>
</tr>
<tr>
<td>Basic support</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td>41.0</td>
<td>44.0 (44)</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
</tr>
<tr>
<td>Temporal dead zone</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td>35.0 (35)</td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
<td><span style="color: rgb(255, 153, 0);" title="Compatibility unknown; please update this.">?</span></td>
</tr>
<tr>
<td><code>let</code> expression <span class="icon-only-inline" title="This API has not been standardized."><i class="icon-warning-sign"> </i></span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
</tr>
<tr>
<td><code>let</code> block <span class="icon-only-inline" title="This API has not been standardized."><i class="icon-warning-sign"> </i></span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
<td><span style="color: #f00;">未实现</span></td>
</tr>
</tbody>
</table>
</div>
<h2 id="Firefox-specific_notes">Firefox-specific notes</h2>
<ul>
<li>[1]: 只允许用在被<code>&lt;script type="application/javascript;version=1.7"&gt;</code> 包裹的代码块中 (或者更高的 版本version。当心无论如何 作为一个非标准特性 很有可能会打破其他浏览器的支持。 <a class="new" href="/zh-CN/docs/XUL" rel="nofollow" title="zh-CN/docs/XUL">XUL</a> 脚本标签实现这些特性不需要特殊的块。 请看 <a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=932517" rel="noopener" title="FIXED: Enable let without version=1.7+ in non-strict mode">bug 932517</a><a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=932517" rel="noopener" title="FIXED: Enable let without version=1.7+ in non-strict mode">bug 932517</a></li>
<li>ES6 compliance for <code>let</code> in SpIderMonkey is tracked in <a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=950547" rel="noopener" title="FIXED: Make let and const ES6-compatible">bug 950547</a> and non-standard extensions are going to be removed in the future <a class="external" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1023609" rel="noopener" title="FIXED: Remove SpiderMonkey support for let expressions">bug 1023609</a>.</li>
</ul>
<h2 id="相关链接">相关链接</h2>
<ul>
<li><a href="Reference/Statements/var"><code>var</code></a></li>
<li><a href="Reference/Statements/const"><code>const</code></a></li>
<li><a class="external" href="https://hacks.mozilla.org/2015/07/es6-in-depth-let-and-const/" rel="noopener">ES6 In Depth: <code>let</code> and <code>const</code></a></li>
<li><a class="external" href="https://blog.mozilla.org/addons/2015/10/14/breaking-changes-let-const-firefox-nightly-44/" rel="noopener">Breaking changes in <code>let</code> and <code>const</code> in Firefox 44.</a></li>
<li><a class="external" href="https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch3.md" rel="noopener">You Don't Know JS: Scope &amp; Closures: Chapter 3: Function vs. Block Scope</a></li>
</ul>
</article>