ECMAScript 5 最早引入了 严格模式(strict mode) 的概念。

通过严格模式,可以在函数内部选择进行较为严格的全局或局部的错误条件检测。使用严格模式的好处是可以提前知道代码中存在的错误,及时捕获一些可能代指编程错误的 ECMAScript 行为。

ECMAScript 的下一个版本将以严格模式为基础制定。

支持严格模式的浏览器包括:IE10+、Firefox4+、Safari5.1+、Chrome

1. 选择使用

要选择进入严格模式,可以使用严格模式的编译指示,实际上就是一个不会赋给任何变量的字符串:

  1. "use strict";

这种语法可以向后兼容那些不支持严格模式的 JS 引擎。支持的会启动严格模式,不支持的会忽略这个编译指示,当作一个未赋值的字符串字面量。

  • 如果在全局作用域中给出这个编译指示,则整个脚本都将使用严格模式。
  • 只对当前 JS 文件中的代码生效,下一个 JS 文件需要开启严格模式,还是需要再次编写编译指示
  • 如果把带有编译指示的脚本引入到其他文件中,则该文件中的 JS 代码也会处于严格模式。

    真实项目中,我们一般会把所有 JS 合并压缩为一个后再导入页面中,此时只要有一个 JS 文件在全局作用域使用了严格模式,那么所有代码都会使用严格模式。所以需要在编写自己代码的时候,不在全局中使用,而是在函数中开启。

只在函数中打开严格模式:

  1. function doSomething() {
  2. "use strict";
  3. }
  4. ~function () {
  5. "use strict";
  6. //=> 你的代码
  7. };

2. 严格模式的区别

  1. 在严格模式下不支持使用 arguments.callee / arguments.callee.caller 以及函数的 caller
  2. 在严格模式下,arguments 和形参没有映射机制
  3. 在严格模式下不允许给一个对象字面量设置重复属性名的 obj = {n:10,n:20}
  4. 在严格模式下,函数执行,如果没有明确指定执行主体(函数前面没有点),不再像非严格模式下一样,统一交给 window,而是让 this 指向 undefined,代表没有执行主体。严格模式下,有执行主体 this 就指向谁,没有执行主体,this 就是 undefined

3. 变量

在严格模式下,什么时候创建变量,怎么创建变量都是有限制的。

首先,不允许意外创建全局变量。

  1. //=> 未声明变量
  2. //=> 非严格模式下:创建全局变量
  3. //=> 严格模式下:抛出 ReferenceError
  4. message = 'Hello world';

在严格模式在,如果给一个未声明的变量赋值,就会抛出 ReferenceError。
其次,不能对变量调用 **delete** 操作符。

  1. //=> 删除变量
  2. /=> 非严格模式下:静默失败
  3. //=> 严格模式下:抛出 ReferenceError
  4. var color = 'red';
  5. delete color;

严格模式下,不能使用保留字作为变量名,会导致语法错误。
严格模式 - 图1

4. 对象

在严格模式下操作对象比在非严格模式下更容易导致错误。一般来说,非严格模式下会静默失败的情形,在严格模式下都会抛出错误。因此,在开发中使用严格模式会加大早发现错误的可能性。

以下情形下操作对象属性会导致错误:

  • 为只读属性赋值会抛出 TypeError
  • 对不可配置的(noconfigurable)性使用 delete 操作符会抛出 TypeError
  • 对不可扩展的(noextensible)对象添加属性会抛出 TypeError

另外一个限制是,在使用字面量时,属性名必须是唯一的。

  1. //=> 重名属性
  2. //=> 非严格模式下:后面的覆盖前面的
  3. //=> 严格模式下:抛出语法错误
  4. var person = {
  5. name: 'aa',
  6. name: 'bb'
  7. }

5. 函数

首先,严格模式下要求命名函数的参数必须唯一。

  1. //=> 重名参数
  2. //=> 非严格模式下:只能访问第二个参数
  3. //=> 严格模式下:抛出语法错误
  4. function sum(num, num) {
  5. }

其次,在严格模式下,arguments 对象的行为也有所不同。在非严格模式下,修改命名参数的值也会反映到 arguments 对象中,而严格模式下,这两个值是完全独立的,不存在映射机制

  1. //=> 修改命名参数的值
  2. //=> 非严格模式下:修改会反映到 arguments 中
  3. //=> 严格模式下:修改不会反映到 arguments 中
  4. function showValue(value) {
  5. value = 'foo';
  6. console.log(value);
  7. console.log(arguments[0]);
  8. }
  9. showValue('hi');
  10. //=> 非严格模式下:'foo','foo'
  11. //=> 严格模式下:'foo', 'hi'

另外一个变化是,淘汰了 **arguments.callee****arguments.caller**。在非严格模式下,这两个属性一个引用函数本身,另一个引用调用函数。而在严格模式下,访问它们会抛出 TypeError。同样,访问函数的 caller 属性,也会抛出错误。

  1. //=> 访问 arguments.callee
  2. //=> 非严格模式下:没有问题
  3. //=> 严格模式下:抛出 TypeError
  4. function factorial(num){
  5. if(num <=1) {
  6. return 1;
  7. } else {
  8. return num * arguments.callee(num-1)
  9. }
  10. }

最后,只能在脚本的顶级和在函数内部声明函数,也就是不能在 if 语句中声明函数,会导致语法错误。

5. eval

饱受诟病的 eval() 函数在严格模式下也得到了提升。最大的变化就是它在包含上下文中不再创建变量或函数

  1. //=> 使用 eval 创建变量
  2. //=> 非严格模式下:输出 10
  3. //=> 严格模式下:抛出 ReferenceError
  4. function doSomething() {
  5. eval('var x=10');
  6. console.log(x);
  7. }

可以在 eval 中声明变量或函数,但是这些变量或函数只能在被求值的特殊作用域中有效,随后就将被销毁。

6. 抑制 this

JavaScript 中一个最大的安全问题,也是最容易让人迷茫的地方,就是在某些情况下如何抑制 this 的值。

在严格模式下,函数执行,如果没有明确指定执行主体(函数前面没有点),不再像非严格模式下一样,统一交给 window,而是让 this 指向 undefined,代表没有执行主体。严格模式下,有执行主体 **this** 就指向谁,没有执行主体,**this** 就是 **undefined**

在非严格模式下,使用函数的 callapply 方法时,传入 nullundefined 值会被转换为全局变量。
而严格模式下,函数的 this 值始终是指定的值,无论指定的是什么值,使用 call 不指定值是,值为 undefined

  1. //=> 访问属性
  2. //=> 非严格模式下:访问全局属性
  3. //=> 严格模式下:抛出错误,因为 this 的值为 null,访问 null 的属性会抛出错误
  4. var color = 'red';
  5. function displayColor() {
  6. console.log(this.color);
  7. }
  8. displayColor.call(null);

7. 其他变化

首先,抛弃了 with 语句。

其次,去掉了 JavaScript 中的八进制字面量。以 0 开头的八进制字面量过去经常导致很多错误。

最后,parseInt 的行为,八进制字面量在严格模式下会被当做以 0 开头的十进制字面量来处理。

若有收获,就点个赞吧