严格模式

注意:有时你会看到默认的非严格模式,称为 草率模式。这不是官方术语,但请注意这一点,以防万一。

¥Note: Sometimes you'll see the default, non-strict mode referred to as sloppy mode. This isn't an official term, but be aware of it, just in case.

JavaScript 的严格模式是一种选择 JavaScript 受限制变体的方法,从而隐式选择退出“草率模式”。严格模式不仅仅是一个子集:它故意具有与正常代码不同的语义。不支持严格模式的浏览器将运行严格模式代码,其行为与支持严格模式的浏览器不同,因此,不要在没有功能测试的情况下依赖严格模式来支持严格模式的相关方面。严格模式代码和非严格模式代码可以共存,因此脚本可以逐步选择进入严格模式。

¥JavaScript's strict mode is a way to opt in to a restricted variant of JavaScript, thereby implicitly opting-out of "sloppy mode". Strict mode isn't just a subset: it intentionally has different semantics from normal code. Browsers not supporting strict mode will run strict mode code with different behavior from browsers that do, so don't rely on strict mode without feature-testing for support for the relevant aspects of strict mode. Strict mode code and non-strict mode code can coexist, so scripts can opt into strict mode incrementally.

严格模式对正常 JavaScript 语义进行了一些更改:

¥Strict mode makes several changes to normal JavaScript semantics:

  1. 通过将某些 JavaScript 无提示错误更改为引发错误,消除了这些错误。
  2. 修复了导致 JavaScript 引擎难以执行优化的错误:有时可以使严格模式代码比非严格模式的相同代码运行得更快。
  3. 禁止 ECMAScript 未来版本中可能定义的某些语法。

调用严格模式

¥Invoking strict mode

严格模式适用于整个脚本或单个函数。它不适用于 {} 大括号内的 块语句;试图将其应用到这样的环境中没有任何作用。eval 代码、Function 代码、事件处理程序 属性、传递给 setTimeout() 的字符串以及相关函数要么是函数体,要么是整个脚本,在其中调用严格模式可以按预期工作。

¥Strict mode applies to entire scripts or to individual functions. It doesn't apply to block statements enclosed in {} braces; attempting to apply it to such contexts does nothing. eval code, Function code, event handler attributes, strings passed to setTimeout(), and related functions are either function bodies or entire scripts, and invoking strict mode in them works as expected.

脚本的严格模式

¥Strict mode for scripts

要为整个脚本调用严格模式,请将确切的语句 "use strict";(或 'use strict';)放在任何其他语句之前。

¥To invoke strict mode for an entire script, put the exact statement "use strict"; (or 'use strict';) before any other statements.

js
// Whole-script strict mode syntax
"use strict";
const v = "Hi! I'm a strict mode script!";

函数的严格模式

¥Strict mode for functions

同样,要为函数调用严格模式,请将确切的语句 "use strict";(或 'use strict';)放入函数体中的任何其他语句之前。

¥Likewise, to invoke strict mode for a function, put the exact statement "use strict"; (or 'use strict';) in the function's body before any other statements.

js
function myStrictFunction() {
  // Function-level strict mode syntax
  "use strict";
  function nested() {
    return "And so am I!";
  }
  return `Hi! I'm a strict mode function! ${nested()}`;
}
function myNotStrictFunction() {
  return "I'm not strict.";
}

"use strict" 指令只能应用于具有简单参数的函数体。在带有 restdefaultdestructured 参数的函数中使用 "use strict"语法错误

¥The "use strict" directive can only be applied to the body of functions with simple parameters. Using "use strict" in functions with rest, default, or destructured parameters is a syntax error.

js
function sum(a = 1, b = 2) {
  // SyntaxError: "use strict" not allowed in function with default parameter
  "use strict";
  return a + b;
}

模块的严格模式

¥Strict mode for modules

JavaScript 模块 的全部内容自动处于严格模式,无需任何语句来启动它。

¥The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.

js
function myStrictFunction() {
  // because this is a module, I'm strict by default
}
export default myStrictFunction;

类的严格模式

¥Strict mode for classes

class 主体的所有部分都是严格模式代码,包括 类声明类表达式

