apply
改变函数的 this 执向, 这个方法是在对象的原型链上的, 调用会执行目标函数, 第一个参数是需要指向的 this, 第二参数是一个数组, 是函数的参数
function myApply (context, ...args) {
if (typeof context !== 'function') {
throw new Error('type error')
}
// 要执行函数, 所以要记录函数本身
const self = this // 这个 this 执向调用者
/** 这里有个知识点, 普通函数的调用者是谁, 他的 this 就执向谁, 所以我们可以将传进来的 this
修改下
**/
context = context === null || context === undefined ? globalThis : Object(context)
// 这样我们将传进来的这个 this 包装成一个对象
// 执行函数
// 执行前我们需要将当前的函数执行, 并将当前函数中的 this 指向传入的 this
const key = Symbol()
/*
这里的原理:
const context = {
key: function (arg) {
console.log(this)
}
}
context.key(arg) // context
看到这里你应该明白为什么箭头函数不能使用这些方法了吧
*/
context[key] = self
const res = context[key](...args)
/*
传入的 this 可能是一个对象, 如果我们在其身上添加数据, 会出现意想不到的错误, 所以需要删除我们添加的属性
*/
deleted context[key]
return res
}
call
改变函数的 this 执向, 这个方法是在对象的原型链上的, 调用会执行目标函数, 第一个参数是需要指向的 this,后面的参数以 , 隔开,是函数的参数
// call 和 apply 的实现一致
function myCall (context, ...args) {
if (typeof context !== 'function'){
throw new Error('type error')
}
const self = this
context = context === null || context === undefined ? globalThis : Object(context)
const key = Symbol()
context[key] = self
const res = context[key](...args)
deleted context[key]
return res
}
bind
改变函数的 this 指向, 这个方法在对象的原型链上, 调用会返回一个新的函数, 不会执行目标函数, 第一个参数是需要执行的 this, 后面的参数以 , 隔开, 是函数的参数
function myBind(context, ...bindData) {
if (typeof context !== 'function') {
throw new Error('type error')
}
const self = this
// 柯里化函数
return function (..args) { // 返回的函数也可以接受参数
const allParams = bindData.concat(args)
return self.myApply(context, allParams) // 执行这个函数的返回值
}
}