JS 中的 this

116 阅读4分钟

前言

JavaScript 中的 this 表现与其他语言略有不同。本文介绍了 this 在不同情况下的指向,包括全局作用域,默认绑定,隐式绑定,显式绑定,构造函数,箭头函数。

this

与其他语言相比,函数的 this 关键字在 JavaScript 中的表现略有不同,此外,在严格模式和非严格模式之间也会有一些差别。在绝大多数情况下,函数的调用方式决定了 this 的值(运行时绑定)。this 不能在执行期间被赋值,并且在每次函数被调用时 this 的值也可能会不同。

常见的this指向:

  • 全局作用域中或普通函数中的 this 指向全局对象 window,严格模式下普通函数中的 thisundefined
  • 立即执行函数的 this 指向 window
  • 定时器的 this 指向 window,因为 setTimeout()window 上的方法。
  • 事件中的 this 指向事件源对象。
  • 方法中的 this 指向调用方法的对象。
  • 构造函数中 this 指向新建的对象实例。

this 的绑定

默认绑定

普通函数调用模式,又或者称为默认绑定,当一个函数不是一个对象的属性,直接作为函数来调用时,this指向全局对象,而在严格模式下 thisundefined

console.log(this === window); // true

隐式绑定

方法调用模式,又或者称为隐式绑定,如果一个函数作为一个对象的方法来调用时, this 指向这个对象。

下面代码中 foo() 的第一个 thisobjtest() 里的 thiswindow ,因为test() 是独立调用的,调用的是 test() 而非 obj.test()

obj 无法直接调用 test(),这里的 obj.test() 是为了更好的理解隐式绑定中函数要作为对象的方法被调用。

或者 this 指向最后调用它的那个对象,这样比较好理解。

bar 拿到了 obj.foo 的引用,然后在全局下独立调用,打印的 this 均为 window

let obj = {
  foo: function () {
    console.log(this); // obj
    function test() {
      console.log(this); // window
    }
    test();
  }
};
obj.foo();
let bar = obj.foo;
bar(); // window

显式绑定

我们通过 call()apply()bind() 调用模式,又或者称为显式绑定,可以使用这三个方法显示地指定调用函数的 this 指向。

构造函数

构造器调用模式,如果一个函数用 new 调用时,函数执行前会新创建一个对象,this 指向这个新创建的对象。

new 调用时的返回值,如果没有显式返回对象或者函数,才是返回生成的新对象,不能简单的用 typeof 判断对象。

优先级

这四种方式的优先级:new 调用(构造器调用模式)> 显式绑定(call()apply()bind() 调用模式)> 隐式绑定(方法调用模式)> 默认绑定(普通函数调用模式)。

箭头函数

ES6中提出了箭头函数,它的语法比函数表达式更简洁,并且没有自己的 thisargumentssupernew.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。

箭头函数中的 this

  • 箭头函数没有自己的 this,只能捕获其所在上下文的 this 值作为自己的 this。箭头函数的this 绑定的是最近一层非箭头函数的this,否则为全局对象。
  • 箭头函数继承来的 this 指向不会改变,这有利于封装回调函数。
  • callapplybind 等方法不能改变箭头函数 this 的指向。
  • 箭头函数不能作为构造函数使用,因为没有自己的 this,也就无法通过 new 操作符使 this 指向生成的新对象。

下面使用 Babel 将箭头函数转成 ES5 代码,就能清楚地说明this的指向。箭头函数里面根本没有自己的 this,而是引用了外层的 this

// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;
  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

参考资料

this - JavaScript | MDN
箭头函数 - JavaScript | MDN
函数的扩展 - ECMAScript 6入门 前端面试题之JavaScript篇
面试官问:JS的this指向 - 掘金
this、apply、call、bind - 掘金
this指向/箭头函数this指向详解 - 掘金