ECMAScript 5的严格模式

前言

  ECMAScript 5引入了严格模式的概念,对向来以宽语法规则出名的javascript语言进行了一系列约束。javascript的严格模式并不是必须的,可以根据需要选择性的将代码放在严格模式下执行。

如何启用严格模式

  ECMAScript 5的严格模式有其作用范围:整个脚本或者函数作用域。在块级作用域如{}中设置严格模式不起作用。启用严格模式解析javascript语句的方法是在脚本或者函数作用域开头插入语句”use strict”;或者’use strict’;。
  ECMAScipt 6中引入了模块的概念,对于一个javascript模块而言,其默认使用严格模式解析。

严格模式做了哪些改变

对错误语法报出运行时错误

  因为javascript语法的宽容性,在非严格模式下运行脚本时,脚本解析器会自动忽略一些错误的语法。在严格模式下,javascript解析器会抛出运行时错误,提醒开发者脚本出错。相关的错误语法可以归结为如下几类:
  (1)隐性创建全局对象属性。

1
2
3
// 在非严格模式下,以下代码可以正常运行,会给当前全局对象创建一个名为invalidVar的属性。
// 在严格模式下,执行以下代码时,解析器会报出Reference Error错误。
invalidVar = 1;

  (2)不合法的赋值操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 以下示例代码在非严格模式下运行时均不会报错,解析器会直接忽略这些操作
// 但是在严格模式运行以下代码时,解析器会报错
// 给内置的具有只读属性的全局变量属性赋值
undefined = 1; // 在严格模式下会报出TypeError
// 给一个普通对象的只读属性复制
var obj1 = {};
Object.defineProperty(obj1, 'prop', {
value: 1,
writable: false
});
// 在严格模式下会报出TypeError错误
obj1.prop = 2;
// 给一个对象已经设置了getter的属性赋值
var obj2 = {
get a() {
return 1;
}
};
// 在严格模式下会报出TypeError错误
obj2.a = 2;
// 给一个设置了不能扩展的对象增加新属性
var nonExensible = {};
Object.preventExtensions(nonExensible);
// 在严格模式下会报出TypeError错误
nonExensible.a = 1;

  (3)删除不能删除的属性。

1
2
// 下面的代码在严格模式下执行会报出TypeError错误
delete Object.prototype;

  (4)函数参数名重复。

1
2
3
4
5
// 在非严格模式下,解析器不会报错,对于重名的参数而言,实际使用参数时,解析器会取参数列表中位置最靠后的已赋值的同名参数值。
// 在严格模式下,解析器会报出SyntaxError
function func(a, a, b) {
return a + b;
}

  (5)使用八进制数表示方法。
  现代浏览器都支持在数字前面添加前缀数字0表示八进制数,但是在严格模式下,这种八进制表示方法是错误的。ECMAScript 5并没有规定八进制数字的表示方式。ECMAScript 6使用’0o’(数字0和字母o)作为八进制数字的前缀。

1
2
// 在严格模式下,下面的代码会报出SyntaxError错误
var a = 012;

  (6)给原始类型值添加属性。

1
2
// 以下代码在严格模式下运行会报出TypeError错误
(1).prop = 2;

有利于编译器更好地优化代码

  严格模式通过简化变量的使用方式,禁止了变量的一些复杂用法,可以让编译器在运行前做一些关于变量的优化工作。
  (1)禁止with语法。

1
2
// 严格模式禁止使用with语法,以下代码运行在严格模式下会抛出SyntaxError错误
with (context) {}

  (2)eval语句不再引入新变量到当前执行上下文中。

1
2
3
// 在非严格模式下,执行下面的语句会在当前上下文中引入新的变量a
// 在严格模式下,执行下面的语句并不会在当前上下文中引入新变量
eval('var a = 1;');

  (3)delete删除原始类型变量会报错。

1
2
3
var primitive = 1;
// 严格模式下会报出SyntaxError
delete primitive;

  (4)arguments中的元素不再和函数参数进行绑定,只保存传入参数的初始值。参数值的改变和arguments中元素值的改变互不干扰。
  (5)禁止arguments.callee。

1
2
3
4
5
// 严格模式下运行下面代码报出TypeError错误
function func() {
arguments.callee();
}
func();

编写更安全的代码

  (1)函数中的this取值不再限制于只能是对象。
  在非严格模式下,当给this赋值为原始类型时,解析器自动将原始类型进行封装,转换成对应的对象类型,如给this赋值1时,解析器会执行Number(1)对数字进行封装;当给this赋值为undefined、null或者不赋值时,解析器会将this赋值成当前所处环境的全局对象。然而,在严格模式下,编译器不会对this的值进行自动封装,当不设置this的值时,this的值为undefined。
  (2)禁止使用函数的caller属性、arguments属性。

1
2
3
4
5
6
7
function func() {
// 严格模式下报出TypeError错误
func.caller;
// 严格模式下报出TypeError错误
func.arguments;
}
func();

操作保留关键字会报错

  在严格模式下使用保留的关键字会报出SyntaxError错误,保留的关键字有implements, interface, let, package, private, protected, public, static, yield。

总结

  严格模式促使javascript被更加规范地使用,编写出的代码更加安全,在平时开发中推荐使用javascript的严格模式。

参考文献

  1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
  2. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode/Transitioning_to_strict_mode