大厂面经 - 聊聊经常被面试官问到的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 指向 ,并且可以动态地传递参数,功能相似只有使用方式略微有点区别

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

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

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

相关推荐
Мартин.4 分钟前
[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入
前端·xss
昨天;明天。今天。2 小时前
案例-表白墙简单实现
前端·javascript·css
数云界2 小时前
如何在 DAX 中计算多个周期的移动平均线
java·服务器·前端
风清扬_jd2 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
安冬的码畜日常2 小时前
【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次
开发语言·前端·javascript·函数式编程·tdd·fp·jasmine
ChinaDragonDreamer2 小时前
Vite:为什么选 Vite
前端
小御姐@stella2 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
GISer_Jing2 小时前
【React】增量传输与渲染
前端·javascript·面试
GISer_Jing2 小时前
WebGL在低配置电脑的应用
javascript
eHackyd2 小时前
前端知识汇总(持续更新)
前端