文章目录
  1. 1. 概述
  2. 2. 开启严格模式
    1. 2.1. 为某个script标签开启严格模式
    2. 2.2. 为某个函数开启严格模式
    3. 2.3. Chrome中调试严格模式
  3. 3. 严格模式有哪些不同?
  4. 4. 代码在严格模式下受到的限制
    1. 4.1. 全局变量显式声明
    2. 4.2. 静态绑定
      1. 4.2.1. 禁止使用with语句
      2. 4.2.2. 创设eval作用域
    3. 4.3. 禁止删除变量
    4. 4.4. 增强的安全措施
      1. 4.4.1. 禁止this关键字指向全局对象,默认值undefined
      2. 4.4.2. caller/callee 被禁用
      3. 4.4.3. 显式报错
    5. 4.5. 重名错误
      1. 4.5.1. 对象不能有重名的属性
      2. 4.5.2. 函数不能有重名的参数
    6. 4.6. 禁止八进制表示法
    7. 4.7. eval和arguments对象的限制
      1. 4.7.1. 赋值和绑定
      2. 4.7.2. arguments不再追踪参数的变化
      3. 4.7.3. 禁止使用arguments.callee
    8. 4.8. 函数必须声明在顶层
    9. 4.9. 保留字
  5. 5. 向严格模式过度
    1. 5.1. 逐步过渡
    2. 5.2. 过程
      1. 5.2.1. 语法错误
      2. 5.2.2. 运行时错误
      3. 5.2.3. 语义差异
    3. 5.3. 严格中立的代码

概述

ECMAScript 5 的严格模式是JavaScript中的一种限制性更强的变种方式。严格模式不是一个子集:它在语义上与正常代码有着明显的差异。不支持严格模式的浏览器与同支持严格模式的浏览器行为上也不一样, 所以不要在未经严格模式特性测试情况下使用严格模式。严格模式可以与非严格模式共存,所以脚本可以逐渐的选择性加入严格模式。
支持情况:IE 10在内的主流浏览器,都已经支持它,许多大项目已经开始全面拥抱。(github上面好多项目都是用的严格模式)
严格模式在语义上与正常的JavaScript有一些不同。

  • 首先,严格模式会将JavaScript陷阱直接变成明显的错误。
  • 其次,严格模式修正了一些引擎难以优化的错误:同样的代码有些时候严格模式会比非严格模式下更快。
  • 第三,严格模式禁用了一些有可能在未来版本中定义的语法。

    开启严格模式

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

    为某个script标签开启严格模式

    为整个script标签开启严格模式, 需要在所有语句之前放一个特定语句 "use strict"; (或 'use strict';)。
1
2
3
// 整个语句都开启严格模式的语法
"use strict";
var v = "Hi! I'm a strict mode script!";

如果你想定义一个模块或者一个小库,自然采用一个匿名函数自执行是不错的选择。同时解决了代码压缩产生的 "use strict"压缩后不在第一行的问题。

1
2
3
4
~function() {
"use strict";
// Define your library strictly...
}();

"use strict" 的位置是很讲究的,必须在首部。首部指其前面没有任何有效js代码。以下都是无效的,将不会触发严格模式。

  • “use strict” 前有代码
  • “use strict” 前有个空语句都不行
    1
    2
    3
    ;//空语句
    'use strict'
    globalVar = 100

当然,“use strict”前加注释是可以的

为某个函数开启严格模式

function strict() {
  // 函数级别严格模式语法
  'use strict';
  function nested() {
    return "And so am I!";
  }
  return "Hi!  I'm a strict mode function!  " + nested();
}

Chrome中调试严格模式

我有这么一段代码:

1
2
3
'use strict'
name = "reeoo";
console.log(name)

把这段代码直接粘贴到Chrome的控制台中执行,正常情况下应该报错,但是并没有报错。
stracit.png
很显然,严格模式下变量不适用var声明是不合法的,但是为什么没有报错?这是什么鬼,难道Chrome不支持严格模式?开什么玩笑。。。
网上搜了一下,原来Chrome的控制台的代码是运行在eval之中的,你没法对eval函数使用严格模式(应该也不完全对,但是具体Chrome做了什么,不得而知),总之要想在Chrome浏览器中对严格模式正常报错,需要在代码外层套一个立即执行函数,或者其它类似的措施。

1
2
3
4
5
(function(){
'use strict'
name = "reeoo";
console.log(name)
})()

严格模式有哪些不同?

  • 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;(将问题直接转化为错误,如语法错误或运行时错误)。
  • 提高编译器效率,增加运行速度。
  • 消除代码运行的一些不安全之处,保证代码运行的安全。
  • 为未来新版本的Javascript做好铺垫。

代码在严格模式下受到的限制

全局变量显式声明

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

"use strict";
mistypedVaraible = 17; // 报错,mistypedVaraible未声明