¥All parts of a class's body are strict mode code, including both class declarations and class expressions.

js
class C1 {
  // All code here is evaluated in strict mode
  test() {
    delete Object.prototype;
  }
}
new C1().test(); // TypeError, because test() is in strict mode

const C2 = class {
  // All code here is evaluated in strict mode
};

// Code here may not be in strict mode
delete Object.prototype; // Will not throw error

严格模式的变化

¥Changes in strict mode

严格模式会更改语法和运行时行为。变更通常分为以下几类:

¥Strict mode changes both syntax and runtime behavior. Changes generally fall into these categories:

  • 将错误转换为错误的更改(作为语法错误或在运行时)
  • 更改简化了变量引用的解析方式
  • 简化 evalarguments 的更改
  • 使编写 "secure" JavaScript 变得更容易的更改
  • 预期未来 ECMAScript 演变的变化。

将错误转化为错误

¥Converting mistakes into errors

严格模式将一些以前接受的错误更改为错误。JavaScript 被设计为对于新手开发者来说很容易,有时它给应该是错误的操作提供非错误语义。有时这可以解决眼前的问题,但有时这会在未来造成更严重的问题。严格模式将这些错误视为错误,以便发现并及时修复。

¥Strict mode changes some previously-accepted mistakes into errors. JavaScript was designed to be easy for novice developers, and sometimes it gives operations which should be errors non-error semantics. Sometimes this fixes the immediate problem, but sometimes this creates worse problems in the future. Strict mode treats these mistakes as errors so that they're discovered and promptly fixed.

分配给未声明的变量

¥Assigning to undeclared variables

严格模式使得不可能意外创建全局变量。在草率模式下,在赋值中错误输入变量会在全局对象上创建一个新属性,并继续到 "work"。在严格模式下,意外创建全局变量的赋值会引发错误:

¥Strict mode makes it impossible to accidentally create global variables. In sloppy mode, mistyping a variable in an assignment creates a new property on the global object and continues to "work". Assignments which would accidentally create global variables throw an error in strict mode:

js
"use strict";
let mistypeVariable;

// Assuming no global variable mistypeVarible exists
// this line throws a ReferenceError due to the
// misspelling of "mistypeVariable" (lack of an "a")
mistypeVarible = 17;

无法分配给对象属性

¥Failing to assign to object properties

严格模式进行分配,否则会默默地失败并抛出异常。属性分配失败有以下三种方式:

¥Strict mode makes assignments which would otherwise silently fail to throw an exception. There are three ways to fail a property assignment:

  • 分配给不可写的数据属性
  • 分配给仅 getter 访问器属性
  • 分配给 non-extensible 对象上的新属性

例如,NaN 是不可写的全局变量。在草率模式下,分配给 NaN 不会执行任何操作;开发者没有收到任何失败反馈。在严格模式下,分配给 NaN 会引发异常。

¥For example, NaN is a non-writable global variable. In sloppy mode, assigning to NaN does nothing; the developer receives no failure feedback. In strict mode, assigning to NaN throws an exception.

js
"use strict";

// Assignment to a non-writable global
undefined = 5; // TypeError
Infinity = 5; // TypeError

// Assignment to a non-writable property
const obj1 = {};
Object.defineProperty(obj1, "x", { value: 42, writable: false });
obj1.x = 9; // TypeError

// Assignment to a getter-only property
const obj2 = {
  get x() {
    return 17;
  },
};
obj2.x = 5; // TypeError

// Assignment to a new property on a non-extensible object
const fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai"; // TypeError

无法删除对象属性

¥Failing to delete object properties

尝试 delete 不可配置或不可删除(例如,它被代理的 deleteProperty 处理程序拦截,返回 false)属性在严格模式下抛出(在此之前尝试不会产生任何效果):

¥Attempts to delete a non-configurable or otherwise undeletable (e.g. it's intercepted by a proxy's deleteProperty handler which returns false) property throw in strict mode (where before the attempt would have no effect):

js
"use strict";
delete Object.prototype; // TypeError
delete [].length; // TypeError

严格模式还禁止删除普通名称。严格模式下的 delete name 是语法错误:

¥Strict mode also forbids deleting plain names. delete name in strict mode is a syntax error:

