严格模式

# 严格模式

参考链接:http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html

[TOC]

# 一、定义和优点

严格模式是一种特殊的执行模式,它修复了部分语言上的不足(禁用with),提供了更强的错误检查(重复属性,删除delete不可配置的属性等),并增强了安全性(在eval中使用独立作用域等)。

# 二、模式的使用

  1. function func(){'use strict';}好处:向上兼容。
  2. 'use strict'; function func(){}指定整个js内的代码都是在严格模式下。

# 三、与普通模式的区别

# 3.1 变量声明

# 3.1.1 全局变量显式声明。

'use strict';
a = 1;		// a is not defined
1
2

# 3.2 静态绑定

严格模式对动态绑定做了一些限制。某些情况下,只允许静态绑定,即属性和方法到底归属哪个对象,在编译阶段就确定了。

# 3.2.1 禁止使用with语句

  • 定义with语句主要是为了简化多次编写同一个对象的工作。
  • 在with语句的代码块内部,每个变量首先被认为是一个局部变量,如果在局部环境中找不到该变量的定义,就会查询with对象中是否具有同名的属性。
  • 大量使用with语句会导致性能下降,同时造成调试困难,不适用于大型应用程序使用。
  • 因为with语句无法在编译时就确定,属性到底归属哪个对象。
// SyntaxError: Strict mode code may not include a with statement
'use strict';
var obj = {};
obj.a = 1;
with(obj){
    var b = a;
}
console.log(b);	// 非严格模式下则输出1
1
2
3
4
5
6
7
8

# 3.2.2 eval变为独立作用域

参考链接:你不知道的 eval (opens new window)

  • 正常模式下,Javascript语言有两种变量作用域(scope):全局作用域和函数作用域。严格模式创设了第三种作用域:eval作用域。
  • 正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。
  • 严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部,其它地方不可以拿到eval的值。
"use strict";
var x = 2;
console.info(eval("var x = 5; x")); // 5
console.info(x); 					// 2
1
2
3
4

# 3.3 增强的安全措施

# 3.3.1 this不指向全局对象,而为undefined。

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

function f(){
    "use strict";
    this.a = 1;
};

f();// TypeError: Cannot set property 'a' of undefined
1
2
3
4
5
6

# 3.3.2 禁止在函数内部遍历调用栈。

  • callee是arguments的一个属性,这个属性是一个指针,指向这个拥有arguments对象的函数,可以用来消除函数内部调用自己的耦合性。
function fn1(){
    console.log(arguments.callee);        //fn1(){}
}
fn1();
1
2
3
4
  • arguments.length获得的是实参的个数,arguments.callee.length获得的是形参的个数。
  • caller是函数的一个属性。如果一个函数f是在全局作用域内被调用的,则f.caller为null。相反,如果一个函数是在另外一个函数作用域内被调用的,则f.caller指向调用它的那个函数。
function f1(){
    "use strict";
    f1.caller;
    f1.arguments;
  }
f1();
// TypeError: 'caller', 'callee', and 'arguments' properties 
// may not be accessed on strict mode functions or the arguments objects for calls to them
1
2
3
4
5
6
7
8

# 3.4 禁止删除变量

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

"use strict";
var x;
delete x; // SyntaxError: Delete of an unqualified identifier in strict mode.

var o = Object.create(null, {'x': {
    value: 1,
    configurable: true
}});
console.log(o.x);	// 1
delete o.x;
console.log(o.x);	// undefined
1
2
3
4
5
6
7
8
9
10
11

# 3.5 显式报错

# 3.5.1 禁止对只读属性赋值。

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

'use strict';
var o = Object.create(null, {'x': {
    value: 1,
    writable: false
}});
o.x = 2;
// TypeError: Cannot assign to read only property 'x' of object '[object Object]'
1
2
3
4
5
6
7

# 3.5.2 禁止对getter方法读取的属性赋值。

"use strict";
var o = {
    get v() { return 1; }
};
o.v = 2; // TypeError: Cannot set property v of #<Object> which has only a getter
1
2
3
4
5

# 3.5.3 禁止对禁止扩展的对象添加属性。

"use strict";
var o = {};
Object.preventExtensions(o);
o.v = 1; // TypeError: Cannot add property v, object is not extensible
1
2
3
4

# 3.5.4 禁止删除不可删除的属性。

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

# 3.6 重名错误

# 3.6.1 对象不能有重名的属性。

正常模式下,如果对象有多个重名属性,最后赋值的那个属性会覆盖前面的值。严格模式下,这属于语法错误。(亲测不实)

"use strict";
var o = {
    p: 1,
    p: 2
}; // 并没有报错
1
2
3
4
5

# 3.6.2 函数不能有重名的参数。

正常模式下,如果函数有多个重名的参数,可以用arguments[i]读取。严格模式下,这属于语法错误。

"use strict";
function fn(a, a){return;}
// SyntaxError: Duplicate parameter name not allowed in this context
1
2
3

# 3.7 禁止八进制表示法

# 3.7.1 禁止整数第一位为0。

正常模式下,整数的第一位如果是0,表示这是八进制数,比如0100等于十进制的64。严格模式禁止这种表示法,整数第一位为0,将报错。

"use strict";
let a = 0100;
// SyntaxError: Octal literals are not allowed in strict mode.
1
2
3

# 3.8 arguments对象的限制

# 3.8.1 禁止对arguments赋值。

"use strict";

arguments++; // SyntaxError: Unexpected eval or arguments in strict mode

var obj = { set p(arguments) { } }; // 同上

try { } catch (arguments) { } // 同上

function arguments() { } // 同上

var f = new Function("arguments", "'use strict'; return 17;"); // 同上
1
2
3
4
5
6
7
8
9
10
11

# 3.8.2 arguments不再追踪参数的变化。

不管参数传与不传,对arguments无影响,但是对象的属性除外

!function(a) {
    arguments[0] = 100;
    console.log(a);	// 100
}(1);

// 未传参,失去绑定关系,始终为undefined
!function(a) {
    arguments[0] = 100;
    console.log(a);	// undefined
}();

!function(a) {
    'use strict';
    arguments[0] = 100;
    console.log(a);	// 1
}(1);

!function(a) {
    'use strict';
    arguments[0].x = 100;
    console.log(a.x);	// 100
}({x:1});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 3.8.3 禁止使用arguments.callee。

"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
2
3
4
5

# 3.9 函数声明必须在顶层

# 3.9.1 只允许在全局作用域或函数作用域的顶层声明函数。

不允许在非函数的代码块内声明函数。(亲测不实)

"use strict";
if (true) {
    function f() { } // 亲测未报错
}
for (var i = 0; i < 5; i++) {
    function f2() { } // 亲测未报错
}
1
2
3
4
5
6
7