严格模式(strict mode),于 ECMAScript 5 中提出。区别于正常模式(sloppy mode/马虎模式/稀松模式/懒散模式)

  • 消除语法上不合理、不严谨之处,减少怪异行为(通过抛出错误来消除了一些原有静默错误)
  • 优化变量使用,提高编译器效率,增加运行速度
  • 消除代码运行的不安全之处,保证代码运行的安全
  • 为未来新版本做铺垫

作用级别

  • 文件级别,应用整个代码文件
"use strict";
var a = "全部是严格模式";
console.log(a);
  • 函数级别,应用到某个函数
function strict() {
  "use strict";
  function nested() {
    return "这里也是严格模式";
  }
  return "这里是严格模式," + nested();
}

function nonStrict() {
  return "这里不是严格模式";
}

console.log(strict());
console.log(nonStrict());
  • 注意
    • 严格模式代码和正常模式代码可以共存
    • 严格模式文件和正常模式文件的合并可能会产生问题

具体的变化

消除语法上不合理、不严谨之处,减少怪异行为(通过抛出错误来消除了一些原有静默错误)

  • 未声明变量赋值报错
a = "hello";
console.log(a); // "hello"
// 未声明变量默认为全局变量
console.log(this.a); // "hello"
console.log(window.a); // "hello"
"use strict";
a = "hello";
  • 只读变量、属性赋值报错
"use strict";
undefined = 1;
"use strict";
NaN = 1;
"use strict";
var obj0 = {};
Object.defineProperty(obj0, "a", { value: 1, writable: false });
obj0.a = 2;
"use strict";
var obj1 = {
  get a() {
    return 1;
  }
};
obj1.a = 2;
  • 不可扩展对象添加新属性报错
"use strict";
var obj2 = { a: 1 };
Object.preventExtensions(obj2);
obj2.a = 2;
obj2.b = 1;
  • 不可删除对象删除报错
"use strict";
delete Object.prototype;
  • 对象声明重名属性报错,正常模式允许重名属性,其值为最后一个重名属性的值
"use strict";
var obj = { a: 1, a: 2 }; // 此段代码在支持 ECMAScript6 的环境中并不会报错
console.log(obj);
  • 函数的参数名不唯一报错
function test(a, a, b, c) {
  console.log(arguments[0], arguments[1], arguments[2], arguments[3]); // 需用 arguments 拿到正确的参数值
  console.log(a, a, b, c);
}
test(1, 2, 3, 4);
// 1 2 3 4
// 2 2 3 4
"use strict";
function test(a, a, b, c) {
  console.log(arguments[0], arguments[1], arguments[2], arguments[3]);
  console.log(a, a, b, c);
}
test(1, 2, 3, 4);
  • 使用 8 进制数字语法报错
var a = 010;
console.log(a);
"use strict";
var a = 010;
console.log(a);
// 补充:ECMAScript6 表示 8 进制
(function () {
  "use strict";
  var a = 0o10;
  console.log(a);
  var b = a.toString(8);
  console.log(b);
})();

// 补充:ECMAScript5 表示 8 进制
(function () {
  "use strict";
  var a = parseInt("10", 8);
  console.log(a);
  var b = a.toString(8);
  console.log(b);
})();
  • 原始数据类型属性设置报错
    • string, number, bigint, boolean, null, undefined, symbol
(function () {
  var a = "a";
  a.b = "b"; // 不报错,也不新增属性
  a.toString = function () {
    console.log("1");
  }; // 不报错,也不修改属性
  console.log(a.b); // undefined
  console.log(a.toString()); // "a"
})();

(function () {
  "use strict";
  var a = "a";
  a.b = "b";
  a.toString = function () {
    console.log("1");
  };
})();

优化变量使用,提高编译器效率,增加运行速度

  • 使用 with 语句报错
var obj = { a: 1, b: 2 };
var a = 3;
var c = 4;
with (obj) {
  console.log(a);
  console.log(b);
  console.log(c);
  console.log(d);
}
"use strict";
var obj = { a: 1, b: 2 };
with (obj) {
  console.log(a);
}
// 补充:with 语句效率比较

function a() {
  var now = Date.now();
  var obj = { a: 1 };
  var c = 0;
  with (obj) {
    for (var i = 100000; i > 0; i--) {
      c += a;
    }
  }
  console.log("耗时", Date.now() - now, "ms");
}

function b() {
  var now = Date.now();
  var obj = { a: 1 };
  var c = 0;
  for (var i = 100000; i > 0; i--) {
    c += obj.a;
  }
  console.log("耗时", Date.now() - now, "ms");
}

