💡严格模式
ECMAScript 5 引入了严格模式,它通过特意改变 JavaScript 的部分语义和运行时行为来提高语言的弹性。更改的内容通常分为以下几类:
把一些 JavaScript 的静默错误(mistakes)改成了直接抛出错误(errors)
修复了一些让 JavaScript 引擎难以进行优化的错误(所以严格模式下的代码有时会运行得更快)
禁止了一些可能会在未来的 ECMAScript 版本中定义的语法
严格模式有浏览器的兼容性问题,在使用之前最好做下特性检测(feature-testing),其中 IE10+, Node 0.6+, Chrome 13+ 都已经全方位地支持了严格模式,详见 Strict Mode Compatibility Table。

严格模式和非严格模式可以共存,所以可以选择让 JavaScript 代码逐步支持严格模式,比如从单个文件,甚至是单个函数开始。
非严格模式有时也称草率模式(sloppy mode),但这不是官方术语,知道即可
1. 开启严格模式
"use strict";
或 'use strict';
语句就能开启严格模式,写在代码的最开头。
严格模式可以用于整个脚本或是单个函数,但不能单独作用于块级结构 {}
。
1.1 整个脚本
为整个脚本启用严格模式。
'use strict';
const s = 'Hi! I\'m a strict mode script!';
1.2 特定函数
为特定函数启用严格模式。
function myStrictFunction() {
// 函数级的严格模式
'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.';
}
1.3 类和模块
JavaScript 类和模块中的所有代码都会自动开启严格模式,无需开发者手动声明去开启。
2. 更改的内容
更改的内容通常分为以下几类:
把一些 JavaScript 的静默错误(mistakes)改成了直接抛出错误(errors)
修复了一些让 JavaScript 引擎难以进行优化的错误(所以严格模式下的代码有时会运行得更快)
简化了变量的特定用法
简化了
eval
和arguments
让编写安全的 JavaScript 代码变得更加容易
禁止了一些可能会在未来的 ECMAScript 版本中定义的语法
2.1 将 mistakes 改成 errors
JavaScript 被设计为对新手友好,因此它有时会把本应是 errors 的操作视为 non-error 的语义,这种处理方式是可以解决一些眼前的问题,但有可能在未来造成更严重的问题。
所以,严格模式将一些以前可接受的 mistakes 变成了 errors,以便提前发现问题并及时修复。
第一,不能再无意间创建全局变量了。
'use strict';
mistypeVarible = 33; // ReferenceError: mistypeVarible is not defined
function f() {
a = 1; // ReferenceError: a is not defined
}
f();
console.log(this.a);
// non-strict mode 会创建一个全局变量
第二,之前是默默失败的赋值操作,现在会抛出异常。包括但不限于:给不可写的全局变量或者属性赋值、给仅 getter 的属性赋值、给不可扩展对象的新属性赋值等等。
'use strict';
// 给全局变量赋值
NaN = 2; // TypeError: Cannot assign to read only property 'NaN' of object '#<Window>'
undefined = true; // TypeError: Cannot assign to read only property 'undefined' of object '#<Window>'
// 给不可写的属性赋值
const o = {};
Object.defineProperty(o, "x", { value: 1, writable: false });
o.x = 2; // TypeError: Cannot assign to read only property 'x' of object '#<Object>'
console.log(o.x);
// 给仅 getter 的属性赋值
const o1 = {
get x() {
return 1;
}
};
o1.x = 2; // TypeError: Cannot set property x of #<Object> which has only a getter
console.log(o1.x);
// 给不可扩展对象的新属性赋值
const fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = 'hi'; // TypeError: Cannot add property newProp, object is not extensible
console.log(fixed);
// non-strict mode 会依次输出:
// 1
// 1
// {}
第三,删除不可删除的属性。
'use strict';
// TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
delete Object.prototype;
第四,函数的参数名必须唯一。
// SyntaxError: Duplicate parameter name not allowed in this context
function sum(a, b, c, a) {
'use strict';
return a + b + c;
}
第五,禁止以 0 为前缀的八进制文字或八进制转义序列。
'use strict';
var n = 010; // SyntaxError: Octal literals are not allowed in strict mode.
console.log(n);
// non-strict mode 会输出 8,因为被当成了八进制
第六,禁止在原始值上设置属性。
'use strict';
false.true = ''; // TypeError: Cannot create property 'true' on boolean 'false'
(14).sailing = 'home'; // TypeError: Cannot create property 'sailing' on number '14'
'with'.you = 'far away'; // TypeError: Cannot create property 'you' on string 'with'
// non-strict mode 虽没报错,但其实是执行失败了——没啥效果
2.2 更多保留字
2.1 部分的修改(将一些以前可接受的 mistakes 变成了 errors),也为语言未来的语义变化留下了空间,从而更好地向后兼容。
未来的保留字是不能作为变量名或函数名的,比如:implements
, interface
, let
, package
, private
, protected
, public
, static
, yield
。
2.3 变量相关
很多编译器优化都依赖从变量名到变量定义(在哪存怎么存)的映射,而 JavaScript 代码有时需要到运行时才能明确这种基本的映射关系,进而在一定程度上阻碍了编译器的优化。严格模式的引入就消除了导致这类结果的大部分情况,以便让编译器更彻底地优化代码。
禁止使用
with
eval
不会给周围作用域引入新的变量禁止删除正常的变量
'use strict';
let a;
delete a; // SyntaxError: Delete of an unqualified identifier in strict mode.
'use strict';
var a = 1;
var evalA = eval('var a = 2; a;');
console.log(a, evalA);
// non-strict mode: 2 2
// strict mode: 1 2
var a = 1;
var evalA = eval('"use strict"; var a = 2; a;');
console.log(a, evalA); // 1 2
2.4 eval
和 arguments
eval
和 arguments
严格模式将 eval
和 arguments
视为关键字。
eval
和arguments
不能被绑定(即当标识符),也不能被赋值不会给
arguments
对象的属性设置别名,即函数的形参和arguments
对象是完全独立的不再支持
arguments.callee
(因为不利于内联函数的优化)arguments.callee
是一个不可删除的属性,访问和设置时都会报错
'use strict';
eval = ''; // SyntaxError: Unexpected eval or arguments in strict mode
arguments = []; // SyntaxError: Unexpected eval or arguments in strict mode
'use strict';
function add(a, b) {
arguments[0] = 11;
b = 22;
console.log(a, b);
return a + b;
}
add(1, 2);
// non-strict mode: 11 22
// strict mode: 1 22
'use strict';
// TypeError:
// 'caller', 'callee', and 'arguments' properties may not be accessed
// on strict mode functions or the arguments objects for calls to them
function fn() {
return arguments.callee;
}
fn();
2.5 安全的代码
严格模式让编写安全的 JavaScript 代码变得更容易。
2.5.1 this
值
this
值在非严格模式中,this
值始终是个对象:undefined
和 null
会被自动设置成全局对象,其它原始值会被自动转成相应的对象,当通过 call()
, apply()
或 bind()
来指定特定 this
的时候。然而,自动装箱的操作不仅会降低性能,更有安全隐患——把全局对象暴露出去了,而全局对象可能提供本该加以限制的安全性行为。
在严格模式中,this
值不再被强制转换成对象了。也就是说,如果 this
值没被指定,那它的值就是 undefined
。
eg1. 全局作用域
'use strict';
console.log(this === globalThis); // true
eg2. 全局作用域里的函数
'use strict';
function fn() {
console.log(this); // undefined
this.a = 1; // TypeError: Cannot set properties of undefined (setting 'a')
console.log(this.a); // TypeError: Cannot read properties of undefined (reading 'a')
}
fn(); // 等价于 fn.call();
eg3. 通过 call()
, apply()
或 bind()
给函数指定特定的 this
'use strict';
function fn() {
this.a = 1;
console.log(this);
console.log(this.a);
}
const o = { a: 10, b: 11 };
fn.call(o);
// {a: 1, b: 11}
// 1
fn.call({});
// {a: 1}
// 1
fn.call(null);
// TypeError: Cannot set properties of null (setting 'a')
// null
// TypeError: Cannot read properties of null (reading 'a')
fn.call();
// TypeError: Cannot set properties of undefined (setting 'a')
// undefined
// TypeError: Cannot read properties of undefined (reading 'a')
2.5.2 函数栈不可追踪
当一个函数 fun
被调用时,fun.caller
表示最近调用 fun
的函数,fun.arguments
表示调用 fun
的参数。考虑到有些 ECMAScript 扩展会通过 fun.caller
和 fun.arguments
得到 JavaScript 的调用堆栈,所以严格模式中 fun.caller
和 fun.arguments
这两个属性都是不可删除的,在设置和检索时会报错。如下:
non-deletable properties, set or retrieved
function fn() {
'use strict';
// TypeError:
// 'caller', 'callee', and 'arguments' properties may not be accessed
// on strict mode functions or the arguments objects for calls to them
fn.caller;
fn.arguments;
}
function privilegedInvoker() {
return fn();
}
privilegedInvoker();
3. 清单
3.1 语法相关
会报 SyntaxError
function
的参数名有重复delete
变量以
0
为前缀的八进制文字或八进制转义序列with
语句使用
eval
和arguments
当标识符(变量名/函数名/类名),或者给它们赋值未来的保留字:
let
,yield
,public
,protected
,private
,static
,interface
,package
,implements
3.2 运行时相关
报错 ReferenceError
没声明直接赋值的变量,不再自动创建全局变量了
报错 TypeError
默默失败的赋值操作
=
,比如给不可写的全局变量或者属性赋值、给仅 getter 的属性赋值、给不可扩展对象的新属性赋值等delete
不可删除的属性访问函数的以下属性:
arguments.callee
,fnName.caller
和fnName.arguments
在原始值上设置属性
=
语义的更改
对于函数的
this
值,不再进行自动装箱操作函数的形参和
arguments
对象是完全独立的eval
不会给周围作用域引入新的变量
4. 主要参考
Last updated