前言
前端面试中,手撕call
、apply
、bind
函数的实现是非常常见的,今天我们就来看下call
和apply
函数的自定义实现。
对自定义bind函数感兴趣的同学可以直接点击这里的传送门:经典面试题:手撕一个bind函数
call函数自定义实现
call函数定义:
call()
方法使用一个指定的 this
值和单独给出的一个或多个参数来调用一个函数。
举例:
// 声明Person类 class Person { // 声明私有属性name、age private name: string; private age: number; // 初始化时,传入name、age constructor(name: string, age: number) { this.name = name; this.age = age; } /** * @method printInfo * @description 打印个人信息 * @param sex string 传入性别信息 */ printInfo(sex: string) { console.log({ name: this.name, age: this.age, sex: sex, }); } } // 实例化类,创建对象 const p = new Person('Jhon', 20); // 调用printInfo方法 p.printInfo('男'); // {name: 'Jhon', age: 20, sex: '男'} // 调用系统方法call,改变this p.printInfo.call({name: '韩梅梅', age: 10}, '女'); // {name: '韩梅梅', age: 10, sex: '女'}
自定义实现:
call
方法需要注意的是this的指向、支持传入一个或多个参数,并且是立即返回函数执行结果。
因为JS中函数作用域是静态类型作用域,在函数声明的位置已经确定了~
function printThis () { // 如果是在dom中指向window,如果是nodejs中指向global console.log(this) } const man = { name: '小名', printThis () { // 指向的是man本身 console.log(this) } }
那如何将原函数的this
指向变为call
方法中传入的this
呢,我们可以借助对象调用自身方法的用法,更改原函数的this指向。
Up Code ~ 上码 ~
/** * @method myCall * @description 自定义call函数的实现 * @param context any 传入的this指向 * @param args any[] 传入的参数 */ // @ts-ignore Function.prototype.myCall = function (context: any, ...args: any[]) { // 1. 处理context可能是null或者是undefined的情况 if (context === undefined || context === null) { // 指向全局的this context = globalThis; } // 2. 处理context类型不是对象的情况 if (typeof context !== 'object') { context = new Object(context); } // 定义唯一key const fnKey = Symbol(); // 将原函数的本身this,赋值给context的fnKey属性上,因为fnKey是唯一不重复的,所以也不会影响context本身的属性 context[fnKey] = this; // 调用原函数 context[fnKey]这种形式,将this指向了context本身 // 传入传递的参数,接收结果 const res = context[fnKey](...args); // 将在处理过程中新增加的fnKey属性再删除,不出现`脏代码` delete context[fnKey]; // 返回最终的结果 return res; };
功能测试:
// @ts-ignore p.printInfo.myCall({ name: '韩梅梅', age: 10 }, '女'); // {name: '韩梅梅', age: 10, sex: '女'}
嘎嘎好使~
apply函数自定义实现
其实apply和call函数基本上就是一致的,唯一的区别是apply支持传递的参数是以数组形式传入的,简单调整下
// @ts-ignore Function.prototype.myApply = function (context: any, args: any[]) { // context的判断逻辑 if (context === undefined || context === null) { // 指向全局的this context = globalThis; } // 判断context不是对象 if (typeof context !== 'object') { context = new Object(context); } // 定义唯一key const fnKey = Symbol(); // 添加对象的方法 // 当前函数对象 context[fnKey] = this; const res = context[fnKey](...args); delete context[fnKey]; return res; };
功能测试:
// @ts-ignore p.printInfo.myApply({ name: '韩梅梅', age: 10 }, ['女']); // {name: '韩梅梅', age: 10, sex: '女'}
没有问题~~~