1.最简单的call
Function.prototype.myCall = function(thisArg, ...args) {
// 1.获取需要被执行的函数,此时的this就是函数
let fn = this
// 2.对thisArg转成对象类型(防止它传入的是非对象类型) Object(thisArg)可以把基本数据类型变成对象类型
thisArg = thisArg !== undefined && thisArg !== null ? Object(thisArg) : window
// 因为需要改变指向,所以赋值为一个属性
thisArg.fn = fn
// 3.调用需要被执行的函数
thisArg.fn(...args)
// 删除这个多余的属性 打印的时候这属性还在
delete thisArg.fn
}
function foo(num1, num2) {
console.log('foo', num1, num2, this)
}
foo.myCall(0, 1, 2, 3, 4, 5, 6, 7)
2.有一点不一样的apply
// 这里参数可以来个默认参数,防止没传参数时,解构会报错
Function.prototype.myApply = function(thisArg, args = []) {
// 1.获取到要执行的函数
let fn = this
// 2.处理绑定的thisArg
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
// 3.执行函数
thisArg.fn = fn
thisArg.fn(...args)
delete thisArg.fn
}
function foo(num1, num2) {
console.log('foo函数被执行', this, num1, num2)
}
foo.myApply({ name: 123 }, [1, 2, 3])
3.最后的bind
Function.prototype.myBind = function(thisArg, ...args) {
// 这里需要使用箭头函数,谁调用了myBind,this指向谁
// 如果用 return function(){} 调用时,this变为了window
return (...argArray) => {
// 1.绑定this
let fn = this
console.log(fn)
// 2.判定thisArg
thisArg =
thisArg !== undefined && thisArg !== null ? Object(thisArg) : window
thisArg.fn = fn
thisArg.fn(...[...args, ...argArray])
delete thisArg.fn
}
}
function foo(num1, num2, num3) {
console.log('foo被执行', this, num1, num2, num3)
}
let bar = foo.myBind('123', 1, 2)
bar(3)
4.有一点绕的柯里化
//柯里化函数的实现
function myCurrying(fn) {
function curried(...args) {
// 判断当前已经接受参数的个数是否和函数本身接受的参数个数一致
// 若传入的参数个数大于或等于函数的参数个数,直接执行
if (args.length >= fn.length) {
fn.call(this, ...args)
} else {
//没有达到个数时,需要返回新的函数,继续接受参数
return function (...args2) {
//接受参数后,递归调用curried来检查参数个数 需要返回到最外层,所以return
return curried.call(this, ...[...args, ...args2])
}
}
}
return curried
}
//测试函数
function foo(x, y, z) {
console.log(x, y, z, this)
}
let myCallried = myCurrying(foo)
myCallried(1)(2)(3)
// myCallried(1, 2)(3)
// myCallried(1)(2, 3)