首页 > 技术文章 > ES5 规范之严格模式详解

XH-jing 2020-03-08 22:02 原文

前言

ECMAScript 5最早引入了“严格模式”(strict mode)的概念。通过严格模式,可以在函数内部存在的错误,及时捕获一些可能导致编程错误的ECMAScript 行为。

理解严格模式的规则非常重要,ECMAScript的下一个版本将以严格模式为基础制定。支持严格模式的浏览器包括IE10+、Firefox 4+、Safari 5.1+和Chrome。

严格模式的目的

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

严格模式的开启

想要开启严格模式,直接在作用域开始的位置写上字符串 "use strict";

在全局模式下开启:

"use strict";

 

在局部模式下开启(在函数中打开严格模式):

function fn(){
    "use strict";
    // 其他代码
}

 

严格模式的规则(执行限制)

1、变量 var:消除了伪全局变量

在严格模式下,什么时候创建变量以及怎么创建变量都是有限制的。不允许意外创建全局变量(就是严格模式下消除了伪全局变量)

 1 // 注意:(function(){})()是一个匿名函数
 2         (function () { 
 3             a = 10;
 4             console.log(a);
 5         })(); // 结果:10
 6         
 7         (function () {
 8             "use strict";
 9             a = 10;
10         })();  // 报错:ReferenceError: a is not defined

 

在非严格模式下,即使变量 a 前面没有 var 关键字,即使没有将它定义为某个全局对象的属性,也能将 a 创建为伪全局变量使用。
在严格模式下,如果给一个没有声明的变量赋值,那代码在执行的适合就会抛出 ReferenceError(引用错误)

2、对函数参数的要求:更为严格

严格模式要求命名函数的参数必须唯一

 1         (function () { 
 2             function foo(a ,a, b){
 3                  console.log(a,b);
 4              }
 5             foo(1,2,3)
 6          })();  // 结果: 2 3
 7          
 8          (function () { 
 9              "use strict";
10              function foo(a ,a, b){
11                  console.log(a,b);
12              }
13              foo(1,2,3)
14          })(); // 报错:SyntaxError: Duplicate parameter name not allowed in this context

 

在非严格模式下,这个函数声明不会抛出错误。通过参数名只能访问重复参数的第二个参数,要访问第一个重复参数,必须通过arguments对象(请见下一条)。
在严格模式下,上面函数参数的不规范会抛出 SyntaxError(对象代表尝试解析语法上不合法的代码的错误)

3、实参形参 和 arguments 的分离

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

 1        // arguments 会受到 形参赋值的影响;
 2         (function(){
 3             function foo(a,b){
 4                 a = 20;
 5                 console.log(a,b);  // 20  2
 6                 console.log(arguments[0],arguments[1]); // 20  2
 7             }
 8             foo(1,2)
 9         })(); 
10         
11         (function(){
12             "use strict";
13             // 形参 和 arguments 之间的区别;
14             // 形参是变量可以随意赋值;
15             // arguments 就是对应实参的关键字获取所有的实参,进行使用,不会被改变;
16             function foo(a,b){
17                 a = 20;
18                 console.log(a,b);  // 20 , 2 
19                 console.log(arguments[0],arguments[1]);  //  1 , 2  
20             }
21             foo(1,2)
22         })();

 

以上代码中,函数 foo() 传入两个参数 a,b 。调用这个函数时传入了两个参数“1,2”,这个值赋个了对应的变量。而在函数内部,a 的值被修改为“20”。
在非严格模式下,这个修改的值也会改变 arguments[0] 的值。
但在严格模式下,arguments[0] 的值仍然是传入的值。

4、arguments 的严格使用,部分功能禁用了

淘汰了 arguments.callee 和 arguments.caller。在非严格模式下,这两个属性一个是引用函数本身,一个是引用调用函数。而在严格模式下,这两个属性都被禁用了。

 1         (function(){
 2             function foo(){
 3                // arguments.callee 指向了当前的函数本身;
 4                console.log(arguments.callee);
 5             }
 6             foo()
 7         })();  // 结果 ƒ foo(){console.log(arguments.callee);}
 8         
 9         (function(){
10             "use strict";
11             function foo(){
12                // 禁用掉了大部分arguments的属性;
13                console.log(arguments.callee)
14             }
15             foo()
16         })();  // 报错:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments

 

类似的,尝试读写函数的 caller 属性也会抛出 TypeError(类型错误)。

5、严格模式之中对 this 的严格(抑制this)

JavaScript 中一个最大的安全问题,也是最容易让人迷惑的地方就是在某些情况下如何抑制 this 的值。在非严格模式下,null 或 undefined 值会被转换为全局对象window。而在严格模式下,函数的 this 值始终是指定的值,无论指定的是什么值。

 1         (function(){
 2             function foo(){
 3                 // this 指向 window;
 4                console.log(this);
 5             }
 6             foo()
 7         })(); // 结果:Window 
 8         
 9         (function(){
10             "use strict";
11             function foo(){
12                 // 禁用指向了 window 的 this,让 this 指向 undefined
13                console.log(this);
14             }
15             foo()
16         })();  // 结果:undefined

 

在以后的编程之中,this 最没有用的指向就是 全局对象window。

6、禁用 with(){} 语句

在非严格模式下的 with 语句能够改变解析标识符的路径,但在严格模式下,with 被抛弃了。因此,在严格模式下使用 with 会导致语法错误。

 1         (function(){    
 2             with(Math){
 3                 // 可以省略对象前置;
 4                 console.log(random()); // => Math.random()
 5                 console.log(PI);  // => Math.PI
 6             }
 7         })();  
 8         
 9         // 严格模式之下禁用 with(){}
10         (function(){
11             "use strict";
12             with(Math){
13                 console.log(random());
14                 console.log(PI);
15             }
16         })();  // 报错:Uncaught SyntaxError: Strict mode code may not include a with statement

 

在非严格模式下,with可以改变作用域链,他可以让他里面的代码的作用域链的最顶端变成with括号里面的这个对象,作用域链是经过很复杂的情况生成的结构,作用域链改了之后,系统内核会消耗大量的效率去更改作用域链,是会把程序变得非常慢的。所以ES5 的严格模式为了提高效率,禁用 with 语句

7、在严格模式之中被禁用的进制:不允许使用八进制

以 0 开头的八进制字面量过去经常会导致很多错误,在严格模式下,八进制字面量已经成为无效的语法了。

1         (function(){
2             // 0 开头就是八进制的标志;
3             console.log(012);
4         })(); // 结果: 10  (自动转化成十进制)
5         
6         (function(){
7             "use strict"
8             console.log(012)
9         })(); //报错:Uncaught SyntaxError: Octal literals are not allowed in strict mode.

 

补充:ES5 也修改了严格模式下的 parseInt() 的行为。如今八进制的字面量在严格模式下会被当作以 0 开头的十进制字面量。例如:

 1         (function(){
 2             var value = 012;
 3             console.log(value);
 4          })(); // 结果: 10 
 5          
 6          (function(){
 7             "use strict";
 8             var value = parseInt("012");
 9             console.log(value); 
10          })(); // 结果: 12 

 


如有遗漏之处欢迎评论区留言。

推荐阅读