js
"use strict";

var x;
delete x; // syntax error

如果该名称是可配置的全局属性,则在其前面加上 globalThis 前缀即可将其删除。

¥If the name is a configurable global property, prefix it with globalThis to delete it.

js
"use strict";

delete globalThis.x;

重复的参数名称

¥Duplicate parameter names

严格模式要求函数参数名称是唯一的。在草率模式下,最后一个重复的参数会隐藏之前的同名参数。以前的那些参数在 arguments 中仍然可用,因此它们并非完全无法访问。尽管如此,这种隐藏意义不大,而且可能是不可取的(例如,它可能隐藏拼写错误),因此在严格模式下,重复的参数名称是语法错误:

¥Strict mode requires that function parameter names be unique. In sloppy mode, the last duplicated argument hides previous identically-named arguments. Those previous arguments remain available through arguments, so they're not completely inaccessible. Still, this hiding makes little sense and is probably undesirable (it might hide a typo, for example), so in strict mode, duplicate argument names are a syntax error:

js
function sum(a, a, c) {
  // syntax error
  "use strict";
  return a + a + c; // wrong if this code ran
}

如果函数具有默认参数、剩余参数或解构参数,则在非严格模式下具有重复的参数名称也是一种语法错误。

¥It is also a syntax error in non-strict mode to have duplicate parameter names, if the function has a default parameter, rest parameter, or destructured parameter.

旧的八进制文字

¥Legacy octal literals

严格模式 禁止 0 前缀八进制文字。在 sloppy 模式下,如果所有数字都小于 8,则以 0 开头的数字(例如 0644)将被解释为八进制数 (0644 === 420)。新手开发者有时认为前导零前缀没有语义意义,因此他们可能会将其用作对齐设备 - 但这会改变数字的含义!八进制的前导零语法很少有用,并且可能会被错误使用,因此严格模式使其成为语法错误:

¥Strict mode forbids a 0-prefixed octal literal. In sloppy mode, a number beginning with a 0, such as 0644, is interpreted as an octal number (0644 === 420), if all digits are smaller than 8. Novice developers sometimes believe a leading-zero prefix has no semantic meaning, so they might use it as an alignment device — but this changes the number's meaning! A leading-zero syntax for the octal is rarely useful and can be mistakenly used, so strict mode makes it a syntax error:

js
"use strict";
const sum =
  015 + // syntax error
  197 +
  142;

表示八进制文字的标准化方法是通过 0o 前缀。例如:

¥The standardized way to denote octal literals is via the 0o prefix. For example:

js
const sumWithOctal = 0o10 + 8;
console.log(sumWithOctal); // 16

八进制转义序列,例如 "\45" 等于 "%",可用于通过八进制的扩展 ASCII 字符代码数字来表示字符。在严格模式下,这是一个 语法错误。更正式地说,不允许 \ 后跟除 0\0 之外的任何十进制数字;例如 \9\07

¥Octal escape sequences, such as "\45", which is equal to "%", can be used to represent characters by extended-ASCII character code numbers in octal. In strict mode, this is a syntax error. More formally, it's disallowed to have \ followed by any decimal digit other than 0, or \0 followed by a decimal digit; for example \9 and \07.

设置原始值的属性

¥Setting properties on primitive values

严格模式禁止设置 primitive 值的属性。访问原语上的属性会隐式创建一个不可观察的封装对象,因此在草率模式下,设置属性将被忽略(无操作)。在严格模式下,会抛出 TypeError

¥Strict mode forbids setting properties on primitive values. Accessing a property on a primitive implicitly creates a wrapper object that's unobservable, so in sloppy mode, setting properties is ignored (no-op). In strict mode, a TypeError is thrown.

js
"use strict";

false.true = ""; // TypeError
(14).sailing = "home"; // TypeError
"with".you = "far away"; // TypeError

重复的属性名称

¥Duplicate property names

重复的属性名称过去在严格模式下被视为 SyntaxError。随着 计算属性名称 的引入,使得运行时复制成为可能,这个限制在 ES2015 中被删除。

¥Duplicate property names used to be considered a SyntaxError in strict mode. With the introduction of computed property names, making duplication possible at runtime, this restriction was removed in ES2015.

