常见面试手撕源码

136 阅读2分钟

Promise.all 与 Promise.race

Promise.race = function (promises) {
  let len = promises.length;
  return new Promise((resolve, reject) => {
    if (!len) {
      resolve([])
    }
    for (let i = 0; i < len; i++) {
      Promise.resolve(promises[i]).then((res) => {
        resolve(res);
        return;
      }).catch((e) => {
        reject(e);
        return;
      })
    }
  })
}


Promise.all = function (propmises) {
  let len = propmises.length;
  return new Promise((resolve, reject) => {
    if (!len) return resolve([]);
    let result = [];
    function handlleData (data, index) {
      result[index] = data;

      if (len - 1 === index && !result.include(false)) {
        resolve(result);
      }
    }

    for (let i = 0; i < len; i++) {
      Promise.resolve(promises[i]).then((res) => {
        handlleData(res, i);
      }).catch((e) => {
        reject(e);
        return;
      })
    }
  })
}

EventBus 事件订阅监听

EventBus 在vue 中经常能用到,当然React 也是,经常用的油四个API emit(触发事件)emit(触发事件) on(监听事件) off(移出事件)off(移出事件) once(只触发一次事件,触发完后则将事件删除)

// 首先定义EventBus 类
class EventBus {
  constructor() { }
  handleBus = {}

  $on (eventName, fn) {
    if (!this.handleBus.hasOwnProperty(eventName)) {
      this.handleBus[eventName] = [fn]
    } else {
      this.handleBus[eventName].push(fn)
    }
  }

  $emit (eventName, payload) {
    if (!this.handleBus.hasOwnProperty(eventName)) {
      throw Error('没有此方法')
    }

    const eventFn = this.handleBus[eventName];
    for (let item of eventFn) {
      item(payload);
    }
  }

  $off (eventName, fn) {
    if (!this.handleBus.hasOwnProperty(eventName)) {
      throw Error('没有此事件')
    }

    if (typeof fn !== 'function') {
      this.handleBus[eventName] = []
    } else {
      let index = this.handleBus[eventName].findIndex((item, index) => item === fn);
      if (index !== -1)
        this.handleBus[eventName].splice(index, 1);
    }
  }

  $once (eventName, fn) {
    const myfunc = () => {
      fn();
      this.$off(eventName, fn);
    }

    this.$emit(eventName, myfunc)
  }
}

快速排序

function quickSort (arr, l, r) {
  let len = arr.length;
  if (len <= 1) return arr;
  l = typeof l === 'number' ? l : 0;
  r = typeof r === 'number' ? r : len - 1;
  let priv;
  if (l < r) {
    priv = getPriv(arr, l, r);
    quickSort(arr, l, priv - 1);
    quickSort(arr, priv + 1, r);
  }
  return arr;
}

function getPriv (arr, l, r) {
  let stand = l;
  let index = l + 1;
  for (let i = index; i <= r; i++) {
    if (arr[stand] > arr[i]) {
      swap(arr, index, i);
      index++;
    }
  }
  swap(arr, stand, index - 1);
  return index - 1
}

function swap (arr, l, r) {
  let temp = arr[l];
  arr[l] = arr[r];
  arr[r] = temp;
}

console.log(quickSort([5, 4, 200, 2, 1]))

数组 reduce 方法简单实现

Array.prototype.reduce = function (fn) {
  // 获取累加值
  let accumulator;
  // 获取默认值
  let defaltValue = arguments.length > 1 ? arguments[1] : undefined;
  //数组下标
  let index = 0;
  //数组
  let arr = this;
  // 边界判断
  let len = arr.length;
  if (!len && !defaltValue) {
    throw Error('请输入一些数据')
  }

  if (typeof fn !== 'function') {
    throw Error('回调函数应该是函数')
  }
  // 默认值存在
  if (defaltValue) {
    accumulator = defaltValue;
  } else {
  //默认值不存在,将数组第一个数设置为默认值
    accumulator = arr[0];
    index++;
  }
  // 循环相加
  while (index < len) {
    accumulator = fn.apply(undefined, [accumulator, arr[index], index, arr]);
    index++;
  }
  return accumulator;
}

JS继承最佳实践 寄生式组合继承

原型链继承子代会共享一些引用数据, 构造函数继承会将函数重复创建, 组合继承会调用两次父类构造函数,并且会在子类实例与子类原型上都存在父类构造函数数据重复创建,寄生式构造函数相当于一个最佳版本,也是ES6 中extends 使用的方法

function Parent () {
  this.color = ['yellow'];
}

Parent.prototype.sayName = function () {
  console.log('Parent');
}

function Child () {
  Parent.apply(this);
}
function F () {}
F.prototype = Parent.prototype

Child.prototype = new F();

const child = new Child ();

js 使用promise 进行并发控制

比如现在需要发30个请求,但是我需要控制每次最多发五个请求,当一个请求结束之后就会空出一个位置出来,这时候就可以发下一个请求。

function multiRequest (urls = [], maxNum) {
  let len = urls.length;
  const result = new Array(len).fill(false);

  let count = 0;
  return new Promise((resolve, reject) => {
    while (count < len) {
      next()
    }
    function next () {
      let current = count++;
      if (current >= len) {
        !result.includes(false) && resolve(result)
        return;
      }
      const url = urls[current];
      fetch(url)
        .then((res) => {
          result[current] = res;
        }).catch((e) => {
          result[current] = res;
        })
        .finally(() => {
          if (current < len) {
            next()
          }
        })
    }
  })
}

函数柯里化

函数柯里化应该是对确定的函数的参数来进行分割,就像分割成多个函数调用一样,可以进行参数复用,本质上还是降低通用性,提高适用性。

function curry (func) {
  let length = func.length;
  return function curried (...args) {
    if (args.length >= length) {
      return func.apply(this, args);
    } else {
      return function (...args2) {
        return func.apply(this, args.concat(args2));
      }
    }
  }
}

function sum (a,b) {
  return a + b;
}