返回

手写JavaScript进阶方法详解:new、call、apply、bind、reduce、currying、防抖节流源码分析

前端

在 JavaScript 中,有很多内置的方法可以帮助我们轻松处理复杂的任务。这些方法通常都经过优化,因此能够高效地执行。但是,如果您想更深入地了解 JavaScript 的工作原理,那么手写这些方法是一个不错的选择。

1. new

function New(constructor, ...args) {
  // 创建一个新的对象
  const obj = {};
  // 将构造函数的原型赋给新对象的原型
  obj.__proto__ = constructor.prototype;
  // 调用构造函数,并把新对象作为this
  const result = constructor.call(obj, ...args);
  // 返回新对象
  return result === undefined ? obj : result;
}

2. call

Function.prototype.call = function (context, ...args) {
  // 将函数赋给context对象的一个属性
  context.fn = this;
  // 调用该属性,并将参数列表传递给它
  const result = context.fn(...args);
  // 删除该属性
  delete context.fn;
  // 返回结果
  return result;
};

3. apply

Function.prototype.apply = function (context, args) {
  // 将函数赋给context对象的一个属性
  context.fn = this;
  // 调用该属性,并将参数列表传递给它
  const result = context.fn(...args);
  // 删除该属性
  delete context.fn;
  // 返回结果
  return result;
};

4. bind

Function.prototype.bind = function (context, ...args) {
  // 返回一个新的函数
  return (...bindArgs) => {
    // 将参数列表合并
    const allArgs = [...args, ...bindArgs];
    // 调用该函数,并将参数列表传递给它
    const result = this.apply(context, allArgs);
    // 返回结果
    return result;
  };
};

5. reduce

Array.prototype.reduce = function (callback, initialValue) {
  // 初始化accumulator和currentIndex
  let accumulator = initialValue;
  let currentIndex = 0;

  // 如果没有初始值,则将accumulator设置为数组的第一个元素,并从第二个元素开始遍历
  if (accumulator === undefined) {
    accumulator = this[0];
    currentIndex = 1;
  }

  // 遍历数组
  for (; currentIndex < this.length; currentIndex++) {
    // 调用回调函数,并更新accumulator
    accumulator = callback(accumulator, this[currentIndex], currentIndex, this);
  }

  // 返回accumulator
  return accumulator;
};

6. currying

function curry(fn) {
  // 获取函数的参数列表
  const args = Array.prototype.slice.call(arguments, 1);

  // 返回一个新的函数
  return (...newArgs) => {
    // 将参数列表合并
    const allArgs = [...args, ...newArgs];

    // 调用原函数,并将参数列表传递给它
    const result = fn.apply(this, allArgs);

    // 返回结果
    return result;
  };
}

7. 防抖节流

// 防抖
function debounce(fn, delay) {
  // 创建一个定时器
  let timer;

  // 返回一个新的函数
  return (...args) => {
    // 清除定时器
    clearTimeout(timer);

    // 创建一个新的定时器
    timer = setTimeout(() => {
      // 调用原函数,并将参数列表传递给它
      fn.apply(this, args);
    }, delay);
  };
}

// 节流
function throttle(fn, delay) {
  // 创建一个变量来存储上次调用的时间
  let lastTime = 0;

  // 返回一个新的函数
  return (...args) => {
    // 获取当前时间
    const now = Date.now();

    // 如果当前时间与上次调用时间之差大于等于延迟时间
    if (now - lastTime >= delay) {
      // 调用原函数,并将参数列表传递给它
      fn.apply(this, args);

      // 更新上次调用的时间
      lastTime = now;
    }
  };
}

以上是一些 JavaScript 进阶方法的手写源码分析。如果您想更深入地了解这些方法的原理和应用场景,可以参考这些源码。