站点工具

用户工具


一次性弄懂this、call/apply/bind

为什么要有this

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

let people = {
  name: 'hunger',
  sayName: function() {
    console.log(people.name)  // 怎样找到自己的name属性
  }
}

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

let people = {
  name: 'hunger',
  sayName: function() {
    console.log(this.name)   
  }
}

   

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

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

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

const app = {
  init() {
		this.$btn = document.querySelector('button')
    this.bind()
  },
  bind() {
    this.$btn.onclick = function() {
      console.log(this)
      this.getData()    //这样写对吗,如果不对该如何修改?
    }
  },
  getData() {
    console.log('get data...')
  }
}
app.init()

你可能已经猜对里答案。

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

const app = {
  init() {
		this.$btn = document.querySelector('button')
    this.bind()
  },
  bind() {
    let self = this
    this.$btn.onclick = function() {
      console.log(this)  // 这个this 还是 btn 对象
      self.getData()    //self 代表外面的this,也就是app
    }
  },
  getData() {
    console.log('get data...')
  }
}
app.init()

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

const app = {
  init() {
    this.$btn = document.querySelector('button')
    this.bind()
  },
  bind() {
    this.$btn.onclick = () => {
      console.log(this)  // 在确定this直属的函数时,不考虑箭头函数
      this.getData()     // 所以当前this直属的函数是bind
    }
  },
  getData() {
    console.log('get data...')
  }
}
app.init()

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

反例

刚刚说过 this 默认指代当前this直属的函数所属的对象,为了让大家深刻理解“所属”这个词的含义下面给一些反例。

var name = '饥人谷'
var people = {
  name: '若愚',
  sayName() {
    console.log(this.name)
  }
}
var sayAgain = people.sayName
function sayName(){
  console.log(this.name)
}
 
 
people.sayName()
sayAgain()
sayName()

如果能给出正确的答案,那再看下面一个更隐晦的例子。

let arr = []
for(let i=0; i<3; i++){
   arr[i] = function() { console.log(this) }
}
 
arr[0]()
let fn = arr[0]
fn()
let people = {
  name: '若愚',
  sayName() {
    setTimeout(function(){
      console.log(this.name)
    }, 0)
  }
}
people.sayName()

箭头函数

let people = {
  name: '若愚',
  sayName() {
    setTimeout(() => {
      console.log(this.name)
    }, 0)
  }
}
people.sayName()

                                                              

call/apply/bind

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

let obj = {
  fn(a, b) {
    console.log(this)
  }
}
obj.fn(1, 2)
//等价于
obj.fn.call(obj, 1, 2)         // 所以 this 是 obj
obj.fn.apply(obj, [1, 2])
obj.fn.bind(obj)(1, 2)

call/apply/bind里面第一个参数传什么,函数里面的this就是什么。

let obj = {name: '饥人谷'}
function sayName(){
  console.log(this.name)
}
let fn = sayName.bind(obj)
fn() // 输出: '饥人谷'

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

let app = {
  container: document.querySelector('body'),
  init: function(){
    //点击的时候会执行 sayHello,sayHello 里面的 this 代表 body 对象
    this.container.addEventListener('click', this.sayHello)                  
 
    //点击的时候会执行 sayHello,sayHello 里面的 this 代表 app 对象
    this.container.addEventListener('click', this.sayHello.bind(this)) 
  }, 
  sayHello: function(){
    console.log(this)
  }
}
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

** 此为饥人谷前端系统班讲义,如需转载或者了解课程,联系微信 xiedaimala03。

若愚 · 2022/04/02 11:14 · javascript_this.txt