前言
🙋 知其然,更知其所以然,举一反三,融会贯通
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最终的指向,可以总结为:
- 判断函数调用时是否存在new关键字,如果有,则this绑定在新创建的对象上
- 函数调用是否使用了call,apply,bind等显示绑定,如果是,则this绑定在指定的对象上(传递的对象参数)
- 函数是否在某个上下文对象中调用,如果是,则this指向调用函数的对象(那个上下文对象)
- 以上3点都没涉及到的话,则为默认绑定,只是严格模式和非严格会存在区别,严格模式指向undefined,非严格模式指向window
- 需要注意的是箭头函数内部没有自己的this,它的this指向最近外层作用域中的this