bind\call\apply、构造函数、箭头函数中的this指向(二)

446 阅读4分钟

「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战

在上一篇文章中搞明白this指向,走遍天下都不怕(一)我们讲了全局环境中的this对象调用中的this

今天我们接着来看剩下其他的bind\call\apply、构造函数、箭头函数中的this指向问题。

bind\call\apply改变this指向

一般面试都会很喜欢问bind\call\apply的区别,这个问题也比较基础,总结来说就是它们都是用来改变函数this指向的,call和apply是直接进行函数调用;bind不会立即执行函数,而是返回一个新的函数,这个新函数已经自动绑定了新的this指向。剩下的call和apply就是传入参数的区别。

请看下面的示例

function fn(a, b) {
  console.log(this)
  console.log(a, b)
}

// call
const obj1 = { a: 1 }
fn.call(obj1, 'a', 'b')

// apply
const obj2 = { a: 2 }
fn.apply(obj2, ['a', 'b'])

// bind
const obj3 = { a: 3}
fn.bind(obj3, 'a', 'b')()

// 依次输出
// {a: 1}
// a b
// {a: 2}
// a b
// {a: 3}
// a b

基础面试题

const user = {
  name: 'shuai',
  print: function() {
    console.log(this.name)
  }
}

const student = {
  name: 'guan'
}

console.log(user.print.call(student)) // ?

通过call函数将print函数内的this指向改到了student对象上,所以这道题的答案将输出guan

构造函数和this

new操作符调用构造函数时做了什么

function User() {
  this.name = 'shuai'
}

const user = new User()
console.log(user.name) // ?

上面代码将会输出shuai

这道题也是面试中的常考题,简单总结来说

  1. 创建了一个新的对象
  2. 将构造函数内的this指向这个新对象
  3. 为这个对象添加属性和方法等
  4. 最后返回这个对象

上面的描述也可以用代码来描述

const obj = {}
obj.__proto__ = User.prototype
User.call(obj)

上面的内容只是最简单、最基本的版本。

我们接着来看两种特殊的情况,在构造函数内出现了return的情况

构造函数内return一个对象

function User() {
  this.name = 'shuai'
  const obj = {}
  return obj
}

const user = new User()
console.log(user.name) // ?

上面的代码将输出undefined,这时我们调用new User()返回的是空对象obj

构造函数内return一个基本类型值

function User() {
  this.name = 'shuai'
  return 1
}

const user = new User()
console.log(user.name) // ?

运行上面的代码我们会发现将输出shuai,也就是这时我们调用new User()返回的是目标对象this,而是是返回1

所以这里要记住

如果构造函数中返回一个对象(复杂类型),那么this就会指向这个返回的对象;如果返回的不是对象,而是基本类型的值,那么this仍然会指向这个实例。

箭头函数中的this

普通函数

const obj = {
  fn: function() {
    setTimeout(function() {
      console.log(this)
    })
  }
}

console.log(obj.fn()) // ?

在上面的代码中,由于this出现在setTimeout的匿名函数中,所以this指向window。

这个和我们昨天讲的是一样的

如果函数是被上一级对象调用那么this就指向上一级这个对象,否则就指向全局环境。

那么如果我们想要这个this指向到obj对象的话,就可以用箭头函数来解决。

箭头函数

const obj = {
  fn: function() {
    setTimeout(() => {
      console.log(this)
    })
  }
}

console.log(obj.fn()) // ?

这里我们需要了解箭头函数的相关知识,在箭头函数中,this的指向是由外层函数或者全局作用域来决定。在本道题目中,setTimeout中的匿名箭头函数的this就由它的外层函数也就obj.fn函数的作用域来决定,obj.fn函数内的this指向就是obj对象,所以setTimeout中的匿名箭头函数的this也就是obj对象。

今日总结

  • 在函数中,严格模式this会被绑定到undefined上,非严格模式会被绑定到window全局对象
  • 通过上下文对象调用函数时,函数内的this会被绑定到该对象上
  • 通过new操作符调用构造函数时,构造函数内的this会被绑定到新创建的对象上
  • 通过bind\call\apply方法调用函数时,函数内的this会被绑定到指定参数的对象
  • 在箭头函数中,this的指向是由外层函数或者全局作用域来决定的

欢迎我的公众号【小帅的编程笔记】,让自己和他人都能有所收获!