js
"use strict";
const o = { p: 1, p: 2 }; // syntax error prior to ECMAScript 2015

注意:使曾经出错的代码变成无错误始终被认为是向后兼容的。这是该语言严格抛出错误的一个很好的部分:它为未来的语义变化留下了空间。

¥Note: Making code that used to error become non-errors is always considered backwards-compatible. This is a good part of the language being strict about throwing errors: it leaves room for future semantic changes.

简化范围管理

¥Simplifying scope management

严格模式简化了变量名称映射到代码中特定变量定义的方式。许多编译器优化依赖于将变量 X 存储在该位置的能力:这对于全面优化 JavaScript 代码至关重要。JavaScript 有时会使代码中的名称到变量定义的基本映射在运行时之前无法执行。严格模式消除了大多数发生这种情况的情况,因此编译器可以更好地优化严格模式代码。

¥Strict mode simplifies how variable names map to particular variable definitions in the code. Many compiler optimizations rely on the ability to say that variable X is stored in that location: this is critical to fully optimizing JavaScript code. JavaScript sometimes makes this basic mapping of name to variable definition in the code impossible to perform until runtime. Strict mode removes most cases where this happens, so the compiler can better optimize strict mode code.

删除 with 语句

¥Removal of the with statement

严格模式禁止 withwith 的问题是,块内的任何名称都可能在运行时映射到传递给它的对象的属性,或者映射到周围(甚至全局)范围内的变量;事先不可能知道是哪一个。严格模式使 with 成为语法错误,因此 with 中的名称不可能在运行时引用未知位置:

¥Strict mode prohibits with. The problem with with is that any name inside the block might map either to a property of the object passed to it, or to a variable in surrounding (or even global) scope, at runtime; it's impossible to know which beforehand. Strict mode makes with a syntax error, so there's no chance for a name in a with to refer to an unknown location at runtime:

js
"use strict";
const x = 17;
with (obj) {
  // Syntax error
  // If this weren't strict mode, would this be const x, or
  // would it instead be obj.x? It's impossible in general
  // to say without running the code, so the name can't be
  // optimized.
  x;
}

将对象分配给一个短名称变量,然后访问该变量的相应属性的简单替代方案可以随时替换 with

¥The simple alternative of assigning the object to a short name variable, then accessing the corresponding property on that variable, stands ready to replace with.

无泄漏评估

¥Non-leaking eval

在严格模式下,eval 不会向周围范围引入新变量。在草率模式下,eval("var x;") 将变量 x 引入到周围函数或全局作用域中。这意味着,一般来说,在包含对 eval 的调用的函数中,每个不引用参数或局部变量的名称都必须在运行时映射到特定定义(因为 eval 可能引入了一个新变量,该变量将隐藏外部变量) 多变的)。在严格模式下,eval 仅为正在评估的代码创建变量,因此 eval 不能影响名称是否引用外部变量或某些局部变量:

¥In strict mode, eval does not introduce new variables into the surrounding scope. In sloppy mode, eval("var x;") introduces a variable x into the surrounding function or the global scope. This means that, in general, in a function containing a call to eval, every name not referring to an argument or local variable must be mapped to a particular definition at runtime (because that eval might have introduced a new variable that would hide the outer variable). In strict mode, eval creates variables only for the code being evaluated, so eval can't affect whether a name refers to an outer variable or some local variable:

js
var x = 17;
var evalX = eval("'use strict'; var x = 42; x;");
console.assert(x === 17);
console.assert(evalX === 42);

传递给 eval() 的字符串是否以严格模式求值取决于 eval() 的调用方式 (直接评估或间接评估)。

¥Whether the string passed to eval() is evaluated in strict mode depends on how eval() is invoked (direct eval or indirect eval).

块作用域函数声明

¥Block-scoped function declarations

JavaScript 语言规范从一开始就不允许函数声明嵌套在块语句中。然而,它是如此直观,以至于大多数浏览器将其实现为扩展语法。不幸的是,实现的语义存在分歧,并且语言规范不可能协调所有实现。因此,块作用域函数声明 仅在严格模式下显式指定(而它们曾经在严格模式下被禁止),而草率模式行为在浏览器之间仍然存在差异。

