小白带你理解this

388 阅读4分钟

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

this到底指向谁

this在前端是一个很常见的关键字,我相信没人不知道。虽然很常见,但是这也是一个棘手的东西,稍不注意就写了bug,通过本文,你将会更加彻底理解this

先上总结

想必很多人都听说,关于this指向的一句话:“谁调用thisthis就指向谁”
这句话本身是没毛病,但本文将会更加细致的具体的分析,看看在各种情况下this的指向,这里大家伙先“死记硬背”以下几条法则

  • 在函数体中,非显示或隐式调用函数时,严格模式下this会指向undefined,非严格模式下会指向window/global
  • 通常使用new调用构造函数时,构造函数内的this会绑定到新实例对象上
  • 通常通过apply/call/bind方法显示调用时,this指向新传入的参数的对象上
  • 一般通过上下文调用函数时,函数体内的this指向该上下文
  • 箭头函数没有自己的thisthis的指向是由外层作用域决定

🌰例子

全局环境中的this


function f1() {
    console.log(this)
}
function f2() {
    'use strict'
    console.log(this)
}
f1()
f2()

这个相信大家都懂了,f1()输出windowf2()输出undefined

来个变种

const sister = {
    name: '石原里美',
    getName: function() {
        console.log(this)
        console.log(this.name)
    }
}

const fn = sister.getName
fn()

答案

// window
// undefined

这里需要注意这里是赋值给一个变量,而这个变量是在windos下定义,所以 这里的this会指向window,如果像下面直接调用,this就指向调用它的这个对象sister

sister.getName()

new中的this

function f1() {
    this.food = '牛肉'
}
const f2 = new f1()
console.log(f2.food)

答案

// 牛肉

这里很好理解,new构造函数,this会指向新创建的实例上

再来一题

function f1() {
    this.food = '牛肉'
    return 0
}
function f2() {
    this.food = '鸡肉'
    return {}
}
const f3 = new f1()
const f4 = new f2()

console.log(f3.food)
console.log(f4.food)

答案

// 牛肉
// undefined

这里是不是出乎你的意料呢,这里有两个需要注意的点,如果这个构造函数显式的return

  • return是值类型的话,this会指向新创建的实例
  • return是引用类型,this指向返回的这个对象

至于为什么会这样,建议可以看mdn关于new运算符的解释,和类型说明,本文就不展开阐述

上下文中的this

const sister = {
    name: '桥本环奈',
    actor: {
        name: '石原里美',
        getName: function () {
            console.log(this.name)
        }
    }
}
sister.actor.getName()

答案

// 石原里美

this会指向最后调用它的对象,就是actor,所以输出就是石原里美

var f1 = {
    text: '新年快乐 f1',
    sayHi: function () {
        return this.text
    }
}

var f2 = {
    text: '新年快乐 f2',
    sayHi: function () {
        return f1.sayHi()
    }
}

var f3 = {
    text: '新年快乐 f3',
    sayHi:function () {
        const sayHi = f1.sayHi
        return sayHi()
    }
}
console.log(f1.sayHi())
console.log(f2.sayHi())
console.log(f3.sayHi())

答案

// 新年快乐 f1
// 新年快乐 f1
// undefined

第一个比较简单,就是常规的调用,输入“新年快乐 f1”
第二个注意看调用的是谁,就很好理解,结果输入f1.text的“新年快乐 f1”
最后一个可能会被迷惑,注意f1.sayHi只是赋值,没有直接调用,然后返回一个函数的调用重点这里是“裸奔”调用,sayHi前面没有任何对象,相当于window.sayHi(),所以输出undefined

call/apply/bind中的this

这三个产生的作用都是相同的,都是通过新传入的参数,从而this指向这个参数,只是这三个方法的调用稍有区别

function fn1() {
}
const obj = {}
fn1.call(obj, '新年快乐')
fn1.apply(obj, ['新年快乐']
fn1.bind(obj, '新年快乐')() // 注意bind绑定this,返回的是一个新对象
// 此时this指向obj

箭头函数中的this

const obj1 = {
    text: 'ob1',
    getText: () => {
        console.log(this.text)
    }
}
const obj2 = {
    text: 'ob2',
    getText: function() {
        (() => console.log(this.text))()
    }
}
obj1.getText()
obj2.getText()

答案

// undefined
// ob2

箭头函数,始终只要记住,this的指向是由外层作用域决定的,而obj2.getText()中,箭头函数外层是一个函数,这个函数的this指向obj2,所以输出是“ob2”

this的优先级

this的绑定方法有new,显式绑定(call/apply/bind),隐式绑定,如何确定优先级高低呢,光靠猜是不行的🤚🏼,实践出真章,通过下面例子一起进行探索

function fn1() {
    console.log(this.a)
}

const obj1 = {
    a: 1
}

const obj2 = {
    a: 2
}

fn1()
fn1.call(obj1)
fn1.call(obj2)

答案

// undefined
// 1
// 2

通过例子可以看出call方法显式绑定this,优先级大于隐式绑定

来个变种

var a = 0
var fn1 = () => console.log(this.a)
const obj1 = {
    a: 1,
}
const obj2 = {
    a: 2,
}
fn1.call(obj1)
fn1.call(obj2)

答案

// 0
// 0

要注意到fn1是一个箭头函数,然后通过call修改this的指向,输出依然是外层的a,证明箭头函数的this的绑定无法被修改

function food (text){
    this.text = text
}
const obj = {}

var x = food.bind(obj)
x('鸡肉')
console.log(obj.text)
var y = new x()
console.log(y.text)

答案

// 鸡肉
// undefined

通过food.bind的时候this指向obj,当执行x('鸡肉')的时候this.text等于obj.text所以输出“鸡肉”
通过new运算符返回新的实例不指向obj,所以输出undefined

所以得出结论new>显式绑定>隐式绑定

参考

《前端开发核心知识进阶》

希望通过本文能够帮助到你,本人写作经验不多,如有表达不对之处,欢迎评论留言指出
首发于个人博客:小白带你理解this