JS专题之严格模式

ECMAScript 5 引入了 strict mode ,现在已经被大多浏览器实现(从IE10开始)

一、什么是严格模式

顾名思义,JavaScript 严格模式就是让 JS 代码以更严格的模式执行,不允许可能会引发错误的代码执行。在正常模式下静默失败的代码,严格模式下就会抛出错误。

二、为什么要过渡到严格模式

  1. 严格模式下的代码在运行的时候,更容易通过抛出的错误定位到问题所在的地方
  2. 严格模式能够帮助你编写更符合规范的代码
  3. 消除 JavaScript 语言上一些不合理,比较怪异的行为
  4. 为未来新版本的 JavaScript 做铺垫
  5. 有时候,严格模式下的 JavaScript 代码运行起来更快

三、如何使用

· 脚本文件范围

"use strict"; 放在脚本文件的第一行。整个脚本文件就会以“严格模式”执行。

· 函数作用域范围

"use strict"; 放在函数体的第一行,则整个函数以"严格模式"运行。

文件合并时,写在脚本文件第一行的 "use strict"; 来实现严格模式会失效,可以将脚本文件的代码放在一个立即执行表达式中。

(funciton() {
    "use strict";
    ...
})()
复制代码

四、严格模式的具体定义

  1. 严格模式下无法再隐式创建全局变量 也就是,变量必须声明后才能使用,正常模式直接赋值给一个未定义的变量时,会将变量定义为全局变量。
"use strict";
var a = b = 3;  // Uncaught ReferenceError: b is not defined

以上代码等于:
var a;
b = 3;
a = b;

复制代码
  1. 禁止 this 关键字指向全局对象 正常模式下,函数中如果没有指明 this 对象,JS 则会将 this 隐式指向为全局对象。如果绑定的值是非对象,将被自动转为对象再绑定上去,而 null 和 undefined 这两个无法转成对象的值,将被忽略。

严格模式下,必须指明 this 的指向对象。如果没有指明的话,this的值为 undefined

var name = "foo";
function func() {
    "use strict";
    this.name;  // Uncaught TypeError: Cannot read property 'name' of undefined
}
func();  // 没有加 new 关键字
new func();

function func() {
    return this
}

func() // window
func.call(8) // Number {8}
func.call(true) // Boolean {true}
func.call("abcd")  // {"abcd"}
func.call(null) // window
func.call(undefined) // window

"use strict"
function func() {
    return this
}

func() // undefined
func.call(8) // 8
func.call(true) // true
func.call(null) //null
func.call(undefined) // undefined
复制代码
  1. 不允许在函数内部遍历调用栈 禁止使用 arguments.callee、arguments.caller、fn.caller、fn.callee; 在严格模式下,arguments.callee 是一个不可删除属性,而且赋值和读取时都会抛出异常
function func() {
	"use strict";
	func.caller;  // 报错
	func.arguments; // Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
}
func()
复制代码
  1. 禁止向对象的只读属性赋值,禁止删除对象的不可设置属性, 禁止向不可扩展的对象添加属性 无法删除 var 声明的变量。

在正常模式中,给对象的只读属性赋值, 删除对象的不可设置属性,添加不可扩展对象的新属性,会静默失败。

但是在严格模式中,会抛出错误。 另外,字符串的属性 length 也是只读属性,修改后会报错。

"use strict";
var str = "abc"
str.length = 8  // Uncaught TypeError: Cannot assign to read only property 'length' of string 'abc'

'use strict';
var obj = Object.defineProperty({}, 'a', {
  value: 37,
  writable: false
});
obj.a = 123; // Uncaught TypeError: Cannot assign to read only property 'a' of object '#

'use strict';
var obj = Object.defineProperty({}, 'p', {
  value: 37,
  configurable: false
});
delete obj.p  // Uncaught TypeError: Cannot delete property 'p' of #<Object>

var obj = {};
Object.preventExtensions(obj);
obj.title = "hello";  // Uncaught TypeError: Cannot add property title, object is not extensible
复制代码
  1. 对象不允许有重名的属性,函数不允许有重名的参数 在正常模式中,对象的重名属性,位置靠后会覆盖位置靠前的重名属性。函数也是,函数体查找到的参数,靠后的重名参数会覆盖靠前的重名参数。
"use strict";
var o = { 
    p: 1,
    p: 2
   };
// IE报错:strict 模式下不允许一个属性有多个定义, 新版的 Chrome 和 firefox 并不会报错,会采用覆盖机制。

