diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/javascript/reference/strict_mode | |
parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip |
initial commit
Diffstat (limited to 'files/zh-cn/web/javascript/reference/strict_mode')
-rw-r--r-- | files/zh-cn/web/javascript/reference/strict_mode/index.html | 375 | ||||
-rw-r--r-- | files/zh-cn/web/javascript/reference/strict_mode/transitioning_to_strict_mode/index.html | 128 |
2 files changed, 503 insertions, 0 deletions
diff --git a/files/zh-cn/web/javascript/reference/strict_mode/index.html b/files/zh-cn/web/javascript/reference/strict_mode/index.html new file mode 100644 index 0000000000..4f48146e49 --- /dev/null +++ b/files/zh-cn/web/javascript/reference/strict_mode/index.html @@ -0,0 +1,375 @@ +--- +title: 严格模式 +slug: Web/JavaScript/Reference/Strict_mode +tags: + - JavaS + - Strict Mode + - 严格模式 +translation_of: Web/JavaScript/Reference/Strict_mode +--- +<div>{{JsSidebar("More")}}</div> + +<div class="note">有时你会看到非严格模式,被称为“<strong><a href="https://developer.mozilla.org/docs/Glossary/Sloppy_mode" id="sloppyModeId333">sloppy mode</a></strong>”。这不是一个官方术语,但以防万一,你应该意识到这一点。</div> + +<div><a class="external" href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript 5</a>的<strong>严格模式</strong>是采用具有限制性JavaScript变体的一种方式,从而使代码显示地 脱离“马虎模式/稀松模式/懒散模式“(sloppy)模式。</div> + +<div>严格模式不仅仅是一个子集:它的产生是为了形成与正常代码不同的语义。</div> + +<div>不支持严格模式与支持严格模式的浏览器在执行严格模式代码时会采用不同行为。</div> + +<div>所以在没有对运行环境展开<strong>特性测试</strong>来验证对于严格模式相关方面支持的情况下,就算采用了严格模式也不一定会取得预期效果。严格模式代码和非严格模式代码可以共存,因此项目脚本可以渐进式地采用严格模式。</div> + +<div></div> + +<div>严格模式对正常的 JavaScript语义做了一些更改。</div> + +<div></div> + +<ol> + <li>严格模式通过<strong>抛出错误</strong>来消除了一些原有<strong>静默错误</strong>。</li> + <li>严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时候,相同的代码,严格模式可以比非严格模式下<strong>运行得更快</strong>。</li> + <li>严格模式<strong>禁用了</strong>在ECMAScript的未来版本中可能会定义的一些语法。</li> +</ol> + +<p>如果你想改变你的代码,让其工作在具有限制性JavaScript环境中,请参阅<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode">转换成严格模式</a>。</p> + +<h2 id="调用严格模式">调用严格模式</h2> + +<p>严格模式可以应用到<span id="noHighlight_0.9206554004884853">整个脚本</span>或个别函数中。不要在封闭大括弧 <code style="font-size: 14px;">{}</code> 内这样做,在这样的上下文中这么做是没有效果的<span style="line-height: 1.5;">。</span>在 <code style="font-size: 14px;">eval</code><span style="line-height: 1.5;"> 、</span><code style="font-size: 14px;">Function</code><span style="line-height: 1.5;"> 、内联事件处理属性、 </span> {{domxref("WindowTimers.setTimeout()")}} 方法中传入的脚本字符串,其行为类似于开启了严格模式的一个单独脚本<span id="noHighlight_0.9206554004884853">,它们会</span>如预期一样工作。</p> + +<h3 id="为脚本开启严格模式">为脚本开启严格模式</h3> + +<p>为整个脚本文件开启严格模式,需要在所有语句之前放一个特定语句 <code>"use strict";</code> (或 <code>'use strict';</code>)</p> + +<pre class="brush: js">// 整个脚本都开启严格模式的语法 +"use strict"; +var v = "Hi! I'm a strict mode script!"; +</pre> + +<p>这种语法存在陷阱,有一个<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=627531">大型网站</a>已经被它<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=579119">坑倒</a>了:不能盲目的合并冲突代码。试想合并一个严格模式的脚本和一个非严格模式的脚本:合并后的脚本代码看起来是严格模式。反之亦然:非严格合并严格看起来是非严格的。合并均为严格模式的脚本或均为非严格模式的都没问题,只有在合并严格模式与非严格模式有可能有问题。建议按一个个函数去开启严格模式(至少在学习的过渡期要这样做).</p> + +<p>您也可以将整个脚本的内容用一个函数包括起来,然后在这个外部函数中使用严格模式。这样做就可以消除合并的问题,但是这就意味着您必须要在函数作用域外声明一个全局变量。</p> + +<h3 id="为函数开启严格模式">为函数开启严格模式</h3> + +<p>同样的,要给某个函数开启严格模式,得把 <code>"use strict";</code> (或 <code>'use strict'; </code>)声明<em>一字不漏地</em>放在函数体所有语句之前。</p> + +<pre class="brush: js">function strict() { + // 函数级别严格模式语法 + 'use strict'; + function nested() { + return "And so am I!"; + } + return "Hi! I'm a strict mode function! " + nested(); +} + +function notStrict() { + return "I'm not strict."; +} +</pre> + +<h2 id="严格模式中的变化">严格模式中的变化</h2> + +<p>严格模式同时改变了语法及运行时行为。变化通常分为这几类:将问题直接转化为错误(如语法错误或运行时错误), 简化了如何为给定名称的特定变量计算,简化了 <code>eval</code> 以及 <code>arguments</code>, 将写"安全“JavaScript的步骤变得更简单,以及改变了预测未来ECMAScript行为的方式。</p> + +<h3 id="将过失错误转成异常" style="position: static;">将过失错误转成异常</h3> + +<p>在严格模式下, 某些先前被接受的过失错误将会被认为是异常. JavaScript被设计为能使新人开发者更易于上手, 所以有时候会给本来错误操作赋予新的不报错误的语义(non-error semantics). 有时候这可以解决当前的问题, 但有时候却会给以后留下更大的问题. 严格模式则把这些失误当成错误, 以便可以发现并立即将其改正.</p> + +<p>第一,严格模式下无法再意外创建全局变量。在普通的JavaScript里面给一个错误命名的变量名赋值会使全局对象新增一个属性并继续“工作”(尽管将来可能会失败:在现代的JavaScript中有可能)。严格模式中意外创建全局变量被抛出错误替代:</p> + +<pre class="brush: js">"use strict"; + // 假如有一个全局变量叫做<code>mistypedVariable</code> +mistypedVaraible = 17; // 因为变量名拼写错误 + // 这一行代码就会抛出 ReferenceError +</pre> + +<p>第二, 严格模式会使引起静默失败(silently fail,注:不报错也没有任何效果)的赋值操作抛出异常. 例如, <code>NaN</code> 是一个不可写的全局变量. 在正常模式下, 给 <code>NaN</code> 赋值不会产生任何作用; 开发者也不会受到任何错误反馈. 但在严格模式下, 给 <code>NaN</code> 赋值会抛出一个异常. 任何在正常模式下引起静默失败的赋值操作 (给不可写属性赋值, 给只读属性(getter-only)赋值, 给不可扩展对象(<a href="/zh-CN/JavaScript/Reference/Global_Objects/Object/preventExtensions" title="zh-CN/JavaScript/Reference/Global Objects/Object/preventExtensions">non-extensible</a> object)的新属性赋值) 都会抛出异常:</p> + +<pre class="brush: js">"use strict"; + +// 给不可写属性赋值 +var obj1 = {}; +Object.defineProperty(obj1, "x", { value: 42, writable: false }); +obj1.x = 9; // 抛出TypeError错误 + +// 给只读属性赋值 +var obj2 = { get x() { return 17; } }; +obj2.x = 5; // 抛出TypeError错误 + +// 给不可扩展对象的新属性赋值 +var fixed = {}; +Object.preventExtensions(fixed); +fixed.newProp = "ohai"; // 抛出TypeError错误 +</pre> + +<p>第三, 在严格模式下, 试图删除不可删除的属性时会抛出异常(之前这种操作不会产生任何效果):</p> + +<pre class="brush: js">"use strict"; +delete Object.prototype; // 抛出TypeError错误 +</pre> + +<p>第四,在Gecko版本34之前,严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。因为只有最后一个属性起作用,当代码要去改变属性值而不是修改最后一个重名属性的时候,复制这个对象就产生一连串的bug。在严格模式下,重名属性被认为是语法错误:</p> + +<div class="note"> +<p>这个问题在ECMAScript6中已经不复存在({{bug(1041128)}})。</p> +</div> + +<pre class="brush: js">"use strict"; +var o = { p: 1, p: 2 }; // !!! 语法错误 +</pre> + +<p>第五, 严格模式要求函数的参数名唯一. 在正常模式下, 最后一个重名参数名会掩盖之前的重名参数. 之前的参数仍然可以通过 <code>arguments[i] 来访问</code>, 还不是完全无法访问. 然而, 这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了), 所以在严格模式下重名参数被认为是语法错误:</p> + +<pre class="brush: js">function sum(a, a, c) { // !!! 语法错误 + "use strict"; + return a + a + c; // 代码运行到这里会出错 +} +</pre> + +<p>第六, 严格模式禁止八进制数字语法. ECMAScript并不包含八进制语法, 但所有的浏览器都支持这种以零(<code>0</code>)开头的八进制语法: <code>0644 === 420</code> 还有 <code>"\045" === "%"</code>.在ECMAScript 6中支持为一个数字加"<code>0</code>o"的前缀来表示八进制数.</p> + +<pre class="brush: js">var a = 0o10; // ES6: 八进制</pre> + +<p>有些新手开发者认为数字的前导零没有语法意义, 所以他们会用作对齐措施 — 但其实这会改变数字的意义! 八进制语法很少有用并且可能会错误使用, 所以严格模式下八进制语法会引起语法错误:</p> + +<pre class="brush: js">"use strict"; +var sum = 015 + // !!! 语法错误 + 197 + + 142; +</pre> + +<p>第七,ECMAScript 6中的严格模式禁止设置{{Glossary("primitive")}}值的属性.不采用严格模式,设置属性将会简单忽略(no-op),采用严格模式,将抛出{{jsxref("TypeError")}}错误</p> + +<pre class="syntaxbox">(function() { + "use strict"; + + false.true = ""; //TypeError + (14).sailing = "home"; //TypeError + "with".you = "far away"; //TypeError +})();</pre> + +<h3 id="简化变量的使用">简化变量的使用</h3> + +<p>严格模式简化了代码中变量名字映射到变量定义的方式. 很多编译器的优化是依赖存储变量X位置的能力:这对全面优化JavaScript代码至关重要. JavaScript有些情况会使得代码中名字到变量定义的基本映射只在运行时才产生. 严格模式移除了大多数这种情况的发生, 所以编译器可以更好的优化严格模式的代码.</p> + +<p>第一, 严格模式禁用 <code>with</code>. <code>with</code>所引起的问题是块内的任何名称可以映射(map)到with传进来的对象的属性, 也可以映射到包围这个块的作用域内的变量(甚至是全局变量), 这一切都是在运行时决定的: 在代码运行之前是无法得知的. 严格模式下, 使用 <code>with</code> 会引起语法错误, 所以就不会存在 with 块内的变量在运行时才决定引用到哪里的情况了:</p> + +<pre class="brush: js">"use strict"; +var x = 17; +with (obj) { // !!! 语法错误 + // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x? + // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。 + x; +} +</pre> + +<p><span style="line-height: 1.5;">一种取代 </span><code>with</code>的简单方法<span style="line-height: 1.5;">是,将目标对象赋给一个短命名变量,然后访问这个变量上的相应属性</span><span style="line-height: 1.5;">.</span></p> + +<p>第二, <a class="external" href="http://whereswalden.com/2011/01/10/new-es5-strict-mode-support-new-vars-created-by-strict-mode-eval-code-are-local-to-that-code-only/"><code>严格模式下的 eval 不再为上层范围(surrounding scope,注:包围eval代码块的范围)引入新变量</code></a>. 在正常模式下, 代码 <code>eval("var x;")</code> 会给上层函数(surrounding function)或者全局引入一个新的变量 <code>x</code> . 这意味着, 一般情况下, 在一个包含 <code>eval</code> 调用的函数内所有没有引用到参数或者局部变量的名称都必须在运行时才能被映射到特定的定义 (因为 <code>eval</code> 可能引入的新变量会覆盖它的外层变量). 在严格模式下 <code>eval</code> 仅仅为被运行的代码创建变量, 所以 <code>eval</code> 不会使得名称映射到外部变量或者其他局部变量:</p> + +<pre class="brush: js">var x = 17; +var evalX = eval("'use strict'; var x = 42; x"); +console.assert(x === 17); +console.assert(evalX === 42); +</pre> + +<p>相应的, 如果函数 <code>eval</code> 被在严格模式下的<span style="font-family: courier new,andale mono,monospace; line-height: normal;"><code>eval(...)</code>以表达式的形式调用时</span><span style="line-height: 1.5;">, 其代码会被当做严格模式下的代码执行. 当然也可以在代码中显式开启严格模式, 但这样做并不是必须的.</span></p> + +<pre class="brush: js">function strict1(str) { + "use strict"; + return eval(str); // str中的代码在严格模式下运行 +} +function strict2(f, str) { + "use strict"; + return f(str); // 没有直接调用eval(...): 当且仅当str中的代码开启了严格模式时 + // 才会在严格模式下运行 +} +function nonstrict(str) { + return eval(str); // 当且仅当str中的代码开启了"use strict",str中的代码才会在严格模式下运行 +} + +strict1("'Strict mode code!'"); +strict1("'use strict'; 'Strict mode code!'"); +strict2(eval, "'Non-strict code.'"); +strict2(eval, "'use strict'; 'Strict mode code!'"); +nonstrict("'Non-strict code.'"); +nonstrict("'use strict'; 'Strict mode code!'"); +</pre> + +<p>因此,在 eval 执行的严格模式代码下,变量的行为与严格模式下非 eval 执行的代码中的变量相同。</p> + +<p>第三, 严格模式禁止删除声明变量。<code>delete name</code> 在严格模式下会引起语法错误:</p> + +<pre class="brush: js">"use strict"; + +var x; +delete x; // !!! 语法错误 + +eval("var y; delete y;"); // !!! 语法错误 +</pre> + +<h3 id="让eval和arguments变的简单">让<code>eval</code>和<code>arguments</code>变的简单</h3> + +<p>严格模式让<code>arguments</code>和<code>eval</code>少了一些奇怪的行为。两者在通常的代码中都包含了很多奇怪的行为: <code>eval</code>会添加删除绑定,改变绑定好的值,还会通过用它索引过的属性给形参取别名的方式修改形参. 虽然在未来的ECMAScript版本解决这个问题之前,是不会有补丁来完全修复这个问题,但严格模式下将eval和arguments作为关键字对于此问题的解决是很有帮助的。</p> + +<p>第一, 名称 <code>eval</code> 和 <code>arguments</code> 不能通过程序语法被绑定(be bound)或赋值. 以下的所有尝试将引起语法错误:</p> + +<pre class="brush: js">"use strict"; +eval = 17; +arguments++; +++eval; +var obj = { set p(arguments) { } }; +var eval; +try { } catch (arguments) { } +function x(eval) { } +function arguments() { } +var y = function eval() { }; +var f = new Function("arguments", "'use strict'; return 17;"); +</pre> + +<p>第二,严格模式下,参数的值不会随 arguments 对象的值的改变而变化。在正常模式下,对于第一个参数是 arg 的函数,对 arg 赋值时会同时赋值给 arguments[<code>0</code>],反之亦然(除非没有参数,或者 arguments[<code>0</code>] 被删除)。严格模式下,函数的 arguments 对象会保存函数被调用时的原始参数。arguments[i] 的值不会随与之相应的参数的值的改变而变化,同名参数的值也不会随与之相应的 arguments[i] 的值的改变而变化。</p> + +<pre class="brush: js">function f(a) { + "use strict"; + a = 42; + return [a, arguments[0]]; +} +var pair = f(17); +console.assert(pair[0] === 42); +console.assert(pair[1] === 17); +</pre> + +<p>第三,不再支持 <code>arguments.callee</code>。正常模式下,<code>arguments.callee</code> 指向当前正在执行的函数。这个作用很小:直接给执行函数命名就可以了!此外,<code>arguments.callee</code> 十分不利于优化,例如内联函数,因为 <code>arguments.callee</code> 会依赖对非内联函数的引用。在严格模式下,<code>arguments.callee</code> 是一个不可删除属性,而且赋值和读取时都会抛出异常:</p> + +<pre class="brush: js">"use strict"; +var f = function() { return arguments.callee; }; +f(); // 抛出类型错误 +</pre> + +<h3 id="安全的_JavaScript" style="position: static;">"安全的" JavaScript</h3> + +<p>严格模式下更容易写出“安全”的JavaScript。现在有些网站提供了方式给用户编写能够被网站其他用户执行的JavaScript代码。在浏览器环境下,JavaScript能够获取用户的隐私信息,因此这类Javascript必须在运行前部分被转换成需要申请访问禁用功能的权限。没有很多的执行时检查的情况,Javascript的灵活性让它无法有效率地做这件事。一些语言中的函数普遍出现,以至于执行时检查他们会引起严重的性能损耗。做一些在严格模式下发生的小改动,要求用户提交的JavaScript开启严格模式并且用特定的方式调用,就会大大减少在执行时进行检查的必要。</p> + +<p>第一,在严格模式下通过<code>this</code>传递给一个函数的值不会被强制转换为一个对象。对一个普通的函数来说,<code>this</code>总会是一个对象:不管调用时<code>this</code>它本来就是一个对象;还是用布尔值,字符串或者数字调用函数时函数里面被封装成对象的<code>this</code>;还是使用<code>undefined</code>或者<code>null</code>调用函数式<code>this</code>代表的全局对象(使用<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call" title="en-US/JavaScript/Reference/Global_Objects/Function/call"><code>call</code></a>, <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply" title="en-US/JavaScript/Reference/Global_Objects/Function/apply"><code>apply</code></a>或者<code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind" title="en-US/JavaScript/Reference/Global_Objects/Function/bind">bind</a></code>方法来指定一个确定的<code>this</code>)。这种自动转化为对象的过程不仅是一种性能上的损耗,同时在浏览器中暴露出全局对象也会成为安全隐患,因为全局对象提供了访问那些所谓安全的JavaScript环境必须限制的功能的途径。所以对于一个开启严格模式的函数,指定的<code>this</code>不再被封装为对象,而且如果没有指定<code>this</code>的话它值是<code>undefined</code>:</p> + +<pre class="brush: js">"use strict"; +function fun() { return this; } +console.assert(fun() === undefined); +console.assert(fun.call(2) === 2); +console.assert(fun.apply(null) === null); +console.assert(fun.call(undefined) === undefined); +console.assert(fun.bind(true)() === true); +</pre> + +<p>第二,在严格模式中再也不能通过广泛实现的ECMAScript扩展“游走于”JavaScript的栈中。在普通模式下用这些扩展的话,当一个叫<code>fun</code>的函数正在被调用的时候,<code>fun.caller</code>是最后一个调用<code>fun</code>的函数,而且<code>fun.arguments</code>包含调用<code>fun</code>时用的形参。这两个扩展接口对于“安全”JavaScript而言都是有问题的,因为他们允许“安全的”代码访问"专有"函数和他们的(通常是没有经过保护的)形参。如果<code>fun</code>在严格模式下,那么<code>fun.caller</code>和<code>fun.arguments</code>都是不可删除的属性而且在存值、取值时都会报错:</p> + +<pre class="brush: js">function restricted() { + "use strict"; + restricted.caller; // 抛出类型错误 + restricted.arguments; // 抛出类型错误 +} + +function privilegedInvoker() { + return restricted(); +} + +privilegedInvoker(); +</pre> + +<p>第三,严格模式下的<code>arguments</code>不会再提供访问与调用这个函数相关的变量的途径。在一些旧时的ECMAScript实现中<code>arguments.caller</code>曾经是一个对象,里面存储的属性指向那个函数的变量。这是一个<a href="http://stuff.mit.edu/iap/2008/facebook/">安全隐患</a>,因为它通过函数抽象打破了本来被隐藏起来的保留值;它同时也是引起大量优化工作的原因。出于这些原因,现在的浏览器没有实现它。但是因为它这种历史遗留的功能,<code>arguments.caller</code>在严格模式下同样是一个不可被删除的属性,在赋值或者取值时会报错:</p> + +<pre class="brush: js">"use strict"; +function fun(a, b) { + "use strict"; + var v = 12; + return arguments.caller; // 抛出类型错误 +} +fun(1, 2); // 不会暴露v(或者a,或者b) +</pre> + +<h3 id="为未来的ECMAScript版本铺平道路" style="position: relative;">为未来的ECMAScript版本铺平道路</h3> + +<p>未来版本的ECMAScript很有可能会引入新语法,ECMAScript5中的严格模式就提早设置了一些限制来减轻之后版本改变产生的影响。如果提早使用了严格模式中的保护机制,那么做出改变就会变得更容易。</p> + +<p>第一,在严格模式中一部分字符变成了保留的关键字。这些字符包括<code>implements</code>, <code>interface</code>, <code>let</code>, <code>package</code>, <code>private</code>, <code>protected</code>, <code>public</code>, <code>static</code>和<code>yield</code>。在严格模式下,你不能再用这些名字作为变量名或者形参名。</p> + +<pre class="brush: js">function package(protected) { // !!! + "use strict"; + var implements; // !!! + + interface: // !!! + while (true) { + break interface; // !!! + } + + function private() { } // !!! +} +function fun(static) { 'use strict'; } // !!! +</pre> + +<p>两个针对Mozilla开发的警告:第一,如果你的JavaScript版本在1.7及以上(你的chrome代码或者你正确使用了<code><script type=""></code>)并且开启了严格模式的话,因为<code>let</code>和<code>yield</code>是最先引入的关键字,所以它们会起作用。但是网络上用<code><script src=""></code>或者<code><script>...</script></code>加载的代码,<code>let</code>或者<code>yield</code>都不会作为关键字起作用;第二,尽管ES5无条件的保留了<code>class</code>, <code>enum</code>, <code>export</code>, <code>extends</code>, <code>import</code>和<code>super</code>关键字,在Firefox 5之前,Mozilla仅仅在严格模式中保留了它们。</p> + +<p>第二,<a href="http://whereswalden.com/2011/01/24/new-es5-strict-mode-requirement-function-statements-not-at-top-level-of-a-program-or-function-are-prohibited/">严格模式禁止了不在脚本或者函数层面上的函数声明</a>。在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。这并不在ES5规范中(甚至是ES3)!这是一种针对不同浏览器中不同语义的一种延伸。未来的ECMAScript版本很有希望制定一个新的,针对不在脚本或者函数层面进行函数声明的语法。<a href="http://wiki.ecmascript.org/doku.php?id=conventions:no_non_standard_strict_decls">在严格模式下禁止这样的函数声明</a>对于将来ECMAScript版本的推出扫清了障碍:</p> + +<pre class="brush: js">"use strict"; +if (true) { + function f() { } // !!! 语法错误 + f(); +} + +for (var i = 0; i < 5; i++) { + function f2() { } // !!! 语法错误 + f2(); +} + +function baz() { // 合法 + function eit() { } // 同样合法 +} +</pre> + +<p>这种禁止放到严格模式中并不是很合适,因为这样的函数声明方式从ES5中延伸出来的。但这是ECMAScript委员会推荐的做法,浏览器就实现了这一点。</p> + +<h2 id="浏览器的严格模式">浏览器的严格模式</h2> + +<p>主流浏览器现在实现了严格模式。但是不要盲目的依赖它,因为市场上仍然有大量的浏览器版本只部分支持严格模式或者根本就不支持(比如IE10之前的版本)。<em>严格模式改变了语义。</em>依赖这些改变可能会导致没有实现严格模式的浏览器中出现问题或者错误。谨慎地使用严格模式,通过检测相关代码的功能保证严格模式不出问题。最后,记得<em>在支持或者不支持严格模式的浏览器中测试你的代码</em>。如果你只在不支持严格模式的浏览器中测试,那么在支持的浏览器中就很有可能出问题,反之亦然。</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>{{SpecName('ES5.1', '#sec-10.1.1', 'Strict Mode Code')}}</td> + <td>{{Spec2('ES5.1')}}</td> + <td>Initial definition. See also: <a href="http://www.ecma-international.org/ecma-262/5.1/#sec-C">Strict mode restriction and exceptions</a></td> + </tr> + <tr> + <td>{{SpecName('ES6', '#sec-strict-mode-code', 'Strict Mode Code')}}</td> + <td>{{Spec2('ES6')}}</td> + <td><a href="http://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-of-ecmascript">Strict mode restriction and exceptions</a></td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-strict-mode-code', 'Strict Mode Code')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td><a href="https://tc39.github.io/ecma262/#sec-strict-mode-of-ecmascript">Strict mode restriction and exceptions</a></td> + </tr> + </tbody> +</table> + +<h2 id="相关链接">相关链接</h2> + +<ul> + <li><a class="external" href="http://whereswalden.com/2010/09/08/new-es5-strict-mode-support-now-with-poison-pills/" title="http://whereswalden.com/2010/09/08/new-es5-strict-mode-support-now-with-poison-pills/">Where's Walden? » New ES5 strict mode support: now with poison pills!</a></li> + <li><a class="external" href="http://whereswalden.com/2011/01/24/new-es5-strict-mode-requirement-function-statements-not-at-top-level-of-a-program-or-function-are-prohibited/" title="http://whereswalden.com/2011/01/24/new-es5-strict-mode-requirement-function-statements-not-at-top-level-of-a-program-or-function-are-prohibited/">Where's Walden? » New ES5 strict mode requirement: function statements not at top level of a program or function are prohibited</a></li> + <li><a class="external" href="http://whereswalden.com/2011/01/10/new-es5-strict-mode-support-new-vars-created-by-strict-mode-eval-code-are-local-to-that-code-only/" title="http://whereswalden.com/2011/01/10/new-es5-strict-mode-support-new-vars-created-by-strict-mode-eval-code-are-local-to-that-code-only/">Where's Walden? » New ES5 strict mode support: new vars created by strict mode eval code are local to that code only</a></li> + <li><a href="http://qnimate.com/javascript-strict-mode-in-nutshell/">JavaScript "use strict" tutorial for beginners.</a></li> + <li><a class="external" href="http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/" title="http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/">John Resig - ECMAScript 5 Strict Mode, JSON, and More</a></li> + <li><a class="external" href="http://dmitrysoshnikov.com/ecmascript/es5-chapter-2-strict-mode/">ECMA-262-5 in detail. Chapter 2. Strict Mode.</a></li> + <li><a class="external" href="http://kangax.github.com/es5-compat-table/strict-mode/">Strict mode compatibility table</a></li> + <li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode">Transitioning to strict mode</a></li> +</ul> diff --git a/files/zh-cn/web/javascript/reference/strict_mode/transitioning_to_strict_mode/index.html b/files/zh-cn/web/javascript/reference/strict_mode/transitioning_to_strict_mode/index.html new file mode 100644 index 0000000000..8973075e47 --- /dev/null +++ b/files/zh-cn/web/javascript/reference/strict_mode/transitioning_to_strict_mode/index.html @@ -0,0 +1,128 @@ +--- +title: 向严格模式过渡 +slug: Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode +translation_of: Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode +--- +<p>{{jsSidebar("More")}}</p> + +<p>ECMAScript 5 引入了 <a href="/zh-CN/docs/JavaScript/Strict_mode" title="/zh-CN/docs/JavaScript/Strict_mode">strict mode</a> ,现在已经被大多浏览器实现(包括IE10. 会使web浏览器更容易的解析代码(只需要添加 <code>"use strict";</code> 在源码的最上面), 由现有的代码到严格模式的过渡需要一些事做.</p> + +<p>该文章旨在为开发者提供指南.</p> + +<h2 id="逐步过渡">逐步过渡</h2> + +<p>严格模式被仔细设计过,因此可以逐渐地进行迁移。你可以分别改变各个文件,甚至以函数级的粒度迁移至严格模式。</p> + +<h2 id="非严格模式到严格模式的区别">非严格模式到严格模式的区别</h2> + +<h3 id="语法错误">语法错误</h3> + +<p>如果代码中使用<code>"use strict"开启了严格模式</code>,则下面的情况都会在脚本运行之前抛出<a href="/zh-CN/docs/Core_JavaScript_1.5_Guide/SyntaxError" title="/zh-CN/docs/Core_JavaScript_1.5_Guide/SyntaxError">SyntaxError</a>异常:</p> + +<ul> + <li>八进制语法<code>:var n = 023和var s = "\047"</code></li> + <li><a class="new" href="https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Statements/with" title="/zh-CN/docs/JavaScript/Reference/Statements/with"><code>with</code></a>语句</li> + <li>使用<a href="https://developer.mozilla.org/zh-CN/docs/JavaScript/Reference/Operators/delete" title="/zh-CN/docs/JavaScript/Reference/Operators/delete">delete</a>删除一个变量名(而不是属性名)<code>:delete myVariable</code></li> + <li>使用<code>eval</code>或<code>arguments</code>作为变量名或函数名</li> + <li>使用未来保留字(也许会在ECMAScript 6中使用):<code>implements</code>, <code>interface</code>, <code>let</code>, <code>package</code>, <code>private</code>, <code>protected</code>, <code>public</code>, <code>static</code>,和<code>yield</code>作为变量名或函数名</li> + <li>在语句块中使用函数声明:<code>if(a<b){ function f(){} }</code></li> + <li>其他错误 + <ul> + <li>对象字面量中使用两个相同的属性名:<code>{a: 1, b: 3, a: 7}</code></li> + <li>函数形参中使用两个相同的参数名:<code>function f(a, b, b){}</code></li> + </ul> + </li> +</ul> + +<p>这些错误是有利的,因为可以揭示简陋的错误和坏的实践,这些错误会在代码运行前被抛出</p> + +<h3 id="新的运行时错误">新的运行时错误</h3> + +<p>JavaScript曾经会在一些上下文的某些情况中静默的失败,严格模式会在这些情况下抛出错误。如果你的代码包含这样的场景,请务必测试以确保没有代码受到影响。再说一次,严格模式是可以设置在代码粒度下的。</p> + +<h4 id="给一个未声明的变量赋值">给一个未声明的变量赋值</h4> + +<pre class="brush: js">function f(x){ + "use strict"; + var a = 12; + b = a + x*35; // error! +} +f(); +</pre> + +<p>改变一个全局对象的值可能会造成不可预期的后果。如果你真的想设置一个全局对象的值,把他作为一个参数并且明确的把它作为一个属性:</p> + +<pre class="brush: js">var global = this; // in the top-level context, "this" always refers the global object +function f(){ + "use strict"; + var a = 12; + global.b = a + x*35; +} +f(); +</pre> + +<h4 id="尝试删除一个不可配置的属性">尝试删除一个不可配置的属性</h4> + +<pre class="brush: js">"use strict"; +delete Object.prototype; // error! +</pre> + +<p>在非严格模式中,这样的代码只会静默失败,这样可能会导致用户误以为删除操作成功了.</p> + +<h4 id="arguments对象和函数属性">arguments对象和函数属性</h4> + +<p>在严格模式下,访问<code>arguments.callee</code>, <code>arguments.caller</code>, <code>anyFunction.caller</code>以及<code>anyFunction.arguments</code>都会抛出异常.唯一合法的使用应该是在其中命名一个函数并且重用之</p> + +<pre class="brush: js">// example taken from vanillajs: http://vanilla-js.com/ +var s = document.getElementById('thing').style; +s.opacity = 1; +(function(){ + if((s.opacity-=.1) < 0) + s.display="none"; + else + setTimeout(arguments.callee, 40); +})();</pre> + +<p>可以重新写成:</p> + +<pre class="brush: js">"use strict"; +var s = document.getElementById('thing').style; +s.opacity = 1; +(function fadeOut(){ // name the function + if((s.opacity-=.1) < 0) + s.display="none"; + else + setTimeout(fadeOut, 40); // use the name of the function +})();</pre> + +<h3 id="语义差异">语义差异</h3> + +<p>这些差异都是一些微小的差异。有可能单元测试没办法捕获这种微小的差异。你很有必要去小心地审查你的代码,来确保这些差异不会影响你代码的语义。幸运的是,这种小心地代码审查可以逐函数地完成。</p> + +<h4 id="函数调用中的this">函数调用中的<code>this</code></h4> + +<p>在普通的函数调用<code>f()中</code>,<code>this</code>的值会指向全局对象.在严格模式中,<code>this</code>的值会指向<code>undefined</code>.当函数通过<a href="/zh-CN/docs/Core_JavaScript_1.5_Reference/Global_Objects/Function/call" title="/zh-CN/docs/Core_JavaScript_1.5_Reference/Global_Objects/Function/call">call</a>和<a href="/zh-CN/docs/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply" title="/zh-CN/docs/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply">apply</a>调用时,如果传入的<code>thisvalue</code>参数是一个<code>null</code>和<code>undefined</code>除外的原始值(字符串,数字,布尔值),则<code>this的值会成为那个原始值对应的包装对象</code>,如果<code>thisvalue</code>参数的值是<code>undefined</code>或<code>null</code>,则<code>this的值会指向全局对象</code>.在严格模式中,<code>this的值就是</code><code>thisvalue</code>参数的值,没有任何类型转换.</p> + +<h4 id="arguments对象属性不与对应的形参变量同步更新"><code>arguments</code>对象属性不与对应的形参变量同步更新</h4> + +<p>在非严格模式中,修改<code>arguments</code>对象中某个索引属性的值,和这个属性对应的形参变量的值也会同时变化,反之亦然.这会让JavaScript的代码混淆引擎让代码变得更难读和理解。在严格模式中<code><span style="font-family: courier,andale mono,monospace;">arguments 对象会以形参变量的拷贝的形式被创建和初始化,因此 arguments 对象的改变不会影响形参。</span></code></p> + +<h4 id="eval相关的区别"><code>eval相关的区别</code></h4> + +<p>在严格模式中,<code>eval</code>不会在当前的作用域内创建新的变量.另外,传入eval的字符串参数也会按照严格模式来解析.你需要全面测试来确保没有代码收到影响。另外,<span style="font-family: courier,andale mono,monospace;">如果你并不是为了解决一个非常实际的解决方案中,</span>尽量不要使用<code>eval。</code></p> + +<h2 id="严格中立的代码">严格中立的代码</h2> + +<p>迁移严格代码至严格模式的一个潜在消极面是,在遗留的老版本浏览器上,由于没有实现严格模式,javascript语义可能会有所不同。在一些罕见的机会下(比如差劲的关联关系或者代码最小化),你的代码可能不能按照你书写或者测试里的模式那样运行。这里有一些让你的代码保持中立的规范:</p> + +<ol> + <li>按照严格模式书写你的代码,并且确保你的代码不会发生仅仅在严格模式下发生的错误(比如上文所说的<a href="#">运行时错误</a>)</li> + <li>远离语义差异 + <ol> + <li><code>eval</code>: 仅仅在你知道你在干什么的情况下使用它</li> + <li><code>arguments</code>: 总是通过形参的名字获取函数参数,或者在函数的第一行拷贝arguments <br> + <code>var args = Array.prototype.slice.call(arguments)</code></li> + <li><code>this</code>: 仅在this指向你自己创建的对象时使用它 </li> + </ol> + </li> +</ol> |