This指向| 8月更文挑战

240 阅读3分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

在JS的世界中,this指向无论工作中还是面试过程中都是一道非常重要的问题,this因其灵活的指向和复杂的使用场景变得千变万化,那么今天我们就来一起看看this

this指向谁

初学this的时候,为了方便理解和记忆,也为了应对面试,当讨论this指向谁的时候,总会拿出万能的总结:

谁调用它,那么this就指向谁,this的指向是在调用的时候确定的

初始这句话时,并没有发现其有什么问题,随着时间的推移、技术的深入了解后,发现并不能覆盖所遇到的this指向问题,渐渐的发现这句话的不严谨

this指向场景:

  1. 在函数体中,简单调用该函数时(非显式/隐式绑定下),严格模式下 this 绑定到 undefined,否则绑定到全局对象 windowglobal
  2. 一般构造函数 new 调用,绑定到新创建的对象上;
  3. 一般由 call/apply/bind 方法显式调用,绑定到指定参数的对象上;
  4. 一般由上下文对象调用,绑定在该对象上;
  5. 箭头函数中,根据外层上下文绑定的 this 决定 this 指向。

通过上述的场景,this的指向到底是如何确定的呢?

一句话总结:代码执行时,根据当前的执行上下文动态决定this的指向

实战分析

函数中

函数在浏览器全局环境中被简单调用,非严格模式下 this 指向 window;在 use strict 指明严格模式的情况下就是 undefined。例题:

const fn1 = function () {
  console.log(this)
}

const fn2 = function () {
  'use strict';
   console.log(this)
}

fn1() // window
fn2() // undefined

对象中

const person = {
  name: 'nordon',
  say () {
    console.log('this: ', this)
    console.log('name: ',this.name)
  }
}
// 直接调用
person.say()

// 将 person.say 赋值给say 然后调用
const say = person.say
say()

两种调用方式的结果是不一样的

person.say()直接调用,这个时候函数中的this的执行上下文是person,因此输出personnordon

person.say 赋值给say 然后调用, 这里 this 仍然指向的是 window。虽然 say 函数在 person 对象中作为方法被引用,但是在赋值给 say 之后,say 的执行仍然是在 window 的全局环境中。因此输出 windowundefined

显示改变this

在JS中call、apply、bind都是用来改变相关函数 this 指向的,但是 call/apply 是直接进行相关函数调用;bind 不会执行相关函数,而是返回一个新的函数,这个新的函数已经自动绑定了新的 this 指向,开发者需要手动调用即可。再具体的 call/apply 之间的区别主要体现在参数设定上

const person = {
  name: 'nordon'
}
var name = 'window name'

const fn = function (env) {
  console.log(`${env}: ${this.name}`)
}


fn('window') // window: window name
fn.call(person, 'call') // call: nordon
fn.apply(person, ['apply']) // apply: nordon
fn.bind(person)('bind') // bind: nordon

构造函数

想要搞懂构造函数中的this,需要知道当我们使用new关键字进行实例化的时候,在构造函数中做了哪些事情

  1. 创建一个新的对象 obj
  2. 将构造函数的this指向这个新的对象 obj
  3. 为这个新的对象添加属性、方法等
  4. 返回新的对象
function Person(name) {
  console.log('this: ', this) 
  this.name = name
}

const p = new Person('nordon')

此时里面的this输出为Person,而非有些人认为的window

箭头函数

箭头函数使用 this 是根据外层(函数或者全局)上下文来决定。

const name = 'window name'
const person = {
  name: 'nordon',
  say: () => {
    console.log('name: ', this.name)
  }
}

person.say() // name:  window name


const person2 = {
  name: 'out name',
  child: {
    name: 'child namae',
    say () {
      console.log('name: ', this.name)
    }
  }
}

person2.child.say() // name:  window name

此时this指向的是window,和普通的对象中直接调用时this指向对象本身不同,而是根据person定义的环境决定的