ES5 严格模式
ECMAScript 5 的严格模式是采用具有限制性JavaScript变体的一种方式,从而使代码显示地脱离“马虎模式/稀松模式/懒散模式“(sloppy)模式。
严格模式代码和非严格模式代码可以共存,建议项目脚本可以渐进式地采用严格模式。
严格模式对正常的 JavaScript语义做了一些更改。
严格模式通过抛出错误来消除了一些原有静默错误。
严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷。有时候,相同的代码,严格模式可以比非严格模式下运行得更快。
严格模式禁用了在ECMAScript的未来版本中可能会定义的一些语法。
调用严格模式
为脚本开启严格模式
为整个脚本文件开启严格模式。
需要在所有语句之前放一个特定语句 “use strict”; (‘use strict’;)。
// 整个脚本都开启严格模式的语法
"use strict";
var v = "Hi! I'm a strict mode script!";
为函数开启严格模式
要给某个函数开启严格模式。
需要把 “use strict”; ( ‘use strict’; ) 声明一字不漏放在函数体所有语句之前。
function strict() {
// 函数级别严格模式语法
'use strict';
function nested() {
return "And so am I!";
}
return "Hi! I'm a strict mode function! " + nested();
}
function notStrict() {
return "I'm not strict.";
}
严格模式的变化
严格模式同时改变了语法及运行时行为。变化通常分为这几类:
- 将问题直接转化为错误(如语法错误或运行时错误);
- 简化了如何为给定名称的特定变量计算;
- 简化了 eval 以及 arguments, 将写 "安全“ JavaScript的步骤变得更简单;
- 改变了预测未来ECMAScript行为的方式;
将过失错误转为异常
在严格模式下, 某些先前被接受的过失错误将会被认为是异常。
1. 无法创建全局变量;
"use strict";
a = 1;
// Uncaught ReferenceError: a is not defined
2. 使静默失败(不报错也没有任何效果)的赋值操作抛出异常;
// 给不可写属性赋值
var obj = {};
Object.defineProperty(obj, "x", { value: 42, writable: false });
obj.x = 9;
// Uncaught TypeError: Cannot assign to read only property 'x' of object '#<Object>'
// 给只读属性赋值
var obj = { get x () { return 17; } };
obj.x = 5;
// Uncaught TypeError: Cannot set property x of #<Object> which has only a getter
// 给不可扩展对象的新属性赋值
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "ohai";
// Uncaught TypeError: Cannot add property newProp, object is not extensible
3. 删除不可删除的属性时会抛出异常;
delete Object.prototype;
// Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
4. 重名属性被认为是语法错误(经测试,无效);
chrome/firefox 浏览器都可以正常运行。
5. 要求函数的参数名唯一;
function sum (a, a, c) {
return a + a + c;
}
// Uncaught SyntaxError: Duplicate parameter name not allowed in this context
6. 禁止八进制语法;
ECMAScript并不包含八进制语法, 但所有的浏览器都支持这种以零(0)开头的八进制语法。
在ECMAScript 6中支持为一个数字加"0o"的前缀来表示八进制数.
var a = 0o10; // ES6: 八进制
所以严格模式下八进制语法会引起语法错误:
var sum = 015 +
197 +
142;
// Uncaught SyntaxError: Octal literals are not allowed in strict mode.
7. 禁止设置原始值属性;
false.true = "";
// Uncaught TypeError: Cannot create property 'true' on boolean 'false'
(14).sailing = "home";
// Uncaught TypeError: Cannot create property 'sailing' on number '14'
"with".you = "far away";
// TypeError: Cannot create property 'you' on string 'with'
简化变量使用
1. 禁用 with 属性;
with 所引起的问题是块内的任何名称可以映射(map)到 with 传进来的对象的属性, 也可以映射到包围这个块的作用域内的变量(甚至是全局变量), 这一切都是在运行时决定的: 在代码运行之前是无法得知的. 严格模式下, 使用 with 会引起语法错误, 所以就不会存在 with 块内的变量在运行时才决定引用到哪里的情况了:
var x = 17;
with (obj) {
// 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?
// 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
x;
}
2. eval 不再为上层范围引入新变量;
var x = 17;
var evalX = eval("var x = 42; x");
console.log(x === 17);
console.log(evalX === 42);
// false true
var x = 17;
var evalX = eval("'use strict'; var x = 42; x");
console.log(x === 17);
console.log(evalX === 42);
// true true
3. 禁止删除声明变量;
"use strict";
var x;
delete x;
// Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.
让 eval 和 arguments 变的简单
1. 名称 eval 和 arguments 不能通过程序语法被绑定(be bound)或赋值;
以下的所有尝试将引起语法错误:
"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;");
2. 参数的值不会随 arguments 对象的值的改变而变化。
function f(a) {
a = 42;
return [a, arguments[0]];
}
var pair = f(17);
console.log(pair[0] === 42);
console.log(pair[1] === 17);
// true false
function f(a) {
"use strict";
a = 42;
return [a, arguments[0]];
}
var pair = f(17);
console.log(pair[0] === 42);
console.log(pair[1] === 17);
// true true
3. 不再支持 arguments.callee。
正常模式下,arguments.callee 指向当前正在执行的函数。
在严格模式下,arguments.callee 是一个不可删除属性,而且赋值和读取时都会抛出异常:
var f = function() { return arguments.callee; };
console.log(f());
// f 函数
"use strict";
var f = function() { return arguments.callee; };
console.log(f());
// Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
“安全的” JavaScript
1. 通过 this 传递给一个函数的值不会被强制转换为一个对象;
对一个普通的函数来说,this 总会是一个对象:不管调用时 this 它本来就是一个对象;还是用布尔值,字符串或者数字调用函数时函数里面被封装成对象的 this;还是使用 undefined 或者 null 调用函数式 this 代表的全局对象(使用 call, apply 或者 bind 方法来指定一个确定的 this)。
"use strict";
function fun() { return this; }
console.log(fun() === undefined);
console.log(fun.call(2) === 2);
console.log(fun.apply(null) === null);
console.log(fun.call(undefined) === undefined);
console.log(fun.bind(true)() === true);
// true true true true true
2. 不能通过广泛实现的ECMAScript扩展“游走于”JavaScript的栈中;
在普通模式下用这些扩展的话,当一个叫 fun 的函数正在被调用的时候,fun.caller 是最后一个调用fun 的函数,而且 fun.arguments 包含调用 fun 时用的形参。
function restricted () {
"use strict";
console.log(restricted.caller);
console.log(restricted.arguments);
}
function privilegedInvoker() {
return restricted(1, 2, 3);
}
privilegedInvoker();
// index.js:3 Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
3. arguments 不会再提供访问与调用这个函数相关的变量的途径(经测试,无效);
chrome/firefox 浏览器都可以正常运行。
arguments.caller 在严格模式下同样是一个不可被删除的属性,在赋值或者取值时会报错。
"use strict";
function fun (a, b) {
"use strict";
var v = 12;
return arguments.caller;
}
console.log(fun(1, 2));
为未来的 ECMAScript 版本铺平道路
1. 一部分字符变成保留关键字;
这些字符包括 implements, interface, let, package, private, protected, public, static 和yield。
function package (protected) {
"use strict";
var implements;
interface:
while (true) {
break interface;
}
function private() { }
}
function fun (static) { 'use strict'; }
2. 禁止了不在脚本或者函数层面上的函数声明(经测试,无效);
chrome/firefox 浏览器都可以正常运行。
在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。
这并不在ES5规范中(甚至是ES3)!这是一种针对不同浏览器中不同语义的一种延伸。
未来的ECMAScript版本很有希望制定一个新的,针对不在脚本或者函数层面进行函数声明的语法。
在严格模式下禁止这样的函数声明 对于将来ECMAScript版本的推出扫清了障碍:
"use strict";
if (true) {
function f() { }
f();
}
for (var i = 0; i < 5; i++) {
function f2() { }
f2();
}
function baz() {
function eit() { }
}
浏览器的严格模式
主流浏览器现在实现了严格模式。但是不要盲目的依赖它,因为市场上仍然有大量的浏览器版本只部分支持严格模式或者根本就不支持(比如IE10之前的版本)。