JavaScript严格模式

介绍

ECMAscript 5添加了“严格模式”,会使得Javascript在更严格的条件下运行,设立"严格模式"的目的,主要有以下几个:

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

说明
此文参考了阮一峰的Javascript 严格模式详解,做个总结笔记。对于原文中的例子我都一一测试了,但有个别测试结果是不一样的,后面已做说明。那些我也是比较迷惑的,如果有错误,希望指出。

 

开始

启用严格模式很简单,只需要一个字符串

"use strict";

但是这个字符串的位置不是随意放的,有两种:

  • 全局严格模式,放在脚本文件的第一行
  • 局部严格模式,放在函数内第一行(推荐)

为什么推荐使用在函数内的严格模式呢?

因为全局严格模式不利于代码的合并,团队多人开发时,合并代码可能会使别人某些代码失效。

所以更好的做法是,借用局部严格模式方法,将整个脚本文件放在一个立即执行的匿名函数之中:

(function (){

  "use strict";
  // some code here

})();

 

思考

为什么是一个字符串启用严格模式?是为了兼容老旧的浏览器,一行字符串不会对不兼容严格模式的浏览器产生影响。

 

改变

严格模式带来了很多语法的改变。

变量赋值前必须声明

通常我们可以直接对一个变量赋值而不需要提前var声明,此时这个变量就是全局变量。严格模式禁止这种用法,全局变量必须显式声明。

"use strict"

a = 2;   //报错

因此,严格模式下,变量都必须先用var命令声明,然后再使用。

 

禁止使用with

正常模式下,我们可以使用with来改变作用域链,如:

var obj = {
   num:1
}
function test(){
   var num = 2;
   with(obj){
    console.log(num);
   }
}
test();  //1

但是在严格模式下,禁用了with,报错:

"use strict";

test();
//Strict mode code may not include a with statement

 

创建eval作用域

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

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

"use strict";

var x = 5;

console.log(eval('var x = 10;console.log(x)')); //10

console.log(x);  //5

 

局部this必须赋值

正常模式下,函数编译时,内部this指向的是全局window对象,但是严格模式时,this不再指向window,而是undefined。你需要自己手动赋值,赋值是什么,this就是什么。

"use strict";

console.log('window: ',this);  //window

function test(){
    console.log('test: ',this);  //undefined
}

test();

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

function fn(){
  "use strict";
  this.a = 1;
};
fn(); // 报错

 

arguments对象的限制

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

不允许对arguments赋值

"use strict";

var arguments = 5;  //报错

function arguments(){   //报错
    //some code
}

 

arguments不再追踪参数的变化

//正常模式
function test(a){
    a = 5;
    console.log([a,arguments[0]])
}

test(2);  //[5,5]



//严格模式

"use strict";

function test(a){
    a = 5;
    console.log([a,arguments[0]])
}

test(2);  //[5,2]

 

禁止使用arguments.callee

arguments.callee可以返回正被执行的函数对象

//正常模式
function test(){
    console.log(arguments.callee);
}

test();
// test(){
//    console.log(arguments.callee);
//}

严格模式不允许再使用arguments.callee

 

禁止使用caller

正常模式下,可以使用caller返回一个函数对象,这个函数调用了当前函数:

function test(){
    demo();
}

function demo(){
    console.log(demo.caller);
}

test();  
// test(){
//    demo();
//}

严格模式禁止再使用caller。所以两个长得很像的callee和caller在严格模式下都不能再使用。

 

函数必须声明在顶层

什么意思呢?

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

//正常模式

if(true){
    function fn(){
        console.log('fn');
    }

    fn();   // fn
}

fn();   // fn
"use strict";

if(true){
    function fn(){
        console.log('fn');
    }

    fn();   // fn
}

fn();  // 报错  fn is not defined

 

重名错误

对象不能有重名属性

按照 阮一峰的文章,在严格模式下,对象是不准有重名属性的,会报错。但实际测试中(谷歌浏览器),严格模式下,对象属性重名并不会报错,而是像正常模式一样,后面的覆盖前面的。

"use strict";

var obj = {
   a:1,
   a:2
}

console.log(obj.a);  //2

后来了解到,ES6中的严格模式已经允许对象有重名的属性。如果有了解的,可以告诉我下。

 

函数不能有重名的参数

正常模式下,参数重名,后面的覆盖前面的:

function test(a,a,b){
    console.log(a,a,b);
}

test(1,2,3); // 2 2 3

但在严格模式下,会报错:

"use strict";

function test(a,a,b){
    console.log(a,a,b);
}

test(1,2,3);
// 报错 Duplicate parameter name not allowed in this context

 

禁止删除变量

按照 阮一峰的文章,严格模式下无法删除变量。只有configurable设置为true的对象属性,才能被删除。

但是即使在正常模式下,用var声明的变量也是无法删除的,不管是全局声明还是局部声明,不过可以删除对象属性:

var obj = {
    a:2
}

delete obj.a;   //true

obj //{}

在严格模式下,用var声明的变量也是无法删除的,但对象的属性也是可以删除的:

"use strict";

var obj = {
    a:2
}

delete obj.a;   //true

obj //{}

需要注意的是,在正常模式下,即使变量不可以删除,你也可以写入 delete,不会报错,但是严格模式下,删除不了的变量不可以用delete

"use strict";

var a = 2;

var obj = {
    b:3
}

delete a;  //报错,不可删除就不能使用delete

 

禁止八进制表示法

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

"use strict";
var num = 0100;
console.log(num);  // 报错

但是es6提供了一种八进制数的新表示法,就是在数值前加上0o(第一个是数字0,第二个是字母o)

"use strict";
var num = 0o100;
console.log(num);

 

保留字

为了向将来Javascript的新版本过渡,严格模式新增了一些保留字:implements, interface, let, package, private, protected, public, static, yield。

使用这些词作为变量名将会报错。

"use strict";

var let;  //报错

说到保留字let,我们知道es6已经加入了letconstconst不管是在正常模式还是严格模式下都不可作为变量名。因为各大浏览器自行增加的const保留字,所以不能作为变量名的。

posted @ 2018-01-13 19:46  黑色瓶子  阅读(1033)  评论(0编辑  收藏  举报