作者:kurtshen
译自The ECMAScript 2016 change you probably don't know, Posted at October 18, 2016 by Nicholas C. Zakas.
与ECMAScript 6(也称为ECMAScript 2015)相比,ECMAScript 2016是对JavaScript语言规范的一个小更新。 这是由于ECMAScript版本现在决定将修订发布的周期变为每年更新,实际上只是已准备好的所有功能的快照。因此,大多数资源仅列出ECMAScript 2016中的两个显着变化:
- 1.添加乘幂(
**
)运算符 - 2.添加Array.prototype.includes()方法
这些功能对JavaScript开发人员具有最直接的影响,但是,还有一个常常被遗忘的重大变化。 这是我在我的书《Understanding ECMAScript 6》中所提到的,但是,我仍然会收到关于它的问题,所以我想深挖一下这个问题。
首先,我将描述变化的内容,然后我将说明变化的内容背后的理由。
变化
ECMAScript 2016说,“use strict”
指令不能用于其参数具有默认值的函数的正文中,使用解构或者rest参数。 规范将简单参数定义为仅包含标识符的参数列表(ECMAScript 5仅支持简单参数列表)[1]。 该更改会影响所有函数类型,包括函数声明和表达式,箭头函数和简明对象字面值方法。例如:
// 可以使用
function doSomething(a, b) {
"use strict";
// code
}
// 在ECMAScript 2016中为语法错误
function doSomething(a, b=a) {
"use strict";
// code
}
// 在ECMAScript 2016中为语法错误
const doSomething = function({a, b}) {
"use strict";
// code
};
// 在ECMAScript 2016中为语法错误
const doSomething = (...a) => {
"use strict";
// code
};
const obj = {
// 在ECMAScript 2016中为语法错误
doSomething({a, b}) {
"use strict";
// code
}
};
你仍可在函数之外全局使用“use strict”
,以便该函数在严格模式下运行,即使该函数具有非简单的参数。 例如:
// 可以使用
"use strict";
function doSomething(a, b=a) {
// code
}
在这种情况下,函数之外的“use strict”
指令是有效的语法。 如果你使用ECMAScript模块,这也不是一个问题,它以严格模式运行所有代码。
为什么要有此变化?
由于严格模式和非简单参数列表的工作方式,此更改很重要。当在ECMAScript 5中创建严格模式时,解构和缺省参数值不存在,因此解析参数列表并查看“use strict”
指令没有问题。在这一点上,“use strict”
不能影响解析参数列表的结果,它只用于验证参数标识符(不允许重复和检查禁用的标识符,如eval和arguments)。然而,随着在ECMAScript 6中引入解构和默认参数值,情况已经不再是这样,因为规范指出参数列表应该按照与函数体相同的模式进行解析(这意味着“use strict”
指令在函数体必须触发严格模式)。
首先要意识到的是严格模式需要更改JavaScript代码的解析和执行[2]。作为一个非常简单的例子,strict模式不允许使用旧式八进制数字文字(例如070
)。如果代码在严格模式下解析,则070
将抛出语法错误。考虑到这一点,你认为以下代码应该做什么?
// 在ECMAScript 2016中为语法错误
function doSomething(value=070) {
"use strict";
return value;
}
如果一个JavaScript解析器试图解析此代码,参数列表将会在函数体之前被解析。这意味着070
被解析为有效,然后在函数体中遇到“use strict”
,它告诉解析器,“实际上,你应该在严格模式下解析参数列表”。 在这一点上,解析器将必须在严格模式下回溯并重新解析参数列表,所以为070
抛出语法错误。这可能不是一个大问题,但如果默认参数值更复杂怎么办?
// 在ECMAScript 2016中为语法错误
function doSomething(value=(function() {
return doSomeCalculation() + 070;
}())) {
"use strict";
return value;
}
在这种情况下,使用默认参数值中使用的函数,你会有更多的问题。为了在严格模式下运行,使得必须展开的token数量更多,还必须将该函数设置为默认值。 为了确保默认参数值表达式被正确解析,并理解为运行在严格模式,将变得十分复杂。
解构参数也会导致类似的问题,因为它们可以包含默认值。 例如:
// 在ECMAScript 2016中为语法错误
function doSomething({value=070}) {
"use strict";
return value;
}
这里,解构参数值具有在严格模式下不允许的默认值,导致与默认参数值相同的问题。
最后,TC-39决定[3]对于这种在ECMAScript 5中不存在问题的情景中,简单地禁止函数体使用“use strict”
,以避免丢失边缘情况。 这意味着具有默认参数值,解构参数或rest参数的函数在函数体中不能有“use strict”
。 这包括“use strict”
没有效果的情况,例如:
function outer() {
"use strict";
// 在ECMAScript 2016中为语法错误
function doSomething(value=070) {
"use strict";
return value;
}
}
此示例将具有非简单参数的函数嵌套在具有“use strict”
的另一个函数中。 doSomething()
函数自动处于严格模式,但JavaScript引擎仍会在doSomething()
的函数体中的“use strict”
指令上抛出语法错误。
解决方法
这种变化不太可能影响许多开发人员,这可能是为什么你不知道它。“use strict”
指令开始沦为JavaScript的历史文物,因为ECMAScript模块和类都会以严格模式自动运行,而无需选择退出,这意味着在这些情况下不需要使用“use strict”
。 但是,在极少数情况下,你需要一个带有非简单参数的函数在严格模式下运行,你可以使用IIFE立即执行函数的形式创建函数:
const doSomething = (function() {
"use strict";
return function(value=42) {
return value;
};
}());
在此代码中,在以严格模式运行的IIFE中创建函数。 这允许返回的函数在使用默认参数值的情况下以严格模式运行。 因为外部作用域以严格模式运行,所以毫无疑问可以正确解析默认参数值,并且不需要在函数体内额外添加“use strict”
。
总结
这个对ECMAScript 2016的小改变,不允许函数体使用非简单参数列表的函数“use strict”
,突显了这样一个流行语言在演进过程中的困难重重。 在这种情况下,TC-39决定通过引入一个新的语法错误消除歧义,如果这个问题早点出现,便可能是ECMAScript 6(2015)的一部分。 添加这个语法错误是最显著有效的方式,因为它影响非常少的现有代码(规范更改是在JavaScript引擎实现非简单参数列表的同时进行的),并且可能不会影响很多未来代码,因为ECMAScript模块和类以严格模式运行。
引用
- 1.Static Semantics: IsSimpleParameterList (ecma-international.org)
- 2.It's time to start using JavaScript strict mode (nczonline.net)
- 3.The scope of "use strict" with respect to destructuring in parameter lists
相关推荐
ECMAScript 2015 (ES6) in Node.js(译)
[译]Top JavaScript Frameworks & Topics to Learn in 2017
React展示组件与容器组件(英译)