持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第24天,点击查看活动详情
前言
说起手写apply、call等方法是面经中常看到的问题了,其实我面试没有遇到过,但没遇到过不代表就不需要掌握。这些应该只有大厂才会考,作为一个双非毕业的普通人,还是要有一个进大厂的梦,万一哪天实现了呢,人一旦没了梦想,和咸鱼有什么区别。今天就带大家一起来实现一下简易版的call和apply。
apply
关于apply
的定义,我其实没在网上找到相关的资料,找到的都是apply
的用法和call
的区别,我理解的就是改变执行函数中执行上下文this的指向,执行该函数。所以我们需要实现的就是其实就只有两点:
1.改变函数的this指向。
2.如果函数有参数,我们把函数参数传入。
// 改变函数this指向 我们应该从Function原型上入手
Function.prototype.myApply = (context) { // context表示函数传入的执行上下文
if(typeof this !== 'function' ) { // 调用apply的应该是一个函数,this就是调用apply的函数
throw new Error('type Error')
}
if (context === null || context === undefined) {
context = window // 执行上下文不存在时直接指向全局window
} else {
context = Object(context) // 值为原始值(Number,Boolean,String),this指向其实例对象 引用类不变
}
const fnSymbol = Symbol()
context[fnSymbol] = this
context[fn] = this // 其实这儿用fn表好理解,这儿用Symbol是为了防止调用函数的对象中有fn属性。
const args = arguments[1] // 获取参数
// apply的第二个参数是数组, 记住appy和array对应就行了 之前老师记不住
const result = args.length > 1 ? context[fnSymbol](...args) : context[fnSymbol]()
delete context[fnSymbol]
return result
}
接下来我们来验证一下我们手写的apply和原生的执行结果有什么不一样,这里只验证最基本的用法,高深的我不会用,更不谈实现了。
const obj = {
data: [1, 3, 4],
add: function (a, b, c) {
return a + b + c
}
}
const add = obj.add;
const res = add.myApply(obj, obj.data)
const res1 = add.apply(obj,obj.data)
console.log(res,res1) // 8 8
call
call
实现和apply
差不多,唯一需要注意的就是call
接收函数参数时是一个一个传入的。
Function.prototype.myCall = function(context) {
if (typeof this !== 'function'){
console.log('type Error')
}
if (context === null && context === undefined){
context = window // 若执行上下文为空直接指向全局window
}else {
context = Object(context) // 基础数据类型转化为实例对象
}
const fnSymbol = Symbol()
context[fnSymbol] = this;
const args = [...arguments].slice(1) // 这里使用结构获取函数传入的所有参数。
const result = context[fnSymbol](...args)
delete context[fnSymbol]
return result
}
// 验证
const obj = {
data: [1, 3, 4],
add: function (a, b, c) {
return a + b + c
}
}
const add = obj.add;
const res = add.myCall(obj, ...obj.data)
const res1 = add.call(obj, ...obj.data)
console.log(res,res1) // 8 8
最后
还有最难的bind
手动实现,有点难,我自己还没搞明白,就不出来献丑了,先立个flag,等我搞明白了再回来补上。