静态绑定

Javascript语言的一个特点,就是允许”动态绑定”,即某些属性和方法到底属于哪一个对象,不是在编译时确定的,而是在运行时(runtime)确定的。
具体来说,涉及以下几个方面。

禁止使用with语句

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

"use strict";
var x = 17;
with (obj) // !!! 语法错误
{
  // 如果没有开启严格模式,with中的这个x会指向with上面的那个x,还是obj.x?
  // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
  x;
}

创设eval作用域

正常模式下,Javascript语言有两种变量作用域(scope):全局作用域和函数作用域。
严格模式创设了第三种作用域:eval作用域。
正常模式下,eval语句的作用域,取决于它处于全局作用域,还是处于函数作用域。
严格模式下,eval语句本身就是一个作用域,不再能够生成全局变量了,它所生成的变量只能用于eval内部。

"use strict";
eval("var testvar = 10");
console.log(testvar);//在严格模式下报错,在非严格模式下 打印 10

禁止删除变量

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

"use strict";
var x;
delete x; // 语法错误

eval("var x; delete x;"); // !!! 语法错误

var o = Object.create(null, {'x': {
    value: 1,
    configurable: true
}});
delete o.x; // 删除成功

增强的安全措施

禁止this关键字指向全局对象,默认值undefined

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

caller/callee 被禁用

function parentCheck() {
    check("");
    function check() {
        subCheck();
        function subCheck() {
            console.log(arguments.caller);
            // ECMAScript 5 还定义了 arguments.caller 属性,但在严格模式下访问它也会导致错误,而在非严格模式下这个属性始终是 undefined。定义这个属性是为了分清arguments.caller 和函数的caller 属性。
            console.log(arguments.callee);
            console.log(subCheck.caller);
            console.log(subCheck.caller.caller);
        }
    }
}
parentCheck();

显式报错

NaN 是一个不可写的全局变量. 在正常模式下, 给 NaN 赋值不会产生任何作用; 开发者也不会受到任何错误反馈. 但在严格模式下, 给 NaN 赋值会抛出一个异常

"use strict";
NaN = 2;//报错

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

"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; // 报错

如果有setter 则不会报错。

"use strict"; 
var o = {
  get v() {
        return this.aa; 
    },
    set v(a){
        this.aa = a;
    }     
};  
o.v = 2; // 不会报错
console.log(o.v);

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

"use strict";  
var o = {};  
Object.preventExtensions(o);  
o.v = 1; // 报错

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

"use strict";  
delete Object.prototype; // 报错

重名错误

对象不能有重名的属性

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

"use strict";  
var o = {    
    p: 1,
    p: 2  
}; // 语法错误

函数不能有重名的参数

正常模式下,如果函数有多个重名的形参,不会报错,在严格模式下,这属于语法错误。

1
2
3
4
5
6
7
"use strict";  
function f(a, a, b) {
console.log(a,a,b);
// Duplicate parameter name not allowed in this context 
//正常模式 输出 1 2 3
}
f(1,2,3);

禁止八进制表示法

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

"use strict";  
var n = 0100; // 语法错误

eval和arguments对象的限制

赋值和绑定

首先, arguments, eval这两个在严格模式下不能做标识符或属性名

"use strict";
eval = 17;
arguments++;
++eval;
var obj = { set p(arguments) { } };
var eval;
try { } catch (arguments) { }
function x(eval) { }
function arguments() { }
var y = function eval() { };
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的新版本会引入”块级作用域”。为了与新版本接轨,严格模式只允许在全局作用域或函数作用域的顶层声明函数。也就是说,不允许在非函数的代码块内声明函数。

"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。
使用这些词作为变量名将会报错。此外,ECMAscript第五版本身还规定了另一些保留字(class, enum, export, extends, import, super),以及各大浏览器自行增加的const保留字,也是不能作为变量名的。

向严格模式过度

逐步过渡

严格模式被仔细设计过,因此可以逐渐地进行迁移。你可以分别改变各个文件,甚至以函数级的粒度迁移至严格模式。

过程

如果代码中使用”use strict”开启了严格模式,则下面的情况都会在脚本运行之前抛出SyntaxError异常:

语法错误

  • 八进制语法:var n = 023和var s = “\047”
  • with语句
  • 使用delete删除一个变量名(而不是属性名):delete myVariable
  • 使用eval或arguments作为变量名或函数名
  • 使用未来保留字(也许会在ECMAScript 6中使用):implements, interface, let, package, private, protected, public, static,和yield作为变量名或函数名
  • 在语句块中使用函数声明:if(a<b){ function f(){} }
  • 其他错误:
    • 对象子面量中使用两个相同的属性名:{a: 1, b: 3, a: 7}
    • 函数形参中使用两个相同的参数名:function f(a, b, b){}