a();
b();
  • 创建 eval 作用域
var a = 1;
eval("var b = 2; console.log(a, b);");
console.log(a, b);
"use strict";
var a = 1;
eval("var b = 2; console.log(a, b);");
console.log(a, b);
var a = 1;
eval('"use strict"; var b = 2; console.log(a, b);');
console.log(a, b);
  • 删除已声明变量报错
var a = 1;
console.log(a);
delete a;
console.log(a);
"use strict";
var a = 1;
console.log(a); // 1
delete a;
console.log(a);
  • 更改 eval, arguments 报错
(function () {
  eval = 1;
  console.log(eval); // 1

  arguments++;
  console.log(arguments); // NaN

  ++eval;
  console.log(eval); // 2

  var obj = {
    set p(arguments) {
      console.log(arguments);
    }
  };
  obj.p = 1; // 1

  var eval;
  console.log(eval); // 2

  try {
    test();
  } catch (arguments) {
    console.log(arguments); // ReferenceError: test is not defined
  }

  function x(eval) {
    console.log(eval);
  }
  x(1); // 1
})();

(function () {
  function arguments() {
    console.log("arguments function");
  }
  arguments(); // "arguments function"
})();

(function () {
  function eval() {
    console.log("eval function");
  }
  eval(); // "eval function"
})();

(function () {
  var f = new Function("arguments", "return 1;");
  console.log(f()); // 1
})();

(function () {
  "use strict";
  eval = 1;
  console.log(eval);
})();

(function () {
  "use strict";
  function arguments() {
    console.log("arguments function");
  }
  arguments();
})();

(function () {
  "use strict";
  function eval() {
    console.log("eval function");
  }
  eval();
})();

(function () {
  "use strict";
  var f = new Function("arguments", "return 1;");
  console.log(f()); // 1
})();

(function () {
  var f = new Function("arguments", '"use strict"; return 1;');
  console.log(f());
})();

消除代码运行的不安全之处,保证代码运行的安全

  • 函数内部 this 不再被包装成对象
function a() {
  return this;
}
var that = this;
function log(result) {
  console.log(typeof result, result, result === that);
}
log(a());
log(a.bind(null)());
log(a.bind(undefined)());
log(a.bind(1)());
log(a.call("1"));
log(a.apply(true));
"use strict";
function a() {
  return this;
}
var that = this;
function log(result) {
  console.log(typeof result, result, result === that);
}
log(a());
log(a.bind(null)());
log(a.bind(undefined)());
log(a.bind(1)());
log(a.call("1"));
log(a.apply(true));
function funcA(arga) {
  console.log("funcA", funcA.caller);
  console.log("funcA", Array.prototype.slice.call(funcA.arguments));
  funcA.caller = 1;
  funcA.arguments = 2;
  console.log("funcA", funcA.caller);
  console.log("funcA", Array.prototype.slice.call(funcA.arguments));
}
function funcB(argb) {
  funcA(1);
  console.log("funcB", funcB.caller);
  console.log("funcB", Array.prototype.slice.call(funcB.arguments));
}
funcB(2);
"use strict";
function funcA(arga) {
  console.log("funcA", funcA.caller);
  console.log("funcA", Array.prototype.slice.call(funcA.arguments));
}
function funcB(argb) {
  funcA(1);
  console.log("funcB", funcB.caller);
  console.log("funcB", Array.prototype.slice.call(funcB.arguments));
}
funcB(2);
"use strict";
function funcA(arga) {
  funcA.caller = 1;
  funcA.arguments = 2;
  console.log("funcA", funcA.caller);
  console.log("funcA", Array.prototype.slice.call(funcA.arguments));
}
funcA(2);
function funcA() {
  console.log(arguments.callee);
  arguments.callee = 1;
  console.log(arguments.callee);
}
funcA();
"use strict";
function funcA() {
  console.log(arguments.callee);
}
funcA();
"use strict";
function a() {
  arguments.callee = 1;
  console.log(arguments.callee);
}
a();

为未来新版本做铺垫

  • 新增保留字 implements, interface, let, package, private, protected, public, static, yield
var implements = 1;
console.log(implements);
"use strict";
var implements = 1;
console.log(implements);
  • 考虑未来新版中的 块级作用域,规定只能在 全局作用域函数作用域 的顶层声明函数
    • 注意,在 ECMAScript6 中函数允许被声明在 块级作用域
"use strict";
function funcA(arg) {
  if (arg) {
    function funcB() {
      console.log("funcB");
    }
    funcB();
  } else {
    function funcC() {
      console.log("funcC");
    }
    funcC();
  }
}
funcA();
funcA(1);

参考