¥The JavaScript language specification, since its start, had not allowed function declarations nested in block statements. However, it was so intuitive that most browsers implemented it as an extension grammar. Unfortunately, the implementations' semantics diverged, and it became impossible for the language specification to reconcile all implementations. Therefore, block-scoped function declarations are only explicitly specified in strict mode (whereas they were once disallowed in strict mode), while sloppy mode behavior remains divergent among browsers.

使 eval 和 argument 更简单

¥Making eval and arguments simpler

严格模式让 argumentseval 不再那么离奇神奇。两者都涉及大量草率模式下的神奇行为:eval 添加或删除绑定以及更改绑定值,arguments 将命名参数与其索引属性同步。严格模式在将 evalarguments 视为关键字方面取得了巨大进步。

¥Strict mode makes arguments and eval less bizarrely magical. Both involve a considerable amount of magical behavior in sloppy mode: eval to add or remove bindings and to change binding values, and arguments syncing named arguments with its indexed properties. Strict mode makes great strides toward treating eval and arguments as keywords.

防止绑定或分配 eval 和参数

¥Preventing binding or assigning eval and arguments

名称 evalarguments 不能在语言语法中绑定或分配。所有这些尝试都是语法错误:

¥The names eval and arguments can't be bound or assigned in language syntax. All these attempts to do so are syntax errors:

js
"use strict";
eval = 17;
arguments++;
++eval;
const obj = { set p(arguments) {} };
let eval;
try {
} catch (arguments) {}
function x(eval) {}
function arguments() {}
const y = function eval() {};
const f = new Function("arguments", "'use strict'; return 17;");

参数和参数索引之间不同步

¥No syncing between parameters and arguments indices

严格模式代码不会将 arguments 对象的索引与每个参数绑定同步。在第一个参数为 arg 的草率模式函数中,设置 arg 也会设置 arguments[0],反之亦然(除非未提供参数或删除 arguments[0])。严格模式函数的 arguments 对象存储调用函数时的原始参数。arguments[i] 不跟踪相应命名参数的值,命名参数也不跟踪相应 arguments[i] 中的值。

¥Strict mode code doesn't sync indices of the arguments object with each parameter binding. In a sloppy mode function whose first argument is arg, setting arg also sets arguments[0], and vice versa (unless no arguments were provided or arguments[0] is deleted). arguments objects for strict mode functions store the original arguments when the function was invoked. arguments[i] does not track the value of the corresponding named argument, nor does a named argument track the value in the corresponding arguments[i].

js
function f(a) {
  "use strict";
  a = 42;
  return [a, arguments[0]];
}
const pair = f(17);
console.assert(pair[0] === 42);
console.assert(pair[1] === 17);

"固定" JavaScript

¥"Securing" JavaScript

严格模式使编写 "secure" JavaScript 变得更容易。一些网站现在为用户提供了编写 JavaScript 的方法,这些 JavaScript 将由网站代表其他用户运行。浏览器中的 JavaScript 可以访问用户的私有信息,因此此类 JavaScript 在运行之前必须进行部分转换,以审查对禁止功能的访问。JavaScript 的灵活性使得在没有大量运行时检查的情况下实际上不可能做到这一点。某些语言函数如此普遍,以至于执行运行时检查会产生相当大的性能成本。一些严格模式调整,加上要求用户提交的 JavaScript 是严格模式代码并且以某种方式调用它,大大减少了对这些运行时检查的需要。

¥Strict mode makes it easier to write "secure" JavaScript. Some websites now provide ways for users to write JavaScript which will be run by the website on behalf of other users. JavaScript in browsers can access the user's private information, so such JavaScript must be partially transformed before it is run, to censor access to forbidden functionality. JavaScript's flexibility makes it effectively impossible to do this without many runtime checks. Certain language functions are so pervasive that performing runtime checks has a considerable performance cost. A few strict mode tweaks, plus requiring that user-submitted JavaScript be strict mode code and that it be invoked in a certain manner, substantially reduce the need for those runtime checks.

没有这个替换

¥No this substitution

