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

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

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

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

相关推荐
m0_748239334 分钟前
前端(Ajax)
前端·javascript·ajax
Fighting_p7 分钟前
【记录】列表自动滚动轮播功能实现
前端·javascript·vue.js
前端Hardy9 分钟前
HTML&CSS:超炫丝滑的卡片水波纹效果
前端·javascript·css·3d·html
技术思考者13 分钟前
HTML速查
前端·css·html
缺少动力的火车13 分钟前
Java前端基础—HTML
java·前端·html
Domain-zhuo26 分钟前
Git和SVN有什么区别?
前端·javascript·vue.js·git·svn·webpack·node.js
雪球不会消失了31 分钟前
SpringMVC中的拦截器
java·开发语言·前端
李云龙I42 分钟前
解锁高效布局:Tab组件最佳实践指南
前端
m0_748237051 小时前
Monorepo pnpm 模式管理多个 web 项目
大数据·前端·elasticsearch
JinSoooo1 小时前
pnpm monorepo 联调方案
前端·pnpm·monorepo