这些错误是有利的,因为可以揭示简陋的错误和坏的实践,这些错误会在代码运行前被抛出。

运行时错误

JavaScript曾经会在一些上下文的某些情况中静默的失败,严格模式会在这些情况下抛出错误。如果你的代码包含这样的场景,请务必测试以确保没有代码受到影响。再说一次,严格模式是可以设置在代码粒度下的。

  1. 给一个未声明的变量赋值
  2. 改变一个全局对象的值可能会造成不可预期的后果。如果你真的想设置一个全局对象的值,把他作为一个参数并且明确的把它作为一个属性。
    var global = this; // in the top-level context, "this" always refers the global object
    function f() {
        "use strict";
        var a = 12;
        global.b = a + x * 35;
    }
    f();
    
    ·
  3. 尝试删除一个不可配置的属性。

    "use strict";
    delete Object.prototype; // error!
    

    在非严格模式中,这样的代码只会静默失败,这样可能会导致用户误以为删除操作成功了.

  4. arguments对象和函数属性

在严格模式下,访问arguments.callee, arguments.caller, anyFunction.caller以及anyFunction.arguments都会抛出异常.唯一合法的使用应该是在其中命名一个函数并且重用之

语义差异

  1. 函数调用中的this
    在普通的函数调用f()中,this的值会指向全局对象。在严格模式中,this的值会指向undefined。当函数通过call和apply调用时,如果传入的”thisvalue”参数是一个null和undefined除外的原始值(字符串,数字,布尔值),则this的值会成为那个原始值对应的包装对象,如果”thisvalue”参数的值是undefined或null,则this的值会指向全局对象。在严格模式中,this的值就是thisvalue参数的值,没有任何类型转换。
  2. arguments对象属性不与对应的形参变量同步更新
    在非严格模式中,修改arguments对象中某个索引属性的值,和这个属性对应的形参变量的值也会同时变化,反之亦然.这会让JavaScript的代码混淆引擎让代码变得更难读和理解。在严格模式中arguments 对象会以形参变量的拷贝的形式被创建和初始化,因此 arguments 对象的改变不会影响形参。
  3. eval相关的区别
    在严格模式中,eval不会在当前的作用域内创建新的变量。另外,传入eval的字符串参数也会按照严格模式来解析。你需要全面测试来确保没有代码收到影响。另外,如果你并不是为了解决一个非常实际的解决方案中,尽量不要使用eval。

严格中立的代码

迁移严格代码至严格模式的一个潜在消极面是,在遗留的老版本浏览器上,由于没有实现严格模式,javascript语义可能会有所不同。在一些罕见的机会下(比如差劲的关联关系或者代码最小化),你的代码可能不能按照你书写或者测试里的模式那样运行。这里有一些让你的代码保持中立的规范:

  1. 按照严格模式书写你的代码,并且确保你的代码不会发生仅仅在严格模式下发生的错误(比如上文所说的运行时错误)。
  2. 原理语义的歧义:
    • eval: 仅仅在你知道你在干什么的情况下使用它
    • arguments: 总是通过他们的名字访问函数的参数,或者作为参数对象的拷贝来使用: var args = Array.prototype.slice.call(arguments),并且这样的代码应该在你的函数第一行
    • this: 只在它指向你之前创建的对象的情况下使用 this

摘自:MDN-严格模式
摘自:MDN-向严格模式过渡

文章目录
  1. 1. 概述
  2. 2. 开启严格模式
    1. 2.1. 为某个script标签开启严格模式
    2. 2.2. 为某个函数开启严格模式
    3. 2.3. Chrome中调试严格模式
  3. 3. 严格模式有哪些不同?
  4. 4. 代码在严格模式下受到的限制
    1. 4.1. 全局变量显式声明
    2. 4.2. 静态绑定
      1. 4.2.1. 禁止使用with语句
      2. 4.2.2. 创设eval作用域
    3. 4.3. 禁止删除变量
    4. 4.4. 增强的安全措施
      1. 4.4.1. 禁止this关键字指向全局对象,默认值undefined
      2. 4.4.2. caller/callee 被禁用
      3. 4.4.3. 显式报错
    5. 4.5. 重名错误
      1. 4.5.1. 对象不能有重名的属性
      2. 4.5.2. 函数不能有重名的参数
    6. 4.6. 禁止八进制表示法
    7. 4.7. eval和arguments对象的限制
      1. 4.7.1. 赋值和绑定
      2. 4.7.2. arguments不再追踪参数的变化
      3. 4.7.3. 禁止使用arguments.callee
    8. 4.8. 函数必须声明在顶层
    9. 4.9. 保留字
  5. 5. 向严格模式过度
    1. 5.1. 逐步过渡
    2. 5.2. 过程
      1. 5.2.1. 语法错误
      2. 5.2.2. 运行时错误
      3. 5.2.3. 语义差异
    3. 5.3. 严格中立的代码