大厂面经 - 聊聊经常被面试官问到的apply和call

1. 前言

最近在家刷抖音刷时间长了,感觉脑壳生疼,于是干脆手机一扔,打开电脑继续刷~~~

无意看到了之前整理的高频面试笔记 ,发现很多都有点陌生了,之后我有时间会陆续把一些比较重要的前端知识更新出来

尤其是现在就业环境不太好的情况下,一方面自己复习巩固下防患于未然,一方面也给小伙伴们的面试助一把力啦

2. apply和call的异同

为什么这篇文章单说它俩呢? 因为它俩挺像的~~~

它们兄弟俩在功能上挺相似 的,都是改变函数在调用时候的上下文

那啥是上下文

通俗说就是过年走亲戚,你在你大姨家住了一晚,你大姨家就是当前所在的上下文。包括你看的电视,睡得床都是你大姨家的。

你过几天又去了你老姨家住了一晚,那当前所在的上下文就是你老姨家。

整明白上下文之后,那为什么需要改变函数在调用时上下文呢?

又比如一直都是奥特曼打怪兽 (这梗网上很火),今天你家二哈在电视上看到了,狗血沸腾 ,于是它也想打怪兽

但是呢?它又不是光,它只是一条狗,那怎么办呢?

简单,用apply 或者call 就行了,它俩的使用方式有点区别

apply

js 复制代码
奥特曼打怪兽.apply(二哈)

call

js 复制代码
奥特曼打怪兽.call(二哈)

这样一波操作后,基本就变成了二哈打怪兽。如果二哈还想顺便用自己的迷踪拳无影脚去打怪兽,咋办呢?简单

apply

js 复制代码
奥特曼打怪兽.apply(二哈, [迷踪拳, 无影脚])

call

js 复制代码
奥特曼打怪兽.apply(二哈, 迷踪拳, 无影脚)

说到这,你是不是已经知道这两货是干什么的了,也知道怎么用的啦

下面,我们去进去它俩的内心看看有什么吧~~~

3. apply

js 复制代码
Function.prototype.myApply = function(context, argsArray) {
  // 如果传入的上下文为 null 或 undefined,则默认为全局对象
  context = context || window;
  // 将当前函数作为上下文对象的一个属性
  context.fn = this;
  // 执行函数,并将传入的参数数组作为参数
  const result = context.fn(...argsArray);
  // 删除上下文对象的 fn 属性
  delete context.fn;
  // 返回函数执行结果
  return result;
};

想理解上面的含义,我们先来看下真正的具体使用

磨刀不误砍柴功,这可以让你的思维更清晰点

js 复制代码
function ATM() {
    console.log('功夫招式:', ...arguments)
    this.name = '奥特曼'
    this.act = ()=>{
        console.log('打怪兽')
    }
}

function Dog() {
    ATM.apply(this, ['迷踪拳', '无影脚'])
}

new Dog()

通过上面我们可以知道两点

  • ATM.apply传了['迷踪拳', '无影脚'],而ATM接受到的是'迷踪拳', '无影脚'

  • ATM是一个函数对象,也就是说apply只能通过函数来调用(call同理)

然后,我们回过头来看源码

js 复制代码
Function.prototype.myApply = function(context, argsArray) {}

我们写了一个函数myApply() ,给了Function对象的原型对象(prototype),这里就可以知道只有Function 对象有这个功能

function(context, argsArray)形参是argsArray不是...argsArray(call用的这个),两者是有区别的,下文会说

js 复制代码
// 将当前函数作为上下文对象的一个属性
  context.fn = this;
  // 执行函数,并将传入的参数数组作为参数
  const result = context.fn(...argsArray);
  // 删除上下文对象的 fn 属性
  delete context.fn;

这三行代码,不知道有没有小伙伴不是很了解,这里顺便说下

  • 随便定义个属性承载Dog上下文

  • 调用Dog并且传入接收到的参数

  • 删除刚定义的属性

总的说apply的核心 就是接收目标上下文和参数执行目标上下文并携带参数

4. call

接下来我们说说call,其实在说过apply之后,再看它就发现连源码都基本一样

js 复制代码
Function.prototype.myCall = function(context, ...args) {
  // 如果传入的上下文为 null 或 undefined,则默认为全局对象
  context = context || window;
  // 将当前函数作为上下文对象的一个属性
  context.fn = this;
  // 执行函数,并将传入的参数作为参数
  const result = context.fn(...args);
  // 删除上下文对象的 fn 属性
  delete context.fn;
  // 返回函数执行结果
  return result;
};

我们挑些核心的说下吧

js 复制代码
Function.prototype.myApply = function(context, ...args) {}

小伙伴也注意到这里和apply其实是区别的

...args其实就是个reset参数,这个和解构是有点区别的

解构

js 复制代码
const arr = [1,2,3]
console.log(...arr)  // 1 2 3

reset

js 复制代码
function test (...arr){
    console.log(arr) // [1, 2, 3]
}
test(1,2,3)

reset的意思就是汇总所有参数到一个数组里

5. 使用场景

  • 继承:apply和call最常见的就是继承了,上面的很多例子都是继承的意思

  • Object.prototype.toString.call():这个面试经常会见到的,一个通用的方法,用来获取一个属性的类型

js 复制代码
let a = [1,2]
console.log(Object.prototype.toString.call(a))  // [object Array]

对了,继承三剑客 还有一个bind ,因为以我实际工作中的经验来看用到的次数很少,远没有applycall这两个哥们实用,这里我大致聊下吧~~~

bind 也是可以在调用函数时改变其上下文的,它不同的是,返回值是一个函数,需要再调用一下才会执行。

js 复制代码
function ATM (a, b) {
    return a + b;
}

function Dog (a, b) {
    return a - b;
}

ATM.bind(Dog, 666, 999)();

这样就会执行了,打印是1665

9. 总结

applycall 可以在调用函数时改变函数内部的 this 指向 ,并且可以动态地传递参数,功能相似只有使用方式略微有点区别

这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。

欢迎转载,但请注明来源。

最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。

相关推荐
阿幸软件杂货间3 分钟前
阿幸课堂随机点名
android·开发语言·javascript
一涯5 分钟前
Cursor操作面板改为垂直
前端
我要让全世界知道我很低调12 分钟前
记一次 Vite 下的白屏优化
前端·css
threelab12 分钟前
three案例 Three.js波纹效果演示
开发语言·javascript·ecmascript
1undefined214 分钟前
element中的Table改造成虚拟列表,并封装成hooks
前端·javascript·vue.js
蓝倾1 小时前
淘宝批量获取商品SKU实战案例
前端·后端·api
comelong1 小时前
Docker容器启动postgres端口映射失败问题
前端
花海如潮淹1 小时前
硬件产品研发管理工具实战指南
前端·python
用户3802258598241 小时前
vue3源码解析:依赖收集
前端·vue.js
用户7579419949701 小时前
基于JavaScript的简易Git
javascript