前端百题斩【015】——快速手撕call、apply、bind

写该系列文章的初衷是“让每位前端工程师掌握高频知识点,为工作助力”。这是前端百题斩的第15斩,希望朋友们关注公众号“执鸢者”,用知识武装自己的头脑。

在百题斩【014】中已经简要概述了call、apply、bind三个方法,这三者作用是相同的,均可以改变this指向,从而让某对象可以调用自身不具备的方法,本节将深入理解这三者的实现原理。

15.1 call()

img
15.1.1 基础

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。其返回值是使用调用者提供的this值和参数调用该函数的返回值,若该方法没有返回值,则返回undefined。

基本用法:

function.call(thisArg, arg1, arg2, ...)

小试牛刀

function method(val1, val2) {
    return this.a + this.b + val1 + val2;
}

const obj = {
    a: 1,
    b: 2
};

console.log(method.call(obj, 3, 4)); // 10
15.1.2 实现

实现一个call函数,将通过以下几个步骤:

  1. 获取第一个参数(注意第一个参数为null或undefined时,this指向window),构建对象

  2. 将对应函数传入该对象中

  3. 获取参数并执行相应函数

  4. 删除该对象中函数,消除副作用

  5. 返回结果

Function.prototype.myCall = function (context, ...args) {
    // 获取第一个参数(注意第一个参数为null或undefined时,this指向window),构建对象
    context = context ? Object(context) : window;
    // 将对应函数传入该对象中
    context.fn = this;
    // 获取参数并执行相应函数
    let result = context.fn(...args);
    // 消除副作用
    delete context.fn;
    // 返回结果
    return result;
}
// ……
console.log(method.myCall(obj, 3, 4)); // 10

15.2 apply()

img
15.2.1 基础

apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。其返回值是指定this值和参数的函数的结果。call()apply()的区别是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组

基本用法

func.apply(thisArg, [argsArray])

小试牛刀

function method(val1, val2) {
    return this.a + this.b + val1 + val2;
}

const obj = {
    a: 1,
    b: 2
};

console.log(method.apply(obj, [3, 4])); // 10
15.2.2 实现

apply和call的区别主要是参数的不同,所以其实现步骤的call大体类似,如下所示:

Function.prototype.myApply = function (context, arr) {
    context = context ? Object(context) : window;
    context.fn = this;

    let result = arr ? context.fn(...arr) : context.fun();

    delete context.fn;

    return result;
}
// ……
console.log(method.myApply(obj, [3, 4])); // 10

15.3 bind()

img
15.3.1 基础

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。该函数的返回值是一个原函数的拷贝,并拥有指定的this值和初始参数。

基本用法

function.bind(thisArg[, arg1[, arg2[, ...]]])

小试牛刀

function method(val1, val2) {
    return this.a + this.b + val1 + val2;
}

const obj = {
    a: 1,
    b: 2
};

const bindMethod = method.bind(obj, 3, 4);
console.log(bindMethod()); // 10
15.3.2 实现

实现一个bind函数相对较复杂一些,应该注意以下几点:

  1. 能够改变this指向;

  2. 返回的是一个函数;

  3. 能够接受多个参数;

  4. 支持柯里化形式传参 fun(arg1)(arg2);

  5. 获取到调用bind()返回值后,若使用new调用(当做构造函数),bind()传入的上下文context失效。

Function.prototype.myBind = function (context, ...args) {
    if (typeof(this) !== 'function') {
        throw new TypeError('The bound object needs to be a function');
    }

    const self = this;
    // 定义一个中装函数
    const fNOP = function() {};
    const fBound = function(...fBoundArgs) {
        // 利用apply改变this指向
        // 接受多个参数+支持柯里化形式传参
        // 当返回值通过new调用时,this指向当前实例 (因为this是当前实例,实例的隐士原型上有fNOP的实例(fnop);fnop instanceof fNOP为true)
        return self.apply(this instanceof fNOP ? this : context, [...args, ...fBoundArgs]);
    }

    // 将调用函数的原型赋值到中转函数的原型上
    if (this.prototype) {
        fNOP.prototype = this.prototype;
    }
    // 通过原型的方式继承调用函数的原型
    fBound.prototype = new fNOP();

    return fBound;
}

1.如果觉得这篇文章还不错,来个分享、点赞、在看三连吧,让更多的人也看到~

2.关注公众号执鸢者,领取学习资料,定期为你推送原创深度好文

3.关注公众号进群,里面大佬多多,一起向他们学习

1. 前端百题斩[001]——typeof和instanceof

2. 前端百题斩【002】——js中6种变量声明方式

3. 前端百题斩【003-004】——从基本类型、引用类型到包装对象

4. 前端百题斩【005】—— js中9种遍历对象的方法

5. 前端百题斩【006】——js中三类字符串转数字的方式

6. 前端百题斩【007】——js中必须知道的四种数据类型判断方法

7. 前端百题斩【008-009】——从JavaScript的代码执行过程到函数执行过程

8. 前端百题斩【010】——通俗易懂的JavaScript执行上下文

9. 前端百题斩【011】——通俗易懂的变量对象

10. 前端百题斩【012】——js中作用域及作用域链的真面目

11. 前端百题斩【013】——用“闭包”问题征服面试官

12. 前端百题斩【014】——js中的这些“this”指向都值得了解

13. 三步法解析Express源码

14. 一篇搞定前端高频手撕算法题(36道)

15. 十七张图玩转Node进程——榨干它

16. 理论与API相结合理解Node中的网络通信

17. 一文彻底搞懂前端监控

18. 前端的葵花宝典——架构

19. canvas从入门到猪头

20. 前端工程师的一大神器——puppeteer

21. 2021 年前端宝典【超三百篇】

22. 前端也要懂机器学习(上)

23. 前端也要懂机器学习(下)

24. 学架构助力前端起飞

25. 假如只剩下canvas标签

26. Vue源码思想在工作中的应用

27. 一文搞定Diff算法

28. 百度、小红书三面,均遇“赛马”问题

29. 十五张图带你彻底搞懂从URL到页面展示发生的故事

30. 一文搞懂Cookie、Storage、IndexedDB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值