ECMAScript 5中引入了严格模式(strict mode),相同的代码在严格模型下有时会比普通模式下执行的更快。在严格模式下,执行引擎会对JavaScript进行更加严格的语法检查,一些在普通模式下的静默错误会在严格模式下抛出异常。
1. 使用严格模式
在语义上严格模式与普通模式并不完全一致,其主要表现在以下几个方面:
- 严格模式会将普通模式下的静默错误直接变成明显的错误
- 严格模式修改了一些执行引擎难以处理的错误,同样的代码在严格模式有时会比非严格模式下执行的更快
- 严格模式禁用了一些可能在未来版本中定义的语法
使用严格模式时应该保证执行环境支持严格模型,并对相关代码进行充分测试。严格模式可以与非严格模式共存,所以可以选择性对代码加入严格模式。
启用严格模型,只要在代码中加入"use strict";或'use strict';即可。我们可以为整个script标签或一个.js文件全部开启严格模式,也可以为某一个函数单独开启严格模式。
1.1 全部开启严格模式
在一个script标签或一个.js文件的顶部加入"use strict";或'use strict';,整个标签或文件就会运行在严格模式下:
// 对一个script标签使用严格模式 <scirpt> "use strict"; var v1 = "use strict之后的代码将运行在严格模式下"; …… </scirpt>
// 对.js文件使用严格模式
'use strict';
const express = require('express');
const router = express();
……
1.2 部分使用严格模式
严格模式与非严格模式的模式的代码并不能合并使用,这时我们可以将使用严格式的代码封装到一个函数中,并对这个函数开启严格模式。对函数使用严格模式,同样在顶部加入"use strict";或'use strict';即可:
function strict(){
// 在函数级别使用严格模式
'use strict';
…… // 严格模式下的代码
}
function notStrict() {
return "非严格模式下的代码";
}
2. 与非严格模式的区别
JavaScript会对错误进行静默处理,有时候会给本来错误操作进行不报错处理,这可能会解决当前问题,但可能会给程序留下一些隐患。严格模式对JavaScript进行了非严格的语法检查,原本不报错的一些操作在严格模式下会抛出错误。
2.1 语法差异
启严格模式('use strict')后,在下面情况下会抛出SyntaxError异常。
不能删除已声明的变量
当使用delete删除已声明的变量时,会抛出SyntaxError错误:
"use strict";
var mySite = "niefengjun.cn";
delete mySite; // SyntaxError
禁用with语句
with语句会导致执行引擎无法进行优化,所以执行速度也会变慢。严格模式禁用with语句,使用时会抛出SyntaxError错误:
"use strict";
var v = 17;
var obj = {v:'niefengjun.cn'};
with (obj) // SyntaxError
{
v; // 普通模式下,在不运行代码的情况下,执行引擎无法知道 v 是上面定义的变量,还是obj.v
}
对象属性与函数参数名的唯一
在普通模式中,在对象中使用重复的属性名时,只有最后一个属性会起作用。但在严格模式中,会抛出SyntaxError异常:
"use strict";
var site = {domain:"niefengjun.cn", domain:"www.itbilcu.om"}; // SyntaxError
在普通模式中,重复的函数参数名与重复的对象属性名处理方式类似,后面的参数会覆盖前面的同名参数。但在严格模式中,会抛出SyntaxError异常:
// "use strict";
function sum(arg1, arg1, arg2) { // SyntaxError
return arg1 + arg2;
}
不能使用eval或arguments作为变量名或函数名
eval是JavaScript中的一个全局对象;arguments表示JavaScript的函数参数对象。在普通模式中eval和arguments都可作为变量名或函数名,但在严格模式中会抛出SyntaxError异常:
"use strict";
var eval = "niefengjun.cn"; // SyntaxError
function arguments () { // SyntaxError
}
不能ECMAScript预留关键字
严格模式对可能在ECMAScript 6中或未来版本的语言规范中使用的关键字,如:implements、interface、let、package、private、protected、public、static、yield。使用这些关键字作为变量名或函数名,严格模式可能会抛出SyntaxError异常:
"use strict"; var implements = "niefengjun.cn"; // SyntaxError var private = "IT笔录"; // SyntaxError
不能使用八进制
在ECMAScript规范中并没有关于八进制语法的定义,但以0开头的八进制语法确得到了浏览器的广泛支持,如:0644 === 420、"\045" === "%"。但在严格模式中使用八进制语法可能会抛出SyntaxError异常:
"use strict"; var sum = 0644; // SyntaxError
禁止块级的函数声明
严格模式下,只能脚本级别或函数级别声名函数。
'use strict';
if (true)
{
function f() { } // SyntaxError
f();
}
for (var i = 0; i < 5; i++)
{
function f2() { } // SyntaxError
f2();
}
function f3() // 合法
{
function f4() { } // 同样合法
}
“不能ECMAScript预留关键字”及“禁止块级的函数声明”为未来版本的ECMAScript可能会引入的新语法提供了保护,扫除了语言未来发展中的一些障碍。
2.2 运行时差异
JavaScript在某些上下文情况下会对失败做静默处理,但在严格模式中同样情况下会抛出错误。
不能使用未定义的变量
普通模式下,JavaScript会对未赋值的错误使用全局变量,而在严格模式下将抛出ReferenceError异常:
undefineVar = 33; // 普通模式下会使用全局变量
'use strict'; undefineVar = 33; // 严格模式抛出 ReferenceError 异常
更严格的对象属性操作
普通模式下,对不合法的对象属性操作会进行静默失败处理。如:对只读属性赋值、给不可写属性赋值、给不可扩展对象添加属性、删除不可删除的属性时,操作不会成功但也不没有任何错误提示。在严格模式中,这些操作都会抛出TypeError异常:
'use strict';
// 给只读属性赋值
var obj1 = { get x() { return 17; } };
obj1.x = 5; // TypeError
// 给不可写属性赋值
var obj2 = {};
Object.defineProperty(obj2, "x", { value: 42, writable: false });
obj2.x = 9; // TypeError
// 给不可扩展对象添加属性
var fixed = {};
Object.preventExtensions(fixed);
fixed.newProp = "niefengjun.cn"; // TypeError
// 删除不可删除的属性
var site = {}
Object.defineProperty(site, "domain", { value: "niefengjun.cn", configurable:false });
delete site.domain; // TypeError
禁用部分Arguments或Function对象属性
严格模式下,使用以下Arguments对象的:arguments.callee和arguments.caller属性或使用Function对象的anyFunction.caller和anyFunction.arguments属性时,会产生TypeError异常。
2.3 语义差别
严格模式与普通模式有一些微小的语义差别,了解这些差异可以帮我们写出严格模式更加健壮的代码。
函数中的this
在普通函数中this总会被封将成一个对象,而在严格模式中this不在被封装成对象。如果没有指定其值,那么它的值是'undefined':
"use strict";
function fun() { return this; }
console.log(fun() === undefined); // flase
console.log(fun.call(2) === 2); // flase
console.log(fun.apply(null) === null); // flase
console.log(fun.call(undefined) === undefined); // true
console.log(fun.bind(true)() === true); // flase
eval的区别
在实际应用中并不推荐使用eval,在严格模式中也不例外。当需要使用eval时应该注意,严格模式中eval不会在当前的作用域内创建新的变量,eval中传入的字符串也会按严格模式进行解析。
参数值不随arguments的变化而变化
正常模式下,函数的参数(形参)值会随arguments对象的变化而变化,反之亦然。而在严格模式下,传递中函数中的值是形参值的拷贝,所以其值不会随arguments的变化而变化。