在严格模式下作为 this 传递给函数的值不会被强制成为对象(也称为 "boxed")。对于草率模式函数,this 始终是一个对象:提供的对象(如果使用对象值 this 调用);或 this 的装箱值(如果使用 this 等原语进行调用);或全局对象,如果使用 undefinednull 作为 this 调用。(使用 callapplybind 来指定特定的 this。)自动装箱不仅会带来性能成本,而且在浏览器中公开全局对象也会带来安全隐患,因为全局对象提供对 "secure" JavaScript 环境必须限制的功能的访问。因此,对于严格模式函数,指定的 this 不会装箱到对象中,如果未指定,则 thisundefined 而不是 globalThis

¥The value passed as this to a function in strict mode is not forced into being an object (a.k.a. "boxed"). For a sloppy mode function, this is always an object: either the provided object, if called with an object-valued this; or the boxed value of this, if called with a primitive as this; or the global object, if called with undefined or null as this. (Use call, apply, or bind to specify a particular this.) Not only is automatic boxing a performance cost, but exposing the global object in browsers is a security hazard because the global object provides access to functionality that "secure" JavaScript environments must restrict. Thus for a strict mode function, the specified this is not boxed into an object, and if unspecified, this is undefined instead of globalThis:

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);

移除堆栈遍历属性

¥Removal of stack-walking properties

在严格模式下,不再可能对 JavaScript 堆栈进行 "walk"。许多实现用于实现一些扩展功能,使检测函数的上游调用者成为可能。当函数 fun 正在被调用时,fun.caller 是最近调用 fun 的函数,fun.arguments 是调用 funarguments。这两个扩展对于 "secure" JavaScript 来说都是有问题的,因为它们允许 "secured" 代码访问 "privileged" 函数及其(可能不安全的)参数。如果 fun 处于严格模式,则 fun.callerfun.arguments 都是不可删除的属性,在设置或检索时会抛出异常:

¥In strict mode it's no longer possible to "walk" the JavaScript stack. Many implementations used to implement some extension features that make it possible to detect the upstream caller of a function. When a function fun is in the middle of being called, fun.caller is the function that most recently called fun, and fun.arguments is the arguments for that invocation of fun. Both extensions are problematic for "secure" JavaScript because they allow "secured" code to access "privileged" functions and their (potentially unsecured) arguments. If fun is in strict mode, both fun.caller and fun.arguments are non-deletable properties which throw when set or retrieved:

js
function restricted() {
  "use strict";
  restricted.caller; // throws a TypeError
  restricted.arguments; // throws a TypeError
}
function privilegedInvoker() {
  return restricted();
}
privilegedInvoker();

同样,不再支持 arguments.callee。在草率模式下,arguments.callee 指的是封闭函数。这个用例很弱:命名封闭函数!此外,arguments.callee 极大地阻碍了内联函数等优化,因为如果访问 arguments.callee,则必须能够提供对未内联函数的引用。严格模式函数的 arguments.callee 是不可删除的属性,在设置或检索时会引发错误:

¥Similarly, arguments.callee is no longer supported. In sloppy mode, arguments.callee refers to the enclosing function. This use case is weak: name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed. arguments.callee for strict mode functions is a non-deletable property which throws an error when set or retrieved:

js
"use strict";
const f = function () {
  return arguments.callee;
};
f(); // throws a TypeError

面向未来的 JavaScript

¥Future-proofing JavaScript

额外保留字

¥Extra reserved words

保留字 是不能用作变量名的标识符。严格模式比宽松模式多保留了一些名称,其中一些名称已经在语言中使用,还有一些是为将来保留的,以便将来的语法扩展更容易实现。

¥Reserved words are identifiers that can't be used as variable names. Strict mode reserves some more names than sloppy mode, some of which are already used in the language, and some of which are reserved for the future to make future syntax extensions easier to implement.

  • implements
  • interface
  • let
  • package
  • private
  • protected
  • public
  • static
  • yield

过渡到严格模式

¥Transitioning to strict mode

严格模式的设计使得可以逐步过渡到该模式。可以单独更改每个文件,甚至可以将代码转换为严格模式,直至函数粒度。

¥Strict mode has been designed so that the transition to it can be made gradually. It is possible to change each file individually and even to transition code to strict mode down to the function granularity.

你可以通过首先将 "use strict" 添加到一段源代码中,然后修复所有执行错误,同时注意语义差异,将代码库迁移到严格模式。

¥You can migrate a codebase to strict mode by first adding "use strict" to a piece of source code, and then fixing all execution errors, while watching out for semantic differences.

语法错误

¥Syntax errors

添加 'use strict'; 时,以下情况会在脚本执行前抛出 SyntaxError

¥When adding 'use strict';, the following cases will throw a SyntaxError before the script is executing:

  • 八进制语法 const n = 023;
  • with 声明
  • 在变量名 delete myVariable 上使用 delete
  • 使用 evalarguments 作为变量或函数参数名称
  • 使用新的 保留关键字 之一(为未来的语言功能做准备):implementsinterfaceletpackageprivateprotectedpublicstaticyield
  • 声明两个同名函数参数 function f(a, b, b) {}
  • 在对象文字中两次声明相同的属性名称 {a: 1, b: 3, a: 7}。此限制后来被删除 (错误 1041128)。

这些错误是好的,因为它们揭示了简单的错误或不良做法。它们发生在代码运行之前,因此只要代码被运行时解析,它们就很容易被发现。

¥These errors are good, because they reveal plain errors or bad practices. They occur before the code is running, so they are easily discoverable as long as the code gets parsed by the runtime.

新的运行时错误

¥New runtime errors

JavaScript 过去常常在执行的操作应该是错误的上下文中默默地失败。在这种情况下,严格模式会抛出异常。如果你的代码库包含此类情况,则需要进行测试以确保没有任何问题。你可以在函数粒度级别筛选此类错误。

¥JavaScript used to silently fail in contexts where what was done should be an error. Strict mode throws in such cases. If your code base contains such cases, testing will be necessary to be sure nothing is broken. You can screen for such errors at the function granularity level.

  • 分配给未声明的变量会抛出 ReferenceError。这用于在全局对象上设置属性,这很少是预期的效果。如果你确实想为全局对象设置一个值,请将其显式分配为 globalThis 上的属性。
  • 未能分配给对象的属性(例如,它是只读的)会抛出 TypeError。在草率模式下,这会默默地失败。
  • 删除不可删除的属性会抛出 TypeError。在草率模式下,这会默默地失败。
  • 如果函数处于严格模式,则访问 arguments.calleestrictFunction.callerstrictFunction.arguments 会抛出 TypeError。如果你使用 arguments.callee 递归调用函数,则可以使用命名函数表达式。

语义差异

¥Semantic differences

这些差异是非常微妙的差异。测试套件可能无法捕捉到这种细微的差异。可能有必要仔细检查你的代码库,以确保这些差异不会影响代码的语义。幸运的是,这种仔细的审查可以逐步降低功能粒度。

¥These differences are very subtle differences. It's possible that a test suite doesn't catch this kind of subtle difference. Careful review of your code base will probably be necessary to be sure these differences don't affect the semantics of your code. Fortunately, this careful review can be done gradually down the function granularity.

this

在草率模式下,像 f() 这样的函数调用会将全局对象作为 this 值传递。在严格模式下,现在是 undefined。当使用 callapply 调用函数时,如果该值是原始值,则该值将被装箱到一个对象中(或 undefinednull 的全局对象)。严格模式下,直接传递值,不进行转换或替换。

arguments

在草率模式下,修改 arguments 对象中的值会修改相应的命名参数。这使得 JavaScript 引擎的优化变得复杂,并使代码更难以阅读/理解。在严格模式下,使用与命名参数相同的值创建和初始化 arguments 对象,但对 arguments 对象或命名参数的更改不会相互反映。

eval

在严格模式代码中,eval 不会在调用它的范围内创建新变量。当然,在严格模式下,字符串是用严格模式规则评估的。需要进行彻底的测试以确保没有任何损坏。如果你确实不需要 eval,则不使用它可能是另一个务实的解决方案。

块作用域函数声明

在草率模式下,块内的函数声明可能在块外可见,甚至可调用。在严格模式下,块内的函数声明仅在块内可见。

规范

Specification
ECMAScript Language Specification

¥Specifications

也可以看看

¥See also