"use strict";
function func(a, a) {
    console.log(a)
}
func(1, 2) // IE报错: strict 模式下不允许正式参数名称重复。新版的 Chrome 和 firefox 并不会报错,会采用覆盖机制。
复制代码
  1. 静态绑定 JavaScript 支持动态绑定,也就是 JavaScript 的属性和方法是在运行时确定,而不是在编译时确定。 于是,JavaScript 严格模式禁用了 with 语句, 因为使用了 with 语句,with 语句块中变量无法确定是外部全局变量还是传入的对象属性。
"use strict";
var x = 17;
with (obj) // !!! 语法错误
{
  // 如果没有开启严格模式,with 中的这个x会指向 with 上面的那个 x,还是obj.x?
  // 如果不运行代码,我们无法知道,因此,这种代码让引擎无法进行优化,速度也就会变慢。
  x; // Uncaught SyntaxError: Strict mode code may not include a with statement
}
复制代码

eval 关键字不再会给上层函数(surrounding function)或者全局引入一个新的变量。在严格模式中,eval 语句会创建自己的一个作用域,eval 里的变量只能在 eval 内部使用。

  1. arguments 的限定 严格模式规定名称为 eval 和 arguments 不能通过程序语法被绑定(be bound)或赋值 严格模式下,参数的值不会随 arguments 对象的值的改变而变化。

正常模式中,对参数重新赋值,会修改 arguments 类数组对象下的参数值。同时,修改 arguments 类数组对象的值,也会修改函数参数的值。

严格模式下,不仅参数的值不会随着 arguments 类数组对象的变化而变化,参数的变化也不会引起 arguments 对象的变化,arguments 对象会记住参数的传入初始值。

function func(a) {
"use strict"
  a = 8;
  // arguments[0] = 8
  return [a, arguments[0]]
}

func(3) // [8, 3]

function func(a) {
"use strict"
  arguments[0] = 8
  return [a, arguments[0]]
}

func(3) // [3, 8]
复制代码
  1. ES5禁止在非函数代码块声明函数 ES5 的严格模式只允许在全局作用域或函数作用域声明函数。也就是说,不允许在非函数的代码块内声明函数。
if (true) {
  function add() {
  }
}
add()

for (var i = 0; i < 5; i++){
  function f2() { } // !!! 语法错误
  f2();
}

以上代码在严格模式是禁止的,但是在 ES6 中,是允许在代码块中声明函数的。
复制代码
  1. 保留关键字 严格模式中一部分字符变成了保留的关键字。这些字符包括implements, interface, let, package, private, protected, public, staticyield。在严格模式下,你不能再用这些名字作为变量名或者形参名
function private() {"use strict" }  //Uncaught SyntaxError: Unexpected strict mode reserved word
复制代码
  1. 严格模式禁止八进制数字语法

五、向严格模式过渡

严格模式能够帮助我们写出更安全,更有规范的代码,则应该避免一些危险的写法,采用更好的写法:

  1. 变量先声明,再使用,
  2. this 应该在指向自己创建的对象时使用。
  3. arguments 应该在函数第一行就拷贝出来。

六、严格模式的缺点

  1. 现在的代码都会进行文件压缩和合并,此时严格模式就会失效。

总结

现在的 webpack 会在打包的时候默认是严格模式,所以现在不用再手动写 use strict了。严格模式能帮助我们以更规范的方式书写代码,但是无论是否严格模式,都应该注意代码的规范,避免隐式 bug 的出现。

2018/02/08 @Starbucks

欢迎关注我的个人公众号“谢南波”,专注分享原创文章。

掘金专栏 JavaScript 系列文章

  1. JavaScript之变量及作用域
  2. JavaScript之声明提升
  3. JavaScript之执行上下文
  4. JavaScript之变量对象
  5. JavaScript之原型与原型链
  6. JavaScript之作用域链
  7. JavaScript之闭包
  8. JavaScript之this
  9. JavaScript之arguments
  10. JavaScript之按值传递
  11. JavaScript之例题中彻底理解this
  12. JavaScript专题之模拟实现call和apply
  13. JavaScript专题之模拟实现bind
  14. JavaScript专题之模拟实现new
  15. JS专题之事件模型
  16. JS专题之事件循环
  17. JS专题之去抖函数
  18. JS专题之节流函数
  19. JS专题之函数柯里化
  20. JS专题之数组去重
  21. JS专题之深浅拷贝
  22. JS专题之数组展开

猜你喜欢

转载自juejin.im/post/5c5d0b495188256282695206