zl程序教程

您现在的位置是:首页 >  前端

当前栏目

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

前端 快速 call bind apply 015
2023-09-27 14:25:56 时间

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


15.1 call()

640.jpg

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函数 将通过以下几个步骤

获取第一个参数 注意第一个参数为null或undefined时 this指向window 构建对象将对应函数传入该对象中获取参数并执行相应函数删除该对象中函数 消除副作用返回结果


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()

640.jpg

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()

640.jpg

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函数相对较复杂一些 应该注意以下几点

能够改变this指向 返回的是一个函数 能够接受多个参数 支持柯里化形式传参 fun(arg1)(arg2) 获取到调用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;

}