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 ,因为以我实际工作中的经验来看用到的次数很少,远没有apply 和call这两个哥们实用,这里我大致聊下吧~~~
bind 也是可以在调用函数时改变其上下文的,它不同的是,返回值是一个函数,需要再调用一下才会执行。
js
function ATM (a, b) {
return a + b;
}
function Dog (a, b) {
return a - b;
}
ATM.bind(Dog, 666, 999)();
这样就会执行了,打印是1665
9. 总结
apply 和 call 可以在调用函数时改变函数内部的 this 指向 ,并且可以动态地传递参数,功能相似只有使用方式略微有点区别
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。