为什么要有this

如果没有this,如何在sayName里找到代表“自己“这个对象?

  1. let people = {
  2. name: '刘德华',
  3. sayName() {
  4. console.log(people.name) // 怎样找到自己的name属性
  5. }
  6. }

可以用 this 代表“自己“这个对象

  1. let people = {
  2. name: '刘德华',
  3. sayName() {
  4. console.log(this.name) // 用this表示自己这个对象
  5. }
  6. }

案例:
假设小二班有小花和旺财两个同学,请问旺财如何向其他人更好的介绍小花?

  • 选项A 旺财说:她是我们班的小花
  • 选项B 旺财说:她说小二班的小花

选项A的说法更好,因为即使他们升到大二班,旺财的话仍然适用。

所以this出现的典型场景是:
在一个对象的方法里找当前对象(this)的其他属性或者方法。

换一种说法:
this 代表当前this直属的函数所属的对象。

来看一个实际点的例子,以下代码里this分别都指什么?第9行代码对吗?

  1. const app = {
  2. init() {
  3. this.$btn = document.querySelector('button')
  4. this.bind()
  5. },
  6. bind() {
  7. this.$btn.onclick = function() {
  8. console.log(this)
  9. this.getData() //这样写对吗,如果不对该如何修改?
  10. }
  11. },
  12. getData() {
  13. console.log('get data...')
  14. }
  15. }
  16. app.init()

那我们如何修改呢?下面有几种写法:

  1. const app = {
  2. init() {
  3. this.$btn = document.querySelector('button')
  4. this.bind()
  5. },
  6. bind() {
  7. let self = this
  8. this.$btn.onclick = function() {
  9. console.log(this) // 这个this 还是 btn 对象
  10. self.getData() //self 代表外面的this,也就是app
  11. }
  12. },
  13. getData() {
  14. console.log('get data...')
  15. }
  16. }
  17. app.init()

在第7行,我们先把this(这里的this代表app对象)换个名字保存起来,在第9行此刻的this代表btn对象,如果想适用app对象可以使用self。

  1. const app = {
  2. init() {
  3. this.$btn = document.querySelector('button')
  4. this.bind()
  5. },
  6. bind() {
  7. this.$btn.onclick = () => {
  8. console.log(this) // 在确定this直属的函数时,不考虑箭头函数
  9. this.getData() // 所以当前this直属的函数是bind
  10. }
  11. },
  12. getData() {
  13. console.log('get data...')
  14. }
  15. }
  16. app.init()

当我们去找this的直属函数时,需要忽略箭头函数。第8行的this的直属函数是bind。

反例

this 默认指代当前this直属的函数所属的对象,下面给一些反例。

  1. var name = '刘德华' // var关键字定义的变量,作为window对象的属性
  2. let people = {
  3. name: '郭富城',
  4. sayName() {
  5. console.log(this.name)
  6. }
  7. }
  8. let sayAgain = people.sayName
  9. // sayName所属的对象是window
  10. function sayName(){
  11. console.log(this.name)
  12. }
  13. people.sayName() //郭富城
  14. // 调用sayAgain()时,其中的this直属的函数不再是people.sayName而是sayAgain
  15. sayAgain() // 刘德华
  16. sayName() // 刘德华

下面是一个更隐晦的例子

let arr = []
for(let i=0; i<3; i++){
   arr[i] = function() { console.log(this) }
}

// 数组是特殊的对象
arr[0]()     // 其中的this指向arr
let fn = arr[0]   
fn()     // 其中的this指向window
let people = {
  name: '刘德华',
  sayName() {
    setTimeout(function(){
      console.log(this)
    }, 0)
  }
}
people.sayName()   // 其中的this指向window

箭头函数

let people = {
  name: '刘德华',
  sayName() {
    setTimeout(() => {
      console.log(this)
    }, 0)
  }
}
// 找直属函数需要忽略箭头函数,找其外层函数
people.sayName()    // this指向people

call/apply/bind

this 默认指代当前this直属的函数所属的对象。假设我们想让this换个对象该怎么办?
用call/apply/bind来修改它

let obj = {
  name: '刘德华'
}
function sayName(){
  console.log(this.name)
}
let fn = sayName.bind(obj)
fn() // 输出: '刘德华'

一个复杂但更实际点的例子

let app = {
  container: document.querySelector('button'),
  init(){
    //点击的时候会执行 sayHello,sayHello 里面的 this 代表 button 对象
    this.container.onclick = this.sayHello                

    //点击的时候会执行 sayHello,sayHello 里面的 this 代表 app 对象
    this.container.onclick = this.sayHello.bind(this) 
  }, 
  sayHello(){
    console.log(this)     // button对象
  }
}
app.init()

另一个场景的this

有时候我们会在class或者构造函数里看到this:

function Wife(name) {
  this.name = name
}

Wife.prototype.showSkill = function() {
  console.log('我是' + this.name + ', 我的技能是唱歌、跳舞、打游戏')
}

一个直男幻想着:如果我以后有个女神老婆,她的技能最好是唱歌、跳舞、打游戏。上面代码里的this(出现在构造函数内和原型的方法内)就代表幻想的那个未来的那个“她”。

let wife = new Wife('新桓结衣')
wife.showSkill()

此刻真正的 wife 才创建,之前幻想时对 她(this) 的操作才真正开始实施。对this操作就是对wife的操作。

总结

当看到this时,想知道它是什么,需要看

  • this的直属函数是谁,忽略箭头函数
  • 直属函数是哪个对象上的方法
  • 有没有使用过call、apply、bind来修改this