JavaScript 的严格模式是一种选择 JavaScript 受限制变体的方法,从而使代码隐式地脱离"马虎模式/稀松模式/懒散模式"(sloppy)模式。严格模式不仅仅是一个子集:它的产生是为了形成与正常代码不同的语义。严格模式对正常 JavaScript 语义进行了一些更改:
- 明确禁止一些不合理、不严谨的语法,减少 JavaScript 语言的一些怪异行为。
- 通过将某些 JavaScript 无提示错误更改为引发错误,消除了一些原有静默错误。
- 修复了导致 JavaScript 引擎难以执行优化的错误:有时可以使严格模式代码比非严格模式的相同代码运行得更快。
- 禁止 ECMAScript 未来版本中可能定义的某些语法。
不支持严格模式与支持严格模式的浏览器在执行严格模式代码时会采用不同行为,因此,在没有对运行环境展开特性测试来验证严格模式支持的情况下,不要依赖严格模式来支持相关方面。严格模式和非严格模式的代码可以共存,因此项目脚本可以渐进式地采用严格模式。
调用严格模式
严格模式适用于整个脚本或单个函数。它不适用于 {}
大括号内的块语句;试图将其应用到这样的环境中没有任何作用。 eval
代码、 Function
代码、事件处理程序属性、传递给 setTimeout()
的字符串以及相关函数是函数体或整个脚本,并在其中调用严格模式按预期工作。
脚本的严格模式
要为整个脚本调用严格模式,需要在任何其他语句之前放一个特定语句 'use strict';
。
js
'use strict';
var num = 1;
函数的严格模式
同样,要调用函数的严格模式,在函数主体中的任何其他语句之前放一个 'use strict';
。
js
function strict() {
'use strict';
}
'use strict';
指令只能应用于具有简单参数的函数体。在带有剩余参数、默认参数或解构参数的函数中使用 'use strict';
是一个语法错误。
js
function sum (a = 0, b = 0) {
'use strict'; // Uncaught SyntaxError
return a + b;
}
模块的严格模式
JavaScript 模块的全部内容自动处于严格模式,无需任何语句来启动它。
js
function myStrictFunction() {}
export default myStrictFunction;
类的严格模式
类主体的所有部分都是严格模式代码,包括类声明和类表达式。
js
class C1 {
test() {
delete Object.prototype; // TypeError
}
}
new C1().test()
const C2 = class {}
delete Object.prototype // Uncaught SyntaxError
严格模式中的变化
严格模式同时改变了语法及运行时行为。变化通常分为这几类:
将失误转化为错误
JavaScript 被设计为对于新手开发人员来说很容易,有时它给应该是错误的操作提供非错误语义 (non-error semantics)。有时这可以解决眼前的问题,但有时这会在未来造成更严重的问题。严格模式则把这些失误当成错误,以便发现并及时修复。
1. 分配给未声明的变量:严格模式使得不可能意外创建全局变量,意外创建全局变量的赋值会引发错误。
js
'use strict'
num = 7 // ReferenceError
2. 无法分配给对象属性 :严格模式进行分配,否则会默默地失败并抛出异常。
属性分配失败有以下三种方式:
- 分配给不可写的数据属性
- 分配给仅
getter
访问器属性 - 分配给不可扩展对象上的新属性
js
'use strict'
const o = {}
Object.defineProperty(o, 'n', {
value: 8,
writable: false
})
o.n = 9 // TypeError
3. 无法删除对象属性 :在严格模式下,尝试删除不可配置或不可删除的属性会抛出异常。还禁止删除普通名称。 delete name
在严格模式下是一个语法错误。如果名称是可配置的全局属性,请在其前面添加 globalThis
前缀以将其删除。
js
'use strict'
delete Object.prototype // TypeError
delete [].length // TypeError
var x
delete x // Uncaught SyntaxError
delete globalThis.x
4. 重复的参数名称:严格模式要求函数参数名称是唯一的,重复的参数名称是语法错误。
js
function sum(a, a, c) { // Uncaught SyntaxError
'use strict'
return a + a + c
}
5. 旧的八进制字面量 :严格模式禁止使用 0
前缀的八进制文字或八进制转义序列,八进制的前导零语法很少有用,并且可能会被错误使用,因此严格模式使其成为语法错误。表示八进制文字的标准化方法是通过 0o
前缀。
js
'use strict'
const sum = 015 + 197 // Uncaught SyntaxError
const sumWithOctal = 0o10 + 8 // 16
6. 设置原始值的属性 :严格模式禁止在原始值上设置属性。访问原语上的属性会隐式创建一个不可观察的包装对象,因此在草率模式下,设置属性将被忽略(no-op)。在严格模式下,会抛出 TypeError
。
js
'use strict'
false.true = '' // TypeError
(14).sailing = 'hello' // TypeError
'with'.you = 'world' // TypeError
7. 重复的属性名称 :在严格模式下,重复的属性名称过去被视为 SyntaxError
。随着计算属性名称的引入,使得运行时重复成为可能,这一限制在 ES2015 中被删除。
js
'use strict'
const o = { p: 1, p: 2 }
简化作用域管理
严格模式简化了变量名称映射到代码中特定变量定义的方式。许多编译器优化依赖于将变量 X 存储在该位置的能力:这对于全面优化 JavaScript 代码至关重要。 JavaScript 有时会使代码中的名称到变量定义的基本映射在运行时之前无法执行。严格模式消除了大多数发生这种情况的情况,因此编译器可以更好地优化严格模式代码。
1. 删除 with
语句 :严格模式禁止 with
,会使 with
成为语法错误,因此 with
中的名称不可能在运行时引用未知位置。将对象分配给短名称变量,然后访问该变量的相应属性的简单替代方法可以随时替换 with
。
js
'use strict'
const x = 17
with (obj) { // Uncaught SyntaxError
x
}
2. 无泄漏 eval
:在严格模式下, eval
不会将新变量引入上层作用域,仅为正在计算的代码创建变量,因此 eval
不能影响名称是否引用外部变量或某些局部变量。传递给 eval()
的字符串是否在严格模式下求值取决于 eval()
的调用方式。
js
var x = 17
var evalX = eval("'use strict'; var x = 42; x;")
console.assert(x === 17)
console.assert(evalX === 42)
3. 块作用域函数声明:块作用域函数声明仅在严格模式下显式指定,而草率模式行为在浏览器之间仍然存在差异。
使 eval 和 argument 更简单
严格模式在将 eval
和 arguments
视为关键字方面取得了巨大进步。
1. 防止绑定或分配 eval
和 arguments
:名称 eval
和 arguments
不能在语言语法中绑定或分配。所有这些尝试都是语法错误。
js
'use strict'
eval = 17 // Uncaught SyntaxError
arguments++ // Uncaught SyntaxError
++eval // Uncaught SyntaxError
const obj = { set p(arguments) { } } // Uncaught SyntaxError
let eval // Uncaught SyntaxError
try { } catch (arguments) { } // Uncaught SyntaxError
function x(eval) { } // Uncaught SyntaxError
function arguments() { } // Uncaught SyntaxError
const y = function eval() { } // Uncaught SyntaxError
const f = new Function("arguments", "'use strict'; return 17;") // SyntaxError
2. 参数和参数索引之间不同步 :严格模式代码不会将 arguments
对象的索引与每个参数绑定同步。
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)
3. 不再支持 arguments.callee
:对于严格模式函数, arguments.callee
是一个不可删除的属性,在设置或检索时会引发错误。
js
var f = function () {
'use strict'
return arguments.callee // TypeError
}
f()
"保护"JavaScript
严格模式使编写"安全"JavaScript 变得更加容易。一些严格模式调整,加上要求用户提交的 JavaScript 是严格模式代码并且以某种方式调用它,大大减少了对这些运行时检查的需要。
1. 没有 this
替换 :在严格模式下作为 this
传递给函数的值不会被强制成为对象(也称为"装箱"),指定的 this
不会装箱到对象中,如果未指定,则 this
是 undefined
而不是 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)
2. 移除堆栈遍历属性:在严格模式下,不再"遍历" JavaScript 堆栈。
js
function restricted() {
'use strict'
restricted.caller // TypeError
restricted.arguments // TypeError
}
function privilegedInvoker() {
return restricted()
}
privilegedInvoker()
面向未来的 JavaScript
严格模式比宽松模式多保留了一些名称,保留关键字不能用作变量名的标识符。其中一些名称已经在语言中使用,还有一些是为将来保留的,以便将来的语法扩展更容易实现。这些字符包括 implements
、interface
、let
、package
、private
、protected
、public
、static
和 yield
。
js
function package(protected) { // Uncaught SyntaxError
'use strict'
var implements // Uncaught SyntaxError
interface: while (true) { break interface } // Uncaught SyntaxError
function private() {} // Uncaught SyntaxError
}
function fun(static) { 'use strict' } // Uncaught SyntaxError