this 允许复用函数时使用不同的上下文。换句话说,“this” 关键字允许在调用函数或方法时决定哪个对象应该是焦点。
判断 this 的引用必须先看函数的定义,在实际地查看函数定义时,我们设立了四条规则来查找引用,它们是

  1. 隐式绑定
  2. 显式绑定
  3. new 绑定
  4. window 绑定

隐式绑定

请记住,这里的目标是查看使用 this 关键字的函数定义,并判断 this 的指向。执行绑定的第一个也是最常见的规则称为 隐式绑定。80% 的情况下它会告诉你 this 关键字引用的是什么。

  1. const user = {
  2. name: 'Tyler',
  3. age: 27,
  4. greet() {
  5. alert(`Hello, my name is ${this.name}`)
  6. }
  7. }
  8. user.greet()

为了判断 this 关键字的引用,函数被调用时先看一看点号左侧。如果有“点”就查看点左侧的对象,这个对象就是 this 的引用。

显式绑定

“call” 是每个函数都有的一个方法,它允许你在调用函数时为函数指定上下文。传递给它的第一个参数会作为函数被调用时的上下文。换句话说,this 将会指向传递给 call 的第一个参数

  1. function greet () {
  2. alert(`Hello, my name is ${this.name}`)
  3. }
  4. const user = { name: 'Tyler', age: 27,}greet.call(user)
  5. function greet (lang1, lang2, lang3) {
  6. alert(`Hello, my name is ${this.name} and I know ${lang1}, ${lang2}, and ${lang3}`)
  7. }
  8. const user = { name: 'Tyler', age: 27}
  9. const languages = ['JavaScript', 'Ruby', 'Python']
  10. greet.call(user, languages[0], languages[1], languages[2])

.apply 和 .call 本质相同,但不是一个一个传递参数,你可以用数组传参而且 .apply 会在函数中为你自动展开。

  1. const languages = ['JavaScript', 'Ruby', 'Python']
  2. greet.call(user, languages[0], languages[1], languages[2])
  3. greet.apply(user, languages)

.bind 和 .call完全相同,除了不会立刻调用函数,而是返回一个能以后调用的新函数。

  1. function greet (lang1, lang2, lang3) {
  2. alert(`Hello, my name is ${this.name} and I know ${lang1}, ${lang2}, and ${lang3}`)
  3. }
  4. const user = {
  5. name: 'Tyler',
  6. age: 27,
  7. }
  8. const languages = ['JavaScript', 'Ruby', 'Python']
  9. const newFn = greet.bind(user, languages[0], languages[1], languages[2])
  10. newFn() // alerts "Hello, my name is Tyler and I know JavaScript, Ruby, and Python"

new 绑定

第三条判断 this 引用的规则是 new 绑定。若你不熟悉 JavaScript 中的 new 关键字,其实每当用 new 调用函数时,JavaScript 解释器都会在底层创建一个全新的对象并把这个对象当做 this。如果用 new 调用一个函数,this 会自然地引用解释器创建的新对象。

  1. function User (name, age) {
  2. /*
  3. JavaScript 会在底层创建一个新对象 `this`,它会代理不在 User 原型链上的属性。
  4. 如果一个函数用 new 关键字调用,this 就会指向解释器创建的新对象。
  5. */
  6. this.name = name
  7. this.age = age
  8. }
  9. const me = new User('Tyler', 27)

window 绑定

  1. window.age = 27
  2. function sayAge () {
  3. console.log(`My age is ${this.age}`)
  4. }

在 ES5 添加的 严格模式 中,JavaScript 不会默认 this 指向 window 对象,而会正确地把 this 保持为 undefined。

  1. 'use strict'
  2. window.age = 27
  3. function sayAge () {
  4. console.log(`My age is ${this.age}`)
  5. }
  6. sayAge() // TypeError: Cannot read property 'age' of undefine
  • 查看函数在哪被调用。
  • 点左侧有没有对象?如果有,它就是 “this” 的引用。如果没有,继续第 3 步。
  • 该函数是不是用 “call”、“apply” 或者 “bind” 调用的?如果是,它会显式地指明 “this” 的引用。如果不是,继续第 4 步。
  • 该函数是不是用 “new” 调用的?如果是,“this” 指向的就是 JavaScript 解释器新创建的对象。如果不是,继续第 5 步。
  • 是否在“严格模式”下?如果是,“this” 就是 undefined,如果不是,继续第 6 步。
  • JavaScript 很奇怪,“this” 会指向 “window” 对象。

箭头函数的 this 指向问题

箭头函数在自己的作用域内不绑定 this,即没有自己的 this,如果要使用 this ,就会指向定义时所在的作用域的 this 值。在上面的代码中即指向 User 函数的 this,而 User 函数通过 new 绑定,所以 this 实际指向 user 对象。
因为箭头函数没有自己的 this,使用 bind,call 或者 apply 时,箭头函数会自动忽略掉 bind 的第一个参数,即 thisArg。
总结:箭头函数在自己的作用域内没有自己的 this,如果要使用 this ,就会指向定义时所在的作用域的 this 值。