温故而知新:javaScript中this的绑定

60 阅读3分钟

前言

🙋‍ 知其然,更知其所以然,举一反三,融会贯通

this算是javascript中比较基础同时也是非常重要的一个概念了,对于初学者来说会让人很迷糊,如果理解不清楚,可能经常会出现莫名其妙的bug。

稍微官方一点的解释:

this 被自动定义在所有函数的作用域中,它提供了一种更好的方式来“隐式”的传递对象引用,这样使得我们的 API 设计或者函数变得更加简洁,而且还更容易复用

this是javascript中的一个关键字,它代表函数运行时,函数内部自动生成的一个对象(执行上下文中的一个对象),只能在函数内部使用,随函数被调用场景的不同,它的指向也会有所变化,但是可以记住一个基本原则:谁调用了这个函数,那么this就会指向谁(调用函数的对象)

  • this是在运行时绑定的,而不是在编写的时候

  • this的绑定与函数的声明和位置没有任何关系

下面来看看绑定this的几种场景:

函数调用(默认绑定)

直接调用函数, 这种是最常见的场景,在全局执行环境调用函数,它默认指向的是window对象,如果是严格模式,this指向的是undefined,这种规则可以称为:默认绑定

const myFun = () => {
    console.log(this) // window对象
}
myFun()

对象调用(隐式绑定)

当声明一个对象,调用里面函数的时候,函数中的this指向调用函数的对象。这种规则可以称为:隐式绑定

const obj = {
    title: 'hello world',
    getName() {
        console.log(this.title) // hello world
    }
}
ob.getName()

上面示例函数如果是箭头函数,里面的this则指向外层的window,因为箭头函数内部没有自己的this,它默认会指向外层的this。

const title = 'hi world'
const obj = {
    title: 'hello world',
    getName: () => {
        console.log(this.title) // hi world
    }
}
ob.getName()

构造函数(new绑定)

构造函数通过new关键字调用,this永远都是指向新创建的对象,并且this的指向不能被改变。

function MyFun(userName, sex) {
    this.userName = userName
    this.sex = sex
}
var myFun = new MyFun('张三', '男')
console.log(muFun.userName) // 张三

我们可以分析下,使用new关键字调用函数时创建对象的过程:

  • 首先创建一个全新的空对象,这个新对象会继承函数的原型(创建原型连接)
  • 把新创建对象的引用赋值给this(绑定函数this到新对象上)
  • 通过this新增属性和方法(绑定到对象上)
  • 如果函数没有返回对象或返回基本类型的值,则默认会返回新建的对象,如果手动的返回一个对象,则直接返回对象

call/apply(显示绑定)

除了上面几种绑定方式外,还有一种显示的绑定,通过call/apply/bind可以直接把某个对象绑定到函数的内部this上 ,相当于修改了this的指向

var userName = 'hello world'
function getName() {
    console.log(this.userName)
}
getName()  // hello world
getName({userName: 'hi world'}) // hi world

绑定总结

当有几种绑定规则同时存在时,对于最终绑定的是有优先级的

new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

判断this最终的指向,可以总结为:

  1. 判断函数调用时是否存在new关键字,如果有,则this绑定在新创建的对象上
  2. 函数调用是否使用了call,apply,bind等显示绑定,如果是,则this绑定在指定的对象上(传递的对象参数)
  3. 函数是否在某个上下文对象中调用,如果是,则this指向调用函数的对象(那个上下文对象)
  4. 以上3点都没涉及到的话,则为默认绑定,只是严格模式和非严格会存在区别,严格模式指向undefined,非严格模式指向window
  5. 需要注意的是箭头函数内部没有自己的this,它的this指向最近外层作用域中的this