Hi,你了解 this 吗?

184 阅读3分钟

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

前言

学习 JavaScript,离不开 this,今天我们就来聊聊 this,看看你是否对 this 有足够的了解。那我们开始吧。

this 执行主体,谁把它执行的「和在哪创建&在哪执行都没有必然的关系」。this 有五种情况,下面逐一举例,结合例子来熟悉。

函数执行,看方法前面是否有“点”

函数执行,看方法前面是否有“点”,没有“点”,this 是 window「严格模式下是 undefined」,有“点”,“点”前面是谁,this 就是谁。

我们看个例子:

const fn = function fn() {
    console.log(this);
}
let obj = {
    name: '追梦玩家',
    fn: fn
};
fn(); // window
obj.fn(); // {name: "追梦玩家", fn: ƒ}

给当前元素的某个事件绑定方法

给当前元素的某个事件绑定方法,当事件行为触发,方法中的 this 是当前元素本身「排除 attachEvent」。

下面的代码给 document.body 绑定事件,当点击事件被触发的时候,打印出来就是 document.body

document.body.addEventListener('click', function() {
    console.log(this);
})

构造函数中的 this 是当前类的实例

我们看下下面代码的输出结果,是什么?

function Factory() {
    this.name = '追梦玩家';
    this.age = 18;
    console.log(this);
}
let f = new Factory(); // {name: "追梦玩家", age: 18}

箭头函数

箭头函数中没有执行主体,所用到的 this 都是其所处上下文的 this。要怎么理解呢?

let demo = {
    name: 'DEMO',
    fn() {
        console.log(this); // {name: "DEMO", fn: ƒ}
        setTimeout(function() {
            console.log(this); // window
        }, 1000);

        setTimeout(() => {
            console.log(this);  // {name: "DEMO", fn: ƒ}
        }, 1000);
    },
}
demo.fn();

从上面的例子,我们可以看出箭头函数的 this,其实就是所在上下文的 this,也就是例子中的 demo 对象。

call/apply/bind

其实就是基于 Function.prototype 上的 call/apply/bind 方法去改变 this 指向。

来个例子:

function func(x, y) {
    console.log(this, x, y);
}
let obj = {
    name: '追梦玩家'
};
func.call(obj, 10, 20);
func.apply(obj, [10, 20]);
document.body.addEventListener('click', func.bind(obj, 10, 20));

输出结果都为 {name: "追梦玩家"} 10 20

我们来看看,为什么输出结果都是这个样子?

其实可以简单理解,因为 obj 对象没有 func 这个方法,所以借用 func 这个方法,func 函数自身没有 call 方法,所以会基于 __proto__ 找到 Function.prototype.call,把 call 方法执行,在 call 方法内部 「call 执行的时候」 call(context->obj,...params->[10,20])。

call 方法的原理:

  1. 把 func 中的 this 改为 obj
  2. 并且把 params 接收到的值当做实参传递给 func 函数
  3. 并且让 func 函数立即执行

知道 call 方法的原理,其实我们就可以自己手写一下 this。大家可以尝试写一下。

apply 和 call 的第一个参数都是 this,区别在于通过 apply 调用时实参是放到数组中的,而通过 call 调用时实参是逗号分隔的。

bind 方法的原理:

  1. 和 call/apply 的区别,并没有把 func 立即执行
  2. 把传递进来的 obj/10/20 等信息存储起来「闭包」
  3. 执行 bind 返回一个新的函数,例如:proxy,把 proxy 绑定给元素的事件,当事件触发执行的时候是返回的 proxy
  4. 在 proxy 内部,再去把 func 执行,把 this 和值都改变为之前存储的那些内容

总结

  • this 的五种情况分析
    • this 执行主体,谁把它执行的「和在哪创建&在哪执行都没有必然的关系」
    • Q1:函数执行,看方法前面是否有“点”,没有“点”,this 是 window「严格模式下是 undefined」,有“点”,“点”前面是谁,this 就是谁
    • Q2:给当前元素的某个事件绑定方法,当事件行为触发,方法中的 this 是当前元素本身「排除 attachEvent」
    • Q3:构造函数中的 this 是当前类的实例
    • Q4:箭头函数中没有执行主体,所用到的 this 都是其所处上下文的 this
    • Q5:可以基于 Function.prototype 上的 call/apply/bind 去改变 this 指向

文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你或者喜欢,欢迎点赞和关注。