"use strict";
name = "Frankie"; // Uncaught ReferenceError: name is not defined
for (i = 0; i < 2; i++) { // Uncaught ReferenceError: i is not defined
// ...
}
// 上述代码在正常模式下,是可以正常运行的,而在严格模式下就会报错(引用类型错误)
"use strict";
var obj = {
name: "Frankie"
}
// 语法错误,Uncaught SyntaxError: Strict mode code may not include a with statement
with (obj) {
name = "Mandy";
}
// 构造函数
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.
"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;
"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
"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() { } // 同样合法
}
一、概念
除了正常运行模式,ECMAScript 5 添加了第二种运行模式:严格模式(strict mode)。顾名思义,这种模式使得 JavaScript 在更严格的条件下运行。
设立严格模式的目的,主要有以下几个:
严格模式体现了 JavaScript 更合理、更安全、更严谨的发展方向,包括 IE10 在内的主流浏览器都已经支持它,许多大项目已经开始全面拥抱它。
另一方面,同样的代码,在严格模式中,可能会有不一样的运行结果;一些在正常模式下可以运行的语句,在严格模式下将不能运行。
二、启用严格模式
启用严格模式很简单,就一行语句。(分号可以显式显示,也可以通过自动分号插入。)
不支持该模式的浏览器,会把它当作一行普通字符串,加以忽略。
三、调用严格模式
严格模式可以应用到整个脚本或个别函数中。不要在封闭大括弧
{}
内这样做,在这样的上下文中这么做是没有效果的。严格模式有两种调用方法,适用于不同的场合。
1. 针对整个脚本文件
将
"use strict";
放在脚本文件的第一行,则整个脚本都将以严格模式运行。如果这行语句不在第一行,则无效,整个脚本以正常模式运行。上述代码表示,一个网页中依次有两段 JavaScript 代码。前一个
<script>
标签是严格模式,后一个是非严格模式。2. 针对单个函数
将
"use strict";
放在函数体的第一行,则整个函数以严格模式运行。3. 脚本文件的变通写法
因为第一种调用方法不利于文件合并,所以更好的做法是借用第二种方法,将整个脚本文件放在一个立即执行的匿名函数之中。
4. 关于 "use strict" 放在 Program 或 FunctionBody 的第一行问题
例如:
ES5 会把
"use bar"
和"abc"
也作为指令序言的某个指令处理,由于 JS 引擎不认识该指令,只认识"use strict"
指令,则同样会进入严格模式.四、严格模式对于语法和行为的改变
严格模式对 JavaScript 的语法和行为,都做了一些改变。
1. 全局变量显式声明
在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。
因此,严格模式下变量都必须先声明再使用。抛开 JavaScript 设计的不合理、缺陷、甚至是 Bug,或者是其他看起来很反人类的东西,在了解历史原因和其中原理之后,为了代码可读性都理应如此。
2. 静态绑定
JavaScript 语言的一个特点,就是允许“动态绑定”,即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的。
严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。这样做有利于编译效率的提高,也使得代码更容易阅读,更少出现意外。
具体来说,涉及以下几个方面。
(1)禁止使用 with 语句
因为
with
语句无法在编译时就确定,属性到底归属哪个对象。(2)创设 eval 作用域
正常模式下,JavaScript 语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval 作用域。
正常模式下,
eval
语句的作用域,取决于它处于全局作用域,还是处于函数作用域。严格模式下,eval
语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于 eval 内部。3. 增强的安全措施
(1)禁止 this 关键字指向全局对象
因此,使用构造函数时,如果忘加
new
关键字时,this
不再指向全局对象,而是报错。(2)禁止在函数内部遍历调用栈
4. 禁止删除变量
严格模式下无法删除变量。
只有
configurable
设置为true
的对象属性,才能被删除。5. 显式报错
正常模式下,对一个对象的只读属性进行赋值,不会报错,只会默默地失败。严格模式下,将报错。
严格模式下,对一个使用
getter
方法读取的属性进行赋值,会报错。严格模式下,对禁止扩展的对象添加新属性,会报错。
严格模式下,删除一个不可删除的属性,会报错。
6. 重名错误
严格模式新增了一些语法错误。
(1)对象不能有重名的属性
在 Gecko 版本 34 之前,严格模式要求一个对象内的所有属性名在对象内必须唯一。正常模式下重名属性是允许的,最后一个重名的属性决定其属性值。因为只有最后一个属性起作用,当代码要去改变属性值而不是修改最后一个重名属性的时候,复制这个对象就产生一连串的 bug。在严格模式下,重名属性被认为是语法错误:
(2)函数不能有重名的参数
正常模式下,如果函数有多个重名的参数,最后一个重名参数名会掩盖之前的重名参数,之前的参数仍然可以通过
arguments[i]
来访问。然而,这种隐藏毫无意义而且可能是意料之外的 (比如它可能本来是打错了),所以在严格模式下重名参数被认为是语法错误。7. 禁止八进制表示法
ECMAScript 并不包含八进制语法,但所有的浏览器都支持这种以零(
0
)开头的八进制语法:0100 === 64
,还有"\045" === "%"
。在 ECMAScript 6 中支持为一个数字加"0o"
的前缀来表示八进制数.8. arguments 对象的限制
arguments 是函数的参数对象,严格模式对它的使用做了限制。
(1)不允许对arguments赋值
(2)arguments不再追踪参数的变化
(3)禁止使用 arguments.callee
这意味着,你无法在匿名函数内部调用自身了。
9. 函数必须声明在顶层
将来 JavaScript 的新版本会引入"块级作用域"。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。
严格模式禁止了不在脚本或者函数层面上的函数声明。在浏览器的普通代码中,在“所有地方”的函数声明都是合法的。这并不在 ES5 规范中(甚至是 ES3)!这是一种针对不同浏览器中不同语义的一种延伸。未来的 ECMAScript 版本很有希望制定一个新的,针对不在脚本或者函数层面进行函数声明的语法。在严格模式下禁止这样的函数声明对于将来 ECMAScript 版本的推出扫清了障碍:
10. 保留字
为了向将来 JavaScript 的新版本过渡,严格模式新增了一些保留字:
implements
、interface
、let
、package
、private
、protected
、public
、static
、yield
。使用这些词作为变量名将会报错。此外,ECMAScript 5 本身还规定了另一些保留字(
class
、enum
、export
、extends
、import
、super
),以及各大浏览器自行增加的const
保留字,也是不能作为变量名的。未完待续...
参考