经典面试题:手撕call/apply函数实现

简介: 前端面试中,手撕call、apply、bind函数的实现是非常常见的,今天我们就来看下call和apply函数的自定义实现。对自定义bind函数感兴趣的同学可以直接点击这里的传送门:经典面试题:手撕一个bind函数

前言


前端面试中,手撕callapplybind函数的实现是非常常见的,今天我们就来看下callapply函数的自定义实现。


对自定义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: '女'}


没有问题~~~



相关文章
|
6天前
|
存储 SQL 数据库
面试题20: 存储过程和函数的区别
面试题20: 存储过程和函数的区别
|
6天前
|
前端开发 UED
【面试题】async/await 函数到底要不要加 try catch ?
【面试题】async/await 函数到底要不要加 try catch ?
|
6天前
|
自然语言处理 前端开发
阿里面试官:如何给所有的async函数添加try/catch?
阿里面试官:如何给所有的async函数添加try/catch?
|
4天前
|
数据采集 Python
10个Python set 常用操作函数!,bilibili面试题
10个Python set 常用操作函数!,bilibili面试题
10个Python set 常用操作函数!,bilibili面试题
|
4天前
|
数据采集 数据挖掘 关系型数据库
Excel计算函数(计算机二级)(1),2024年最新2024Python架构面试指南
Excel计算函数(计算机二级)(1),2024年最新2024Python架构面试指南
|
4天前
|
存储 Java Shell
【Python学习教程】Python函数和lambda表达式_6(1),2024蚂蚁金服面试题及答案
【Python学习教程】Python函数和lambda表达式_6(1),2024蚂蚁金服面试题及答案
|
4天前
|
机器学习/深度学习 数据采集 自然语言处理
python函数参数的传递、带星号参数的传递,2024年大厂Python高级面试题分享
python函数参数的传递、带星号参数的传递,2024年大厂Python高级面试题分享
|
6天前
|
存储 JavaScript 前端开发
每日一道javascript面试题(九)函数的参数可以和函数体中的变量重名吗
每日一道javascript面试题(九)函数的参数可以和函数体中的变量重名吗
|
6天前
|
存储 Go 开发者
Golang深入浅出之-Go语言字符串操作:常见函数与面试示例
【4月更文挑战第20天】Go语言字符串是不可变的字节序列,采用UTF-8编码。本文介绍了字符串基础,如拼接(`+`或`fmt.Sprintf()`)、长度与索引、切片、查找与替换(`strings`包)以及转换与修剪。常见问题包括字符串不可变性、UTF-8编码处理、切片与容量以及查找与替换的边界条件。通过理解和实践这些函数及注意事项,能提升Go语言编程能力。
28 0
|
6天前
|
算法 搜索推荐 C++
浅谈sort函数底层(一道c++面试的天坑题)
浅谈sort函数底层(一道c++面试的天坑题)