# 手写系列:call、apply、bind、函数柯里化、防抖、节流、new、Promise

少废话,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);
    };
  }
}

# 防抖

防抖函数最常见的应用场景是搜索框,用户的输入一般是连续的,为了减轻服务器压力,没有必要响应每个字符的搜索结果,如果用户连续输入一段关键字后等待一定时间,没有再次输入,就可以发起搜索请求,这样就少了很多请求。

function debounce(fn, defer) {
  let timer;
  return () => {
    // timer有值说明在defer时间内,多次触发了该函数,清空定时器,重新计时
    timer && clearTimeout(timer);
    timer = setTimeout(() => {
      fn();
      timer = null;
    }, defer);
  }
}

# 节流

节流,让回调函数在一定时间内只执行一次,适用于比较耗费性能的操作,如DOM操作。

function throttle(fn, defer) {
  let time = new Date();
  return () => {
    // 两次时间差大于defer,就执行一次
    if(new Date() - time > defer){
      fn();
      time = new Date();
    }
  }
}

# new

new操作符可以实例化一个对象,new一个对象的时候,会调用对象的构造函数,实例化出来的结果,它的__proto__指向构造函数的prototype。如果构造函数有返回值,直接返回这个值。

// 因无法实现new那样的操作符,用函数代替
function myNew(Constructor, ...arguments) {
  const obj = new Object();
  const res = Constructor.call(obj, ...arguments);
  obj.__proto__ = Constructor.prototype;
  return typeof res === 'object' ? res : obj;
}

# Promise

这个需要另起一篇:Promise是如何实现异步编程的? (opens new window)

上次更新: 10/23/2021, 10:33:11 PM