【拓展】
严格模式是限制性更强的JavaScript变体,旨在改善错误检查功能,并且标识可能不会延续到未来JavaScript版本的脚本。与常规的JavaScript语义不同,其分析更为严格,下面介绍严格模式对Javascript语法和行为限制性规定。
显式声明变量
Javascript是弱类型语言,不使用var声明的变量默认会转为全局变量。但在严格模式中将不允许,会报语法错误。【示例1】执行下面代码,将会提示语法错误。
"use strict"; v = 1; // 报错,v未声明 for(i = 0; i < 2; i++) { // 报错,i未声明 }
因此,严格模式下,变量都必须先用var命令声明,然后再使用。
静态绑定
在正常模式下,Javascript允许动态绑定,即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时确定的。
严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定。也就是说,属性和方法到底归属哪个对象,在编译阶段就确定。这样限制有利于编译效率的提高,使得代码更容易阅读,避免出现意外。具体来说,涉及以下几个方面。
- 禁止使用with语句
因为with语句无法在编译时就确定,属性到底归属哪个对象。例如:
"use strict"; var v = 1; with (o){ // 语法错误 v = 2; }
- 创设eval作用域
在正常模式下,Javascript有两种变量作用域:全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。
在正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。而在严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部。例如:
"use strict"; var x = 2; console.info(eval("var x = 5; x")); // 5 console.info(x); // 2
另外,任何使用'eval'的操作都会被禁止,例如,下面用法都是非法的。
'use strict' var obj = {} var eval = 3 obj.eval = 1 obj.a = eval for (var eval in obj) {} function eval() {} function func(eval) {} var func = new Function('eval')
增强的安全措施
- 禁止this关键字指向全局对象
【示例2】执行下面代码,比较正常模式和严格模式下this的值。
function f(){ return !this; } // 返回false,因为"this"指向全局对象,"!this"就是false function f(){ "use strict"; return !this; } // 返回true,因为在严格模式下,this的值为undefined,所以"!this"为true。
【示例3】使用构造函数时,如果忘了加new语句,this不再指向全局对象,而是报错。
function f(){ "use strict"; this.a = 1; }; f(); // 报错,this未定义
- 禁止在函数内部遍历调用栈
【示例4】caller、callee和arguments的调用行为都被禁用。
function f1(){ "use strict"; f1.caller; // 报错 f1.arguments; // 报错 } f1();
禁止删除变量
在严格模式下无法删除变量。只有configurable设置为true的对象属性,才能被删除。
【示例5】错误的删除操作。
"use strict"; var x; delete x; // 语法错误 var o = Object.create(null, 'x', { value: 1, configurable: true }); delete o.x; // 删除成功
显式报错
在正常模式下,对一个对象的只读属性进行赋值,不会报错,只会默默地失败。严格模式下,将报错。
【示例6】提示错误信息。
"use strict"; var o = {}; Object.defineProperty(o, "v", { value: 1, writable: false }); o.v = 2; // 报错
严格模式下,对一个使用getter方法读取的属性进行赋值,会报错。
"use strict"; var o = { get v() { return 1; } }; o.v = 2; // 报错
严格模式下,对禁止扩展的对象添加新属性,会报错。
"use strict"; var o = {}; Object.preventExtensions(o); o.v = 1; // 报错
严格模式下,删除一个不可删除的属性,会报错。
"use strict"; delete Object.prototype; // 报错
重名错误
严格模式新增了一些语法错误。
- 对象不能有重名的属性
在正常模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。在严格模式下,这属于语法错误。例如:
"use strict"; var o = { p: 1, p: 2 }; // 语法错误
- 函数不能有重名的参数
在正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。在严格模式下,这属于语法错误。例如:
"use strict"; function f(a, a, b) { // 语法错误 return ; }
禁止八进制表示法
在正常模式下,整数的第一位如果是0,表示这是八进制数,如0100等于十进制的64。在严格模式中将禁止这种表示法,整数第一位为0,将报错。例如:
"use strict"; var n = 0100; // 语法错误
arguments对象的限制
arguments是函数的参数对象,严格模式对它的使用进行限制。- 不允许对arguments赋值
【示例7】下面代码演示了无法对arguments对象写操作。
"use strict"; arguments++; // 语法错误 var obj = { set p(arguments) { }}; // 语法错误 try { } catch (arguments) { } // 语法错误 function arguments() { } // 语法错误 var f = new Function("arguments", "'use strict'; return 17;"); // 语法错误
- arguments不再追踪参数的变化
function f(a) { a = 2; return [a, arguments[0]]; } f(1); // 正常模式为[2,2] function f(a) { "use strict"; a = 2; return [a, arguments[0]]; } f(1); // 严格模式为[2,1]
- 禁止使用arguments.callee
这意味着,无法在匿名函数内部调用自身了。例如:
"use strict"; var f = function() { return arguments.callee; }; f(); // 报错
函数必须声明在顶层
将来Javascript的新版本会引入块级作用域。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。
【示例8】下面代码演示了函数不能够用在条件和循环语句中。
"use strict"; if (true) { function f() { } // 语法错误 } for (var i = 0; i < 5; i++) { function f2() { } // 语法错误 }
保留字
为了向未来Javascript新版本过渡,严格模式新增了一些保留字:implements、interface、 let、package、private、protected、public、static、yield。使用这些词作为变量名将会报错。
【示例9】下面代码显示implements是保留字,并禁止使用。
function package(protected) { // 语法错误 "use strict"; var implements; // 语法错误 }
此外,ECMAscript 5本身还规定了另一些保留字,如class、enum、export、extends、import、super,以及各大浏览器自行增加的const保留字,也是不能作为变量名的。
动态绑定
- call、apply的第一个参数直接传入不包装为对象
【示例10】在下面代码中,输出依次为"string"、"number"。而在非严格模式中call、apply将对值类型的"abcd"包装为对象后传入,即两次输出都为"object"。
'use strict' function func() { console.log(typeof this) } func.call('abcd') // string func.apply(1) // number
- call、apply的第一个参数为null、undefined时,this为null、undefined
【示例11】下面代码输出依次是undefined、null,而在正常模式中则是宿主对象(浏览器里是window,node.js环境则是global)。
'use strict' function func() { console.log(this) } func.call(undefined) // undefined func.call(null) // null
- bind的第一个参数为null、undefined时,this为null/undefined
bind是ECMAScript 5给Function.prototype新增的一个方法,它和call、apply一样在function上直接调用。它返回一个指定了上下文和参数的函数。当它的第一个参数为null、undefined时,情形和call、apply一样,this也为null、undefined。
【示例12】下面代码在非严格模式中输出都是window(或global)。
'use strict' function func() { console.log(this) } var f1 = func.bind(null) var f2 = func.bind(undefined) f1() // null f2() // undefined