💡严格模式

ECMAScript 5 引入了严格模式,它通过特意改变 JavaScript 的部分语义和运行时行为来提高语言的弹性。更改的内容通常分为以下几类:

  1. 把一些 JavaScript 的静默错误(mistakes)改成了直接抛出错误(errors)

  2. 修复了一些让 JavaScript 引擎难以进行优化的错误(所以严格模式下的代码有时会运行得更快)

  3. 禁止了一些可能会在未来的 ECMAScript 版本中定义的语法

严格模式有浏览器的兼容性问题,在使用之前最好做下特性检测(feature-testing),其中 IE10+, Node 0.6+, Chrome 13+ 都已经全方位地支持了严格模式,详见 Strict Mode Compatibility Table

严格模式和非严格模式可以共存,所以可以选择让 JavaScript 代码逐步支持严格模式,比如从单个文件,甚至是单个函数开始。

非严格模式有时也称草率模式(sloppy mode),但这不是官方术语,知道即可

1. 开启严格模式

"use strict";'use strict'; 语句就能开启严格模式,写在代码的最开头。

严格模式可以用于整个脚本或是单个函数,但不能单独作用于块级结构 {}

1.1 整个脚本

为整个脚本启用严格模式。

1.2 特定函数

为特定函数启用严格模式。

1.3 类和模块

JavaScript 类和模块中的所有代码都会自动开启严格模式,无需开发者手动声明去开启。

2. 更改的内容

更改的内容通常分为以下几类:

  1. 把一些 JavaScript 的静默错误(mistakes)改成了直接抛出错误(errors)

  2. 修复了一些让 JavaScript 引擎难以进行优化的错误(所以严格模式下的代码有时会运行得更快)

    1. 简化了变量的特定用法

    2. 简化了 evalarguments

    3. 让编写安全的 JavaScript 代码变得更加容易

  3. 禁止了一些可能会在未来的 ECMAScript 版本中定义的语法

2.1 将 mistakes 改成 errors

JavaScript 被设计为对新手友好,因此它有时会把本应是 errors 的操作视为 non-error 的语义,这种处理方式是可以解决一些眼前的问题,但有可能在未来造成更严重的问题。

所以,严格模式将一些以前可接受的 mistakes 变成了 errors,以便提前发现问题并及时修复。

第一,不能再无意间创建全局变量了。

第二,之前是默默失败的赋值操作,现在会抛出异常。包括但不限于:给不可写的全局变量或者属性赋值、给仅 getter 的属性赋值、给不可扩展对象的新属性赋值等等。

第三,删除不可删除的属性。

第四,函数的参数名必须唯一。

第五,禁止以 0 为前缀的八进制文字或八进制转义序列。

第六,禁止在原始值上设置属性。

2.2 更多保留字

2.1 部分的修改(将一些以前可接受的 mistakes 变成了 errors),也为语言未来的语义变化留下了空间,从而更好地向后兼容。

未来的保留字是不能作为变量名或函数名的,比如:implements, interface, let, package, private, protected, public, static, yield

2.3 变量相关

很多编译器优化都依赖从变量名到变量定义(在哪存怎么存)的映射,而 JavaScript 代码有时需要到运行时才能明确这种基本的映射关系,进而在一定程度上阻碍了编译器的优化。严格模式的引入就消除了导致这类结果的大部分情况,以便让编译器更彻底地优化代码。

  1. 禁止使用 with

  2. eval 不会给周围作用域引入新的变量

  3. 禁止删除正常的变量

2.4 evalarguments

严格模式将 evalarguments 视为关键字。

  1. evalarguments 不能被绑定(即当标识符),也不能被赋值

  2. 不会给 arguments 对象的属性设置别名,即函数的形参和 arguments 对象是完全独立的

  3. 不再支持 arguments.callee(因为不利于内联函数的优化)

    • arguments.callee 是一个不可删除的属性,访问和设置时都会报错

2.5 安全的代码

严格模式让编写安全的 JavaScript 代码变得更容易。

2.5.1 this

在非严格模式中,this 值始终是个对象:undefinednull 会被自动设置成全局对象,其它原始值会被自动转成相应的对象,当通过 call(), apply()bind() 来指定特定 this 的时候。然而,自动装箱的操作不仅会降低性能,更有安全隐患——把全局对象暴露出去了,而全局对象可能提供本该加以限制的安全性行为。

在严格模式中,this 值不再被强制转换成对象了。也就是说,如果 this 值没被指定,那它的值就是 undefined

eg1. 全局作用域

eg2. 全局作用域里的函数

eg3. 通过 call(), apply()bind() 给函数指定特定的 this

2.5.2 函数栈不可追踪

当一个函数 fun 被调用时,fun.caller 表示最近调用 fun 的函数,fun.arguments 表示调用 fun 的参数。考虑到有些 ECMAScript 扩展会通过 fun.callerfun.arguments 得到 JavaScript 的调用堆栈,所以严格模式中 fun.callerfun.arguments 这两个属性都是不可删除的,在设置和检索时会报错。如下:

non-deletable properties, set or retrieved

3. 清单

3.1 语法相关

会报 SyntaxError

  1. function 的参数名有重复

  2. delete 变量

  3. 0 为前缀的八进制文字或八进制转义序列

  4. with 语句

  5. 使用 evalarguments 当标识符(变量名/函数名/类名),或者给它们赋值

  6. 未来的保留字:let, yield, public, protected, private, static, interface, package, implements

3.2 运行时相关

  1. 报错 ReferenceError

    1. 没声明直接赋值的变量,不再自动创建全局变量了

  2. 报错 TypeError

    1. 默默失败的赋值操作 =,比如给不可写的全局变量或者属性赋值、给仅 getter 的属性赋值、给不可扩展对象的新属性赋值等

    2. delete 不可删除的属性

    3. 访问函数的以下属性:arguments.callee, fnName.callerfnName.arguments

    4. 在原始值上设置属性 =

  3. 语义的更改

    1. 对于函数的 this 值,不再进行自动装箱操作

    2. 函数的形参和 arguments 对象是完全独立的

    3. eval 不会给周围作用域引入新的变量

4. 主要参考

Last updated