Day20 - 如何实现call和apply、bind

1,244 阅读4分钟

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

知识讲解

call、apply、bind的功能就是改变this的指向、只是三个函数的参数风格不太一样。

call函数实现

[function].call([this], [param]...),一句话概括:call() 将函数的 this 指定到 call() 的第一个参数值同时将剩余参数指定的情况下调用某个函数或方法。

用测试用例描述

  it("测试call方法", () => {
    const { call } = require("../index");
    Function.prototype.myCall = call;
    const obj = { a: 1 };
    const f = function (...args) {
      return { context: this, args };
    };
    expect(f.myCall(obj, 1, 2)).toEqual({ context: { a: 1 }, args: [1, 2] });
  });

代码实现

exports.call = function (context, ...args) {
  // this 为调用方法 例:f.call this = f
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
};

讲解

  • f.myCall函数调用时,this上下文其实是f函数。
  • 需要的绑定的上下文用参数context传入
  • 其余参数使用展开语法表示
  • 首先将函数绑定到上下文对象中
  • 删除context上线文中的fn方法清理现场
  • 返回执行结果

apply的实现

call方法和apply方法类似,两者唯一不同的是,执行参数是是一个数组而不是多个参数

测试用例表示

it("测试apply方法", () => {
    const { apply } = require("../index");
    Function.prototype.myApply = apply;
    const obj = { a: 1 };
​
    const f = function (...args) {
      return { context: this, args };
    };
    expect(f.myApply(obj, [1, 2])).toEqual({ context: { a: 1 }, args: [1, 2] });
});

代码实现

由于变动非常小甚至可以直接调用call函数完成

exports.apply = function (context, args) {
    return this.call(context,...args)
};

正经代码是这样

exports.apply = function (context, args) { // 只需要变动一行 ...args => args
  // this 为调用方法 例:f.call this = f
  context.fn = this;
  const result = context.fn(...args);
  delete context.fn;
  return result;
};

bind函数

bind返回fun的拷贝,并指定了fun的this指向,保存了fun的参数。

测试用例表示

it("测试bind方法", () => {
    const { bind } = require("../index");
    Function.prototype.myBind = bind;
    const obj = { a: 1 };
​
    const f = function (...args) {
      return { context: this, args };
    };
    expect(f.bind(obj, 1, 2)(3, 4)).toEqual({
      context: { a: 1 },
      args: [1, 2, 3, 4],
    });
});

代码实现

exports.bind = function (context, ...args) {
  // this 为调用方法 例:f.call this = f
  const f = this;
  return function F() {
    return f.apply(context, [...args, ...arguments]);
  };
};
  • 实现一个工厂函数
  • 使用apply指向函数
  • 使用传入的context作为上下文
  • 将bind传入的执行参数与执行F()时传入的参数合并作为执行参数

image-20220120174836487

面试攻略

这个就是一道经典的手写代码题。

🔥2022然叔坚持打卡365天

大家一波一键三连和然叔一起呀

Day1 - JS 整数是怎么表示的?

Day2 - 0.1 + 0.2 === 0.3 嘛?为什么?怎么解决?

Day3 - Number() 的存储空间是多大?如果后台发送了一个超过最大限制的数字怎么办?

Day4 - 判断数据类型的方式有哪些?

Day5 - new 一个函数发生了什么?

Day6 - new 一个构造函数,如果函数返回 return {}return nullreturn 1return true 会发生什么情况?

Day7 - 分析一下箭头语法为什么不能当做构造函数

Day8 - 什么是闭包?如何产生闭包

Day9 - 如何用闭包制造惰性函数?

Day14 - 词法作用域、块级作用域、作用域链、静态动态作用域

Day10 - 闭包与科里化、偏应用函数的关系

Day11 - 谈谈闭包与即时函数的应用

Day12 - 如何利用闭包完成类库封装

Day13 - 如何用闭包完成模块化(Webpack原理)

Day15 - let为什么能够解决循环陷阱

Day16 - 为什么一定要有块级作用域?

Day17 - let是否会造成变量提升

Day18 - 介绍一下this指向4种形式

Day19 - React中的事件绑定与箭头函数

===============