关于 JavaScript 严格模式的介绍

 2016年05月31日    65     声明


ECMAScript 5中引入了严格模式(strict mode),相同的代码在严格模型下有时会比普通模式下执行的更快。在严格模式下,执行引擎会对JavaScript进行更加严格的语法检查,一些在普通模式下的静默错误会在严格模式下抛出异常。

  1. 使用严格模式
  2. 与非严格模式的区别

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 = "itbilu.com";         
delete mySite;  // SyntaxError

禁用with语句

with语句会导致执行引擎无法进行优化,所以执行速度也会变慢。严格模式禁用with语句,使用时会抛出SyntaxError错误:

"use strict";
var v = 17;
var obj = {v:'itbilu.com'};
with (obj) // SyntaxError
{
  v; // 普通模式下,在不运行代码的情况下,执行引擎无法知道 v 是上面定义的变量,还是obj.v
}

对象属性与函数参数名的唯一

在普通模式中,在对象中使用重复的属性名时,只有最后一个属性会起作用。但在严格模式中,会抛出SyntaxError异常:

"use strict";

var site = {domain:"itbilu.com", domain:"www.itbilcu.om"};  // SyntaxError

在普通模式中,重复的函数参数名与重复的对象属性名处理方式类似,后面的参数会覆盖前面的同名参数。但在严格模式中,会抛出SyntaxError异常:

// "use strict";

function sum(arg1, arg1, arg2) {  // SyntaxError
	return arg1 + arg2;
}

不能使用evalarguments作为变量名或函数名

eval是JavaScript中的一个全局对象;arguments表示JavaScript的函数参数对象。在普通模式中evalarguments都可作为变量名或函数名,但在严格模式中会抛出SyntaxError异常:

"use strict";

var eval = "itbilu.com";  // SyntaxError
function arguments () {  // SyntaxError
	
}

不能ECMAScript预留关键字

严格模式对可能在ECMAScript 6中或未来版本的语言规范中使用的关键字,如:implementsinterfaceletpackageprivateprotectedpublicstaticyield。使用这些关键字作为变量名或函数名,严格模式可能会抛出SyntaxError异常:

"use strict";

var implements = "itbilu.com";  // 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 = "itbilu.com"; // TypeError

// 删除不可删除的属性
var site = {}
Object.defineProperty(site, "domain", { value: "itbilu.com", configurable:false });
delete site.domain; // TypeError

禁用部分ArgumentsFunction对象属性

严格模式下,使用以下Arguments对象的:arguments.calleearguments.caller属性或使用Function对象的anyFunction.calleranyFunction.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的变化而变化。