手写系列:call、apply、bind、函数柯里化

少废话,show my code

call

原理都在注释里了

// 不覆盖原生call方法,起个别名叫myCall,接收this上下文context和参数params
Function.prototype.myCall = function (context, ...params) {
  // context必须是个对象并且不能为null,默认为window
  const _this = typeof context === "object" ? context || window : window;
  // 为了避免和原有属性冲突,定义一个Symbol类型的属性
  const key = Symbol();
  // call方法的目的是改变函数的this指向,函数的this指向它的调用者,也就是说我们的目标是改变函数的调用者。
  // 下面的this就是函数本身,给_this增加一个名为[key]的方法指向this,就能用_this来调用this了
  _this[key] = this;
  const result = _this[key](...params);
  // 获取函数执行结果后,删除以上添加的属性
  delete _this[key];
  return result;
};

apply

和call的区别在于第二个参数

Function.prototype.myApply = function (context, params) {
  return this.myCall(context, ...params);
};

bind

和call的区别在于不立即执行,返回一个函数即可

Function.prototype.myBind = function (context, ...params) {
  const _this = this;
  // 返回的函数也能接收参数,但是是放在params后面
  return function (...args) {
    return _this.myCall(context, ...[...params, ...args]);
  };
};

函数柯里化

函数柯里化,举例,有如下函数

function test(a, b, c, d, e) {
  console.log(a + b + c + d + e);
}

有一个curry转换函数对test函数进行一些转换

function curry(){
  // todo
}
const transformTest = curry(test, ...args)

转换之后,原本一次性传过去的参数现在可以分步传参

// 使得
test(1,2,3,4,5)
// 等同于
transformTest(1)(2)(3)(4)(5)
// 或者
transformTest(1, 2)(3)(4, 5)
// 又或者
transformTest(1, 2, 3, 4)(5)

curry函数应该怎么写?

function curry(fn, ...args) {
  // 判断参数个数是不是等于原函数参数个数
  // 如果是,直接返回调用结果
  if ([...args].length >= fn.length) {
    return fn(...args);
  } else {
    // 如果不是,则返回一个函数
    return (...params) => {
      // 将前面传的全部参数传给curry,回到第一步的if判断,直到参数个数满足要求
      return curry(fn, ...args, ...params);
    };
  }
}

本文GitHub链接:手写系列:call、apply、bind、函数柯里化