严格模式改变了哪些行为?
严格模式把一些静默失败改成抛错:给未声明变量赋值抛 ReferenceError,给只读属性赋值抛 TypeError。函数里的 this 不再被替换成全局对象或包装对象,arguments 不再和参数同步,禁用 with、八进制字面量和重复参数名,eval 不会往外层加变量。ES 模块和 class 内部自动是严格模式。
简答
在函数里不小心写了
totle = 1,拼错了变量名,非严格模式下会悄悄创建一个全局变量,严格模式下直接抛ReferenceError。这就是严格模式的主要作用:把原来静默失败的写法变成报错,让问题早点暴露。它改变的行为大致有三类。第一类是把静默失败变成抛错,比如给冻结对象的属性赋值会抛
TypeError。第二类是this:普通函数直接调用时this是undefined,用call传入原始值也不会被包装成对象。第三类是去掉一些难以优化的写法,with被禁用,eval不再往外层作用域加变量,arguments也不再和参数同步。开启方式是在脚本或函数体开头写
'use strict'。ES 模块和class的内部自动就是严格模式,不用再写,所以现代工程代码大多已经在严格模式下运行了。要注意,参数用了默认值、解构或剩余参数的函数,函数体里不能写
'use strict',会报语法错误。
运行时行为的变化
function sloppyThis() {
return typeof this;
}
function strictThis() {
'use strict';
return this;
}
console.log(sloppyThis.call(1)); // object
console.log(strictThis.call(1)); // 1
console.log(strictThis()); // undefined
function strictAssign() {
'use strict';
const frozen = Object.freeze({ count: 0 });
try {
frozen.count = 1;
} catch (error) {
console.log(error.name); // TypeError
}
try {
undeclared = 1;
} catch (error) {
console.log(error.name); // ReferenceError
}
}
strictAssign();
function sloppyArgs(a) {
a = 2;
return arguments[0];
}
function strictArgs(a) {
'use strict';
a = 2;
return arguments[0];
}
console.log(sloppyArgs(1)); // 2
console.log(strictArgs(1)); // 1
前三行看 this。非严格模式下,MDN 说函数里的 this 总是对象:传入原始值会被包装成对象,所以 typeof 得到 'object';传入 undefined 或 null 会换成全局对象。严格模式下传什么就是什么,call(1) 得到 1,直接调用得到 undefined。所以严格模式下方法被单独拿出来调用,访问 this.xxx 会马上报错,而不是悄悄改到全局对象上。
中间两个 try 是静默失败变成抛错。非严格模式下给冻结对象赋值什么也不发生,代码继续跑,很难发现;给未声明的变量赋值会创建全局变量。严格模式下这两种都抛错。
最后两行是 arguments。非严格模式下 arguments[0] 和参数 a 是联动的,改 a 会同步改 arguments[0]。严格模式下 arguments 只保存调用时传入的值,互不影响。严格模式还禁止访问 arguments.callee。
语法上的限制
有些写法在严格模式下直接是语法错误,代码一行都不会执行。with 语句被禁用,因为它让变量到底来自哪个对象在运行前无法确定。以 0 开头的八进制字面量如 0644 被禁止,要写成 0o644。函数参数不能重名。delete 一个普通变量名也是语法错误。
eval 的行为也收紧了。非严格模式下 eval('var x') 会在外层函数或全局里加一个变量 x;严格模式下 eval 里声明的变量只留在 eval 自己的作用域里。
怎样开启,作用范围多大
'use strict' 必须写在脚本或函数体的最前面,前面只能有注释或其他指令,否则就是一个普通字符串,不起作用。写在脚本开头会让整个脚本都是严格模式;写在函数开头只影响这个函数。
ES 模块的全部内容、class 的全部内容都自动是严格模式。this 在不同调用方式下的取值,见《普通函数和箭头函数的 this 有什么区别?》。
常见追问
为什么带默认参数的函数不能写 'use strict'?
这是语言的规定:MDN 写明
'use strict'只能用在参数是简单参数列表的函数体里,参数带默认值、剩余参数或解构时写它就是语法错误。需要的话,把严格模式写到外层脚本或函数上。
严格模式会让代码更快吗?
不能一概而论。MDN 的说法是严格模式代码“有时”能比相同的非严格代码跑得更快,因为它去掉了
with、eval改外层变量、arguments.callee这类妨碍引擎优化的行为。但具体快不快取决于引擎和代码,不能把它当成性能开关。