严格模式使代码隐式地脱离"马虎模式/稀松模式/懒散模式"(sloppy)模式, 将语法限制在原来语法的子集中.
'use strict';
禁止意外的创建全局变量
'use strict';
demo1 = 1; // ✖: 意外创建的
var demo2 = 1; // ✔: 不是意外创建的
(() => {
demo3 = 1; // ✖: 意外创建的
var demo4 = 1; // ✔: 不是全局变量
global.demo5 = 1; // ✔: 不是意外创建的
})();
禁止引起静默失败(不报错也没有任何效果)的赋值操作. 包括
为不可写属性赋值
'use strict';
// 不可写属性赋值-1
NaN = 1; // ✖: 在非严格模式下不报错但无效果, 在严格模式下直接报错
console.log(NaN);
// 不可写属性赋值-2
let obj1 = {};
// 定义不可写属性x(顺便了解一下defineProperty的参数)
Object.defineProperty(obj1, 'x', {
configurable: true, // 能否通过delete删除属性从而重新定义属性, 能否修改属性的特性, 能否把属性修改为访问器属性[默认true]
enumerable: true, // 能否通过for-in返回属性(默认true)
writable: false, // 能否修改属性值(默认true)
value: 123,
// get(){} // 我们不用这个属性
// set(){} // 我们不用这个属性
});
obj1.x = 1; // ✖: x属性是不可写的, 在非严格模式下不报错但无效果, 在严格模式下直接报错
// ✔: 由于configurable=true, 可以重新定义, 严格模式也不报错
Object.defineProperty(obj1, 'x', {
value: 12,
});
为只读属性赋值
'use strict';
// 给只读属性赋值
let obj2 = {
x: 1,
get x() {
// 一旦用了get, 如果不写set, 那么无法设置x(相当于上面x属性直接无效, 因为set x()和x一起存在要爆栈赋值)
return 2;
},
};
obj2.x = 3; // ✖: 没有set, 在非严格模式下不报错但无效果, 在严格模式下直接报错
console.log(obj2.x);
为不可扩展对象扩展
'use strict';
// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = 'ohai'; // ✖: 在非严格模式下不报错但无效果, 在严格模式下直接报错
console.log(fixed);
禁止删除不可删除属性
'use strict';
const obj = {};
Object.defineProperty(obj, 'x', {
configurable: false,
value: 1,
});
// delete obj.x; // ✖: 不可删除
console.log(obj.x); // 1
function foo() {}
delete foo.prototype; // ✖: 不可删除
对象内的所有属性名在对象内必须唯一(该规则在ES6被取消)
'use strict';
const obj1 = { a: 1, b: 2 };
const obj2 = { a: 3, b: 4 };
const obj3 = { ...obj1, ...obj2 }; // ✔: 在ES6不报错
console.log(obj3);
const obj4 = { a: 1, b: 2, a: 3, b: 4 }; // ✔: 在ES6不报错
console.log(obj4);
函数参数名必须唯一. 在非严格模式下, 重名参数名会发生覆盖, 但是仍然可以在arguments中读取到原值
// 单独执行这一部分不报错
function sloppy(a, a, c) {
console.log([a, a, c]); // [2,2,3]
console.log(arguments); // [Arguments] { '0': 1, '1': 2, '2': 3 }
return a + a + c;
}
sloppy(1, 2, 3);
// 单独执行这一部分, **即使不调用函数都报错**, 在语法检查的时候就炸了
function strict(a, a, c) {
'use strict';
console.log([a, a, c]);
console.log(arguments);
return a + a + c;
}
strict(1, 2, 3);
禁止使用加前导0
的方式表示八进制数, 在ES6中允许使用0o
做前导表示八进制
'use strict';
const a = 010; // ✖: 非严格模式: 8, 严格模式: Error
console.log(a);
const b = 0o10; // ✔: 8
console.log(b);
const c = '010'; // ✔: 10(可不是8)
console.log(+c);
const d = '0o10'; // ✔: 8(ES6支持)
console.log(+d);
禁止为基本类型(String, Number, Boolean, Null, Undefined, BitInt, Symbol)设置属性
'use strict';
const a = 123;
a.demo = 1; // ✖: 禁止设置属性
console.log(a, a.demo); // 123 undefined
禁用with
, 在运行之前无法获知with
中变量的指向, 这回拖慢代码执行速度
禁止eval
为外层作用域set/引入变量
var k = 10;
var x = 17;
var evalX = eval("'use strict'; var x = k; x"); // 严格模式下不会污染外层x
console.log(x); // 17
console.log(evalX); // 10
var y = 17;
var evalY = eval('var y = k; y'); // 非严格模式下会污染外层y
console.log(y); // 10
console.log(evalY); // 10
禁止delete
变量
'use strict';
let obj1 = { a: 1 };
console.log(obj1); // { a: 1 }
delete obj1.a; // ✔: 允许删除属性
console.log(obj1); // {}
let obj2 = { a: 1 };
console.log(obj2); // { a: 1 }
delete obj2; // ✖: 不允许删除属性
console.log(obj2);
eval
与arguments
的怪异行为禁止eval
与arguments
被绑定或赋值
"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
var f = new Function("arguments", "'use strict'; return 17;");
严格模式中arguments
中的值(指针)不会随arguments
中对象的变化而变化, 非严格模式中arguments
会随对象的变化而变化
function demo1(a, b, c) {
a = 2;
b.a = 2;
c = { b: 2 };
return [a, b, c, arguments];
// [2, { a: 2 }, { b: 2 }, [Arguments] { '0': 2, '1': { a: 2 }, '2': { b: 2 } }]
}
console.log(demo1(1, { a: 1 }, { a: 1 }));
function demo2(a, b, c) {
'use strict';
a = 2;
b.a = 2;
c = { b: 2 };
return [a, b, c, arguments];
// [ 2, { a: 2 }, { b: 2 }, [Arguments] { '0': 1, '1': { a: 2 }, '2': { a: 1 } }]
// 注意, 他做不了深层检测
}
console.log(demo2(1, { a: 1 }, { a: 1 }));
严格模式不允许使用arguments.callee
获得正在执行的函数
function demo1() {
console.log(arguments.callee === demo1);
}
function demo2() {
'use strict';
console.log(arguments.callee === demo2); // ✖
}
demo1();
demo2();
严格模式下通过this
传递给一个函数的值不会被强制转换为一个对象. 对一个普通的函数来说, this
总会是一个对象:不管调用时this
它本来就是一个对象.还是用布尔值, 字符串或者数字调用函数时函数里面被封装成对象的this
.还是使用undefined
或者null
调用函数式this
代表的全局对象(使用call
, apply
或者bind
方法来指定一个确定的this
). 这种自动转化为对象的过程不仅是一种性能上的损耗, 同时在浏览器中暴露出全局对象也会成为安全隐患, 因为全局对象提供了访问那些所谓安全的JavaScript环境必须限制的功能的途径. 所以对于一个开启严格模式的函数, 指定的this
不再被封装为对象, 而且如果没有指定this
的话它值是undefined
:
function demo1() {
return this;
}
console.log(demo1()); // <ref *1> Object [global]
console.log(demo1.call(2)); // [Number: 2] <- 是一个Number对象!
console.log(demo1.apply(null)); // <ref *1> Object [global]
console.log(demo1.call(undefined)); // <ref *1> Object [global]
console.log(demo1.bind(true)()); // [Boolean: true]
function demo2() {
'use strict';
return this;
}
console.log(demo2()); // undefined
console.log(demo2.call(2)); // 2
console.log(demo2.apply(null)); // null
console.log(demo2.call(undefined)); // undefined
console.log(demo2.bind(true)()); // true
禁用function.caller
/function.caller
以阻止程序"游走于"JS调用栈
'use strict';
function fun(a, b) {
// 非严格模式: true
// 严格模式 : TypeError
console.log(fun.caller === prefun);
// 非严格模式: [Arguments] { '0': 1, '1': 2 }
// 严格模式 : TypeError
console.log(fun.arguments);
}
function prefun(a, b) {
fun(a, b);
}
prefun(1, 2);
禁止读取arguments.caller
(这个属性同function.caller
)出于优化原因, 浏览器并没有实现这个属性, 无论是否使用严格模式都会返回undefined
增加保留字implements
, interface
, let
, package
, private
, protected
, public
, static
, yield
禁止在非全局/函数中声明函数
"use strict";
if (true) {
function f() { } // ✖
f();
}
for (var i = 0; i < 5; i++) {
function f2() { } // ✖
f2();
}
function baz() { // ✔
function eit() { } // ✔
}