经典面试题:手撕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: '女'}


没有问题~~~



相关文章
|
5月前
|
存储 SQL 数据库
面试题20: 存储过程和函数的区别
面试题20: 存储过程和函数的区别
|
2月前
|
机器学习/深度学习
【机器学习】如何判断函数凸或非凸?(面试回答)
文章介绍了如何判断函数是凸函数还是非凸函数,包括凸函数的定义、几何意义、判定方法(一元函数通过二阶导数判断,多元函数通过Hessian矩阵的正定性判断),以及凸优化的概念和一些经典的凸优化问题。
124 1
【机器学习】如何判断函数凸或非凸?(面试回答)
|
2月前
|
JavaScript
【Vue面试题八】、为什么data属性是一个函数而不是一个对象?
这篇文章解释了为什么在Vue中组件的`data`属性必须是一个函数而不是一个对象。原因在于组件可能会有多个实例,如果`data`是一个对象,那么这些实例将会共享同一个`data`对象,导致数据污染。而当`data`是一个函数时,每次创建组件实例都会返回一个新的`data`对象,从而确保了数据的隔离。文章通过示例和源码分析,展示了Vue初始化`data`的过程和组件选项合并的原理,最终得出结论:根实例的`data`可以是对象或函数,而组件实例的`data`必须为函数。
【Vue面试题八】、为什么data属性是一个函数而不是一个对象?
面试官: 请你手写一份 Call()源码,看完此篇不用担心!
面试官: 请你手写一份 Call()源码,看完此篇不用担心!
|
3月前
|
安全 Android开发 Kotlin
Android经典面试题之Kotlin中常见作用域函数
**Kotlin作用域函数概览**: `let`, `run`, `with`, `apply`, `also`. `let`安全调用并返回结果; `run`在上下文中执行代码并返回结果; `with`执行代码块,返回结果; `apply`配置对象后返回自身; `also`附加操作后返回自身
49 8
|
2月前
|
安全 编译器 C++
【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数
【剑指offer】2.2编程语言(p22-p25)——面试题1:string赋值运算函数
|
3月前
|
Android开发 Kotlin
Android面试题之kotlin中怎么限制一个函数参数的取值范围和取值类型等
在Kotlin中,限制函数参数可通过类型系统、泛型、条件检查、数据类、密封类和注解实现。例如,使用枚举限制参数为特定值,泛型约束确保参数为Number子类,条件检查如`require`确保参数在特定范围内,数据类封装可添加验证,密封类限制为一组预定义值,注解结合第三方库如Bean Validation进行校验。
61 6
|
4月前
|
缓存 NoSQL Java
15分钟面试被5连CALL,你扛得住么?
在Java并发编程中,锁是控制共享资源访问的关键,用于避免数据竞争、保证原子性、维护执行顺序、提高性能、实现同步及避免死锁。分布式锁在多节点系统中同样重要,确保一致性、防止资源冲突、提高可扩展性并解决竞态条件。实现分布式锁的方法包括基于数据库、缓存(如Redis)、Zookeeper等。选型时要考虑性能、可靠性、可扩展性和特定场景需求,如一致性、可用性和分区容忍性。
|
5月前
|
机器学习/深度学习 数据采集 自然语言处理
python函数参数的传递、带星号参数的传递,2024年大厂Python高级面试题分享
python函数参数的传递、带星号参数的传递,2024年大厂Python高级面试题分享
|
5月前
|
数据采集 Python
10个Python set 常用操作函数!,bilibili面试题
10个Python set 常用操作函数!,bilibili面试题
10个Python set 常用操作函数!,bilibili面试题