最近看一些源码的时候经常看到’use strict’ 严格模式,对这个概念有点模棱两可。感觉 ES 5的严格模式和ES6有些地方有点相似,比如变量提升。这个可能注定要被历史掩埋,翻出来整理下纯粹为了好玩。

“use strict” 是 ECMAScript 5引入的一条指令。指令不是语句(但非常接近于语句)

先科普下指令和语句之间的两个重大的区别

它不包含其他语言的关键字。

指令仅仅是一个包含一个特殊字符串直接量的表达式。对于没有支持ECMAScript 5解释器的浏览器,“use strict” 只是个没有副作用的表达式语句。将来如果 user被作为关键字的时候,将不需要引号

ps:大部分浏览器都支持ECMAScript 5的 javascript解释器, 但是 ie8对ECMAScript大部分功能支持,但不支持strict mode

它只能出现脚本代码的开始或者函数体的开始、任何实体语句之前。

这里其实有点疑义,’use strict’可以不在第一行,只要前面不是产生实际运行结果的语句,比如直接跟在一个空的分好后面。

1
2
3
4
5
6
7
8
<script>
    "use strict";
    console.log("这是严格模式。");
  </script>
  <script>
    console.log("这是正常模式。");kly, it's almost 2 years ago now. I can admit it now - I run it on my school's network that has about 50 computers.
  </script>

上面代码中,前一个script标签是严格模式,后一个标签不是严格模式

严格模式和非严格模式的区别

严格模式下,this指向的值是undefined, 非严格模式下this 指向的是全局变量

1
2
3
4
5
6
7
8
9
10
11
12
function (){
return !this;
}
非严格模式下 this指向的全局变量,!this返回 false
function() {
'use strict'
return !this;
}
严格模式下 this指向的是undefined, 所以!this的值是 true

可以利用这种特性来判断javascript实现是否支持严格模式

1
2
3
4
var isStrictMode = (function() {
"use strict";
return this === undefinded
}());

严格模式禁止使用 with语句

with语句用于扩展临时作用域链

1
2
3
4
5
6
7
8
9
10
11
12
with(document.form[0]){
name.value = '';
address.value = '';
email.value = '';
}
以上的 with语句等价于 ->
var form = document.form[0];
form.name.value = '';
form.address.value = '';
form.email.value = '';

但是必须一提的是,with 有两个致命缺点。一个是难于优化,一个是运行比较慢,所以在严格模式中是被禁止使用的。

严格模式中,所有变量都要先申明

如果给一个为申明的变量、函数、函数参数、catch从句参数或全局对象的属性赋值,将会抛出一个错误异常。这个跟ES 6中的没有变量提升一样,变量得先定义在使用。

在非严格模式中,这种隐式申明的全局变量的方法是给全局对象新添加一个新属性

call() 或 apply()

在严格模式中,通过 call()apply() 来调用函数时,其中的this值就是传入的第一个参数

在非严格模式中,null 和 undefined 值被全局对象和转换为对象的的非对象值所替代

只读和不可扩展对象

严格模式下,给只读和不可扩展对象创建新成员都将抛出一个类型错误
非严格模式,这些操作只是简单的操作失败,不会报错

eval()

在严格模式中,传入eval()的代码不能再调用程序所在的上下文中声明变量或定义函数。变量和函数的定义是在eval()创建新的作用域,在eval()返回时,作用域就被弃用了。

正常模式下,eval()的作用域取决于所处全局作用域,或者处于函数的局部作用域。

1
2
3
4
5
6
7
'use strict'
var a = 1;
console.log(eval("var a = 5; a")) // 5
console.log(a); // 1

arguments 对象

在严格模式,传入参数arguments拥有传入传入函数值的静态副本。
所以你不允许对arguments进行操作。

1
2
3
4
5
6
7
8
9
10
11
'use strict'
function f() {
arguments ++; // 报错:SyntaxError: can't assign to arguments in strict mode
}
try { } catch (arguments) { } // 语法错误
function arguments() { } // 语法错误
var f = new Function("arguments", "'use strict'; return 17;"); // 语法错误

在非严格模式,arguments的数组元素和函数参数都是指向同一个值的引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
function f1(a) {
a++;
return [a, arguments[0]];
}
f1(1); //非严格模式下输出为 [2,2];
function f2(a) {
'use strict'
a++;
return [a,arguments[0]]
}
f2(1) // 严格模式下输出位[2,1]

delete

严格模式下,就变得严格的,当 delete运算符后跟随非法的标识符。将抛出一个语法错误。

1
2
3
4
5
6
7
'use strict'
delete a; // SyntaxError: applying the 'delete' operator to an unqualified name is deprecated
非严格模式下
delete a; // false

同名错误

在严格模式中,在一个对象直接量中定义两个或多个同名属性将会报错。

但在浏览器是了下同名并不会报错

1
2
3
4
5
6
7
8
重名的属性
'use strict'
var a = {
b: 1,
b: 2
}
严格模式和非严格模式下同名属性都不会报错。

重名的参数

1
2
3
4
5
6
7
"use strict";
function f(a, a, b) { // 语法错误
return ;
}

八进制

严格模式是不允许使用八进制整数直接量,以0为前缀,而不是已0x为前缀。

caller

匿名函数无法在内部调用自身

1
2
3
4
5
"use strict";
var f = function() { return arguments.callee; };
f(); // 报错 TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them

目的

  1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  2. 消除代码运行的一些不安全之处,保证代码运行的安全;
  3. 提高编译器效率,增加运行速度;
  4. 为未来新版本的Javascript做好铺垫。

缺点

现在网站的 JS 都会进行压缩,一些文件用了严格模式,而另一些没有。这时这些本来是严格模式的文件,被 merge 后,这个串就到了文件的中间,不仅没有指示严格模式,反而在压缩后浪费了字节。

更多内容可以参考阮老师的文章 Javascript 严格模式详解