JavaScript 严格模式详解

简介: JavaScript 严格模式详解

正文


一、概念


除了正常运行模式,ECMAScript 5 添加了第二种运行模式:严格模式(strict mode)。顾名思义,这种模式使得 JavaScript 在更严格的条件下运行。

与之相反的非严格模式,被称为“sloppy mode”,也称之为“正常模式”。因为翻译原因,正常模式也被翻译为 —— 马虎模式/稀松模式/懒散模式。但这并不是一个官方术语,但是你会经常见到如上的一些说法,其意义就是指代非严格模式,即正常模式。

设立严格模式的目的,主要有以下几个:


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


严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向,包括 IE10 在内的主流浏览器都已经支持它,许多大项目已经开始全面拥抱它。

另一方面,同样的代码,在严格模式中,可能会有不一样的运行结果;一些在正常模式下可以运行的语句,在严格模式下将不能运行。


二、启用严格模式


启用严格模式很简单,就一行语句。(分号可以显式显示,也可以通过自动分号插入。)

"use strict";
// 或者
'use strict';
// 或者(由于 ASI 机制,编写代码时可省略分号)
'use strict'


不支持该模式的浏览器,会把它当作一行普通字符串,加以忽略。


三、调用严格模式


严格模式可以应用到整个脚本个别函数中。不要在封闭大括弧 {} 内这样做,在这样的上下文中这么做是没有效果的。

严格模式有两种调用方法,适用于不同的场合。


1. 针对整个脚本文件


"use strict"; 放在脚本文件的第一行,则整个脚本都将以严格模式运行。如果这行语句不在第一行,则无效,整个脚本以正常模式运行。

如果不同模式的代码文件合并成一个文件,这一点需要特别注意。

<script>
  "use strict";
  console.log("这是严格模式!");
</script>
<script>
  console.log("这是正常模式!");
</script>


上述代码表示,一个网页中依次有两段 JavaScript 代码。前一个 <script> 标签是严格模式,后一个是非严格模式。


2. 针对单个函数


"use strict"; 放在函数体的第一行,则整个函数以严格模式运行。

function strict() {
  "use strict";
  return "这是严格模式!";
}
function notStrict() {
  return "这是正常模式!";
}


3. 脚本文件的变通写法


因为第一种调用方法不利于文件合并,所以更好的做法是借用第二种方法,将整个脚本文件放在一个立即执行的匿名函数之中。


(function () {
  "use strict";
  // ...
})();


4. 关于 "use strict" 放在 Program 或 FunctionBody 的第一行问题


严格地说,只要前面不是产生实际运行结果的语句,"use strict"; 可以不在第一行,比如前面包括一些注释、或者是一些 JS 引擎无法识别的指令序言等。


例如:

function fn() {
  "use bar";
  "abc";
  "use strict"; // 因为这完全符合指令序言 — 多指令共存的语法. 所被应用的代码仍然会进入严格模式。
}


ES5 会把 "use bar""abc" 也作为指令序言的某个指令处理,由于 JS 引擎不认识该指令,只认识  "use strict" 指令,则同样会进入严格模式.


四、严格模式对于语法和行为的改变


严格模式对 JavaScript 的语法和行为,都做了一些改变。


1. 全局变量显式声明


在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。

"use strict";
name = "Frankie"; // Uncaught ReferenceError: name is not defined
for (i = 0; i < 2; i++) { // Uncaught ReferenceError: i is not defined
  // ...
}
// 上述代码在正常模式下,是可以正常运行的,而在严格模式下就会报错(引用类型错误)


因此,严格模式下变量都必须先声明再使用。抛开 JavaScript 设计的不合理、缺陷、甚至是 Bug,或者是其他看起来很反人类的东西,在了解历史原因和其中原理之后,为了代码可读性都理应如此。


2. 静态绑定


JavaScript 语言的一个特点,就是允许“动态绑定”,即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的。

严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。这样做有利于编译效率的提高,也使得代码更容易阅读,更少出现意外。

具体来说,涉及以下几个方面。


(1)禁止使用 with 语句


因为  with 语句无法在编译时就确定,属性到底归属哪个对象。

"use strict";
var obj = {
  name: "Frankie"
}
// 语法错误,Uncaught SyntaxError: Strict mode code may not include a with statement
with (obj) {
  name = "Mandy";
}


(2)创设 eval 作用域


正常模式下,JavaScript 语言有两种变量作用域(scope):全局作用域函数作用域。严格模式创设了第三种作用域:eval 作用域

正常模式下,eval 语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval 语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于 eval 内部。

"use strict";
var name = "Frankie";
console.log(eval("var name = 'Mandy'; name")); // "Mandy"
console.log(name); // "Frankie"


3. 增强的安全措施


(1)禁止 this 关键字指向全局对象

function fn1() {
  // 返回 false,因为 "this" 指向全局对象 
  return !this;
}
function fn2() {
  "use strict";
  // 返回 true,因为严格模式下,this 的值为 undefined。
  return !this;
}


因此,使用构造函数时,如果忘加 new 关键字时,this 不再指向全局对象,而是报错。

// 构造函数
function Fn() {
  "use strict";
  this.name = "Frankie"; // Uncaught TypeError: Cannot set property 'name' of undefined
};
// 直接当作普通函数调用就会报错,因为此时 this 为 undefined。
Fn();


(2)禁止在函数内部遍历调用栈

function fn() {
  "use strict";
  fn.arguments; // 报错
  fn.caller; // 报错
  // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
}
fn();


4. 禁止删除变量


严格模式下无法删除变量。

"use strict";
var name;
delete name; // 语法错误,Uncaught SyntaxError: Delete of an unqualified identifier in strict mode.


只有 configurable 设置为 true 的对象属性,才能被删除。

"use strict";
var obj = Object.create(null, {
  "name": {
    value: "Frankie",
    configurable: true
  }
});
delete obj.name; // 删除成功


5. 显式报错


正常模式下,对一个对象的只读属性进行赋值,不会报错,只会默默地失败。严格模式下,将报错。

"use strict";
var obj = {};
Object.defineProperty(obj, "name", { value: "Frankie", writable: false });
obj.name = "Mandy"; // 报错,Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'


严格模式下,对一个使用 getter 方法读取的属性进行赋值,会报错。

"use strict";
var obj = {
  get name() {
    return "Frankie";
  }
};
obj.name = "Mandy"; // 报错,Uncaught TypeError: Cannot set property name of #<Object> which has only a getter


严格模式下,对禁止扩展的对象添加新属性,会报错。

"use strict";
var obj = {};
Object.preventExtensions(obj);
obj.name = "Frankie"; // 报错,Uncaught TypeError: Cannot add property name, object is not extensible

严格模式下,删除一个不可删除的属性,会报错。

"use strict";
// 报错,Uncaught TypeError: Cannot delete property 'prototype' of function Object() { [native code] }
delete Object.prototype;


6. 重名错误


严格模式新增了一些语法错误。

(1)对象不能有重名的属性


在 Gecko 版本 34 之前,严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。因为只有最后一个属性起作用,当代码要去改变属性值而不是修改最后一个重名属性的时候,复制这个对象就产生一连串的 bug。在严格模式下,重名属性被认为是语法错误:


这个问题在 ECMAScript 6 中已经不复存在(bug 1041128)。

"use strict";
// 语法错误:SyntaxError: property name age appears more than once in object literal
var obj = {
  age: 18,
  age: 20
}


(2)函数不能有重名的参数


正常模式下,如果函数有多个重名的参数,最后一个重名参数名会掩盖之前的重名参数,之前的参数仍然可以通过 arguments[i] 来访问。然而,这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了),所以在严格模式下重名参数被认为是语法错误。

"use strict";
// 语法错误:Uncaught SyntaxError: Duplicate parameter name not allowed in this context
function fn(x, x, y) {
  return;
}


7. 禁止八进制表示法


ECMAScript 并不包含八进制语法,但所有的浏览器都支持这种以零(0)开头的八进制语法:0100 === 64,还有 "\045" === "%"。在 ECMAScript 6 中支持为一个数字加 "0o" 的前缀来表示八进制数.

"use strict";
var n = 0100; // 语法错误:Uncaught SyntaxError: Octal literals are not allowed in strict mode.

var n = 0o100; // ES6 八进制数表示法


8. arguments 对象的限制


arguments 是函数的参数对象,严格模式对它的使用做了限制。


(1)不允许对arguments赋值

"use strict";
arguments++; // 语法错误:Uncaught SyntaxError: Unexpected eval or arguments in strict mode
var obj = { set p(arguments) { } }; // 语法错误,同上
try { } catch (arguments) { } // 语法错误,同上
function arguments() { } // 语法错误,同上
var fn = new Function("arguments", "'use strict'; return 17;"); // 语法错误,同上


(2)arguments不再追踪参数的变化

function fn1(x) {
  x = 2;
  return [x, arguments[0]];
}
fn1(1); // 正常模式为 [2, 2]
function fn2(x) {
  "use strict";
  x = 2;
  return [x, arguments[0]];
}
fn2(1); // 严格模式为 [2, 1]


(3)禁止使用 arguments.callee


这意味着,你无法在匿名函数内部调用自身了。


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


9. 函数必须声明在顶层


将来 JavaScript 的新版本会引入"块级作用域"。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。


严格模式禁止了不在脚本或者函数层面上的函数声明。在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。这并不在 ES5 规范中(甚至是 ES3)!这是一种针对不同浏览器中不同语义的一种延伸。未来的 ECMAScript 版本很有希望制定一个新的,针对不在脚本或者函数层面进行函数声明的语法。在严格模式下禁止这样的函数声明对于将来 ECMAScript 版本的推出扫清了障碍:

"use strict";
if (true) {
  function f() { } // !!! 语法错误,SyntaxError: in strict mode code, functions may be declared only at top level or immediately within another function
  f();
}
for (var i = 0; i < 5; i++) {
  function f2() { } // !!! 语法错误,SyntaxError: in strict mode code, functions may be declared only at top level or immediately within another function
  f2();
}
function baz() { // 合法
  function eit() { } // 同样合法
}


关于这块内容,可以看下这两篇文章或讨论:


10. 保留字


为了向将来 JavaScript 的新版本过渡,严格模式新增了一些保留字:implementsinterfaceletpackageprivateprotectedpublicstaticyield。使用这些词作为变量名将会报错。


function package(protected) { // 语法错误,Uncaught SyntaxError: Unexpected strict mode reserved word
  "use strict";
  var implements; // 语法错误
}


此外,ECMAScript 5 本身还规定了另一些保留字(classenumexportextendsimportsuper),以及各大浏览器自行增加的 const 保留字,也是不能作为变量名的。

未完待续...


参考


目录
相关文章
|
2月前
|
JSON JavaScript 前端开发
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
JavaScript第五天(函数,this,严格模式,高阶函数,闭包,递归,正则,ES6)高级
|
2月前
|
Web App开发 JavaScript 前端开发
JavaScript 严格模式(use strict)
JavaScript 严格模式(use strict)
39 0
|
4月前
|
JavaScript 前端开发 安全
20 JavaScript学习:变量提升和严格模式
20 JavaScript学习:变量提升和严格模式
|
5月前
|
JavaScript 前端开发 安全
JS 严格模式和正常模式的区别
JS 严格模式和正常模式的区别
|
6月前
|
JavaScript 前端开发 安全
javascript中的严格模式(use strict)
javascript中的严格模式(use strict)
48 1
|
JavaScript 前端开发
JavaScript的严格模式
ECMAScript 6 首次引入严格模式的概念。严格模式用于选择以更严格的条件检查 JavaScript 代码错 误,可以应用到全局,也可以应用到函数内部。严格模式的好处是可以提早发现错误,因此可以捕获某 些 ECMAScript 问题导致的编程错误。 理解严格模式的规则非常重要,因为未来的 ECMAScript 会逐步强制全局使用严格模式。严格模式 已得到所有主流浏览器支持。
58 0
|
6月前
|
JavaScript 前端开发 安全
|
6月前
|
JavaScript 前端开发 安全
JavaScript高级主题:JavaScript 中的严格模式是什么?有什么好处?
JavaScript高级主题:JavaScript 中的严格模式是什么?有什么好处?
77 0
|
6月前
|
JavaScript 前端开发 安全
JavaScript 严格模式
JavaScript 严格模式
|
JavaScript 前端开发
JS严格模式详解
JS严格模式详解
169 0