js基础训练,手写实现

136 阅读4分钟

控制并发请求的函数

/**
 *
 * @param {string[]} urls url数组
 * @param  {number} max 最大并发数
 */
function concurrentRequests(urls: string[], max = 6) { //chrome最大并发请求6个
  return new Promise((resolve) => {
    if (urls.length === 0) {
      resolve([]);
      return;
    }
    const urlLen = urls.length;
    const results = [];
    // 下一个请求
    let next = 0;
    // 请求完成数量
    let finished = 0;
    
    async function _request() {
      // 超过urls个数,就停止
      if (next === urlLen) {
        return;
      }
      const i = next;
      const url = urls[i];
      next++;

      try {
        const res = await fetch(url);
        results[i] = res;
      } catch (err) {
        results[i] = err;
      } finally {
        finished++;
        // 所有请求完成就执行resolve()
        if (finished === urlLen) {
          resolve(results);
        }
        //成功或失败都调用下一个请求
        //不去等待响应结果可以看做同时发
        _request();
      }
    }

    //最大并发数如果大于url个数,取最小数
    const times = Math.min(max, urlLen);
    for (let i = 0; i < times; i++) {
      _request();
    }
  });
}

深拷贝

function isObject(value) {
  const valueType = typeof value
  return (value !== null) && (valueType === "object" || valueType === "function")
}

function deepClone(originValue, map = new WeakMap()) {
  // Set类型
  if (originValue instanceof Set) {
    return new Set([...originValue])
  }

  // Map类型
  if (originValue instanceof Map) {
    return new Map([...originValue])
  }

  // Symbol
  if (typeof originValue === "symbol") {
    return Symbol(originValue.description)
  }

  // 函数
  if (typeof originValue === "function") {
    return originValue
  }

  // 不是对象
  if (!isObject(originValue)) {
    return originValue
  }
  
  //防止循环应用
  if (map.has(originValue)) {
    return map.get(originValue)
  }

  // 判断传入的对象是数组, 还是对象
  const newObject = Array.isArray(originValue) ? []: {}
  map.set(originValue, newObject)
  
  for (const key in originValue) {
    newObject[key] = deepClone(originValue[key], map)
  }

  // 对Symbol的key进行特殊的处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue)
  for (const sKey of symbolKeys) {
    newObject[sKey] = deepClone(originValue[sKey], map)
  }
  
  return newObject
}

防抖

function debounce(fn, delay) {
  let timer = null;

  const _debounce = function () {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn();
    }, delay);
  };

  return _debounce;
}

加上this和参数

function debounce(fn, delay) {
 let timer = null

 const _debounce = function(...args) {
   if (timer) clearTimeout(timer)
   timer = setTimeout(() => {
     // 外部传入的真正要执行的函数
     // this绑定为_debounce的this
     fn.apply(this, args) 
   }, delay)
 }

 return _debounce
}

加上立即执行

function debounce(fn, delay, immediate = false) {
  let timer = null
  let isInvoke = false

  const _debounce = function(...args) {
    if (timer) clearTimeout(timer)

    // 判断是否需要立即执行
    if (immediate && !isInvoke) {
      fn.apply(this, args)
      isInvoke = true
    } else {
      // 延迟执行
      timer = setTimeout(() => {
        // 外部传入的真正要执行的函数
        fn.apply(this, args)
        isInvoke = false
      }, delay)
    }
  }

  return _debounce
}

加上取消功能

function debounce(fn, delay, immediate = false) {
  let timer = null
  let isInvoke = false

  const _debounce = function(...args) {
    if (timer) clearTimeout(timer)

    if (immediate && !isInvoke) {
      fn.apply(this, args)
      isInvoke = true
    } else {
      timer = setTimeout(() => {
        fn.apply(this, args)
        isInvoke = false
        timer = null
      }, delay)
    }
  }

  // 封装取消功能
  _debounce.cancel = function() {
    if (timer) clearTimeout(timer)
    timer = null
    isInvoke = false
  }

  return _debounce
}

获取返回值,两种方式获取返回值,promise或者回调函数

function debounce(fn, delay, immediate = false, resultCallback) {
  let timer = null
  let isInvoke = false

  const _debounce = function(...args) {
    return new Promise((resolve, reject) => {
      if (timer) clearTimeout(timer)

      if (immediate && !isInvoke) {
        const result = fn.apply(this, args)
        if (resultCallback) resultCallback(result)
        resolve(result)
        isInvoke = true
      } else {
        timer = setTimeout(() => {
          const result = fn.apply(this, args)
          if (resultCallback) resultCallback(result)
          resolve(result)
          isInvoke = false
          timer = null
        }, delay)
      }
    })
  }

  _debounce.cancel = function() {
    if (timer) clearTimeout(timer)
    timer = null
    isInvoke = false
  }

  return _debounce
}

节流

function throttle(fn, interval) {
  let lastTime = Date.now()

  const _throttle = function () {
    const nowTime = Date.now()
    if (nowTime - lastTime >= interval) {
      lastTime = Date.now()
      return fn.apply(this, [...arguments])
    }
  }

  return _throttle
}

instanceof

function myInstanceof(left, right) {
  if (typeof left !== 'object' || typeof left !== 'function' || left === null) {
    return false
  }
  let proto = Object.getPrototypeOf(left)
  while (true) {
    if (proto === null) return false
    if (proto === right.prototype) return true
    proto = Object.getPrototypeOf(proto)
  }
}

typeof

function myTypeof(obj) {
  const type = typeof obj
  if (type !== 'object') {
    return type
  }
  return Object.prototype.toString.call(obj).replace(/^[object (\S+)]$/,'$1')

call

Function.prototype.myCall = function (ctx, ...args) {
  // ctx保证是对象
  ctx = ctx === undefined || ctx === null ? globalThis : Object(ctx);
  const fn = this;
  // 避免覆盖
  const key = Symbol("fn");
  // 不能枚举
  Object.defineProperty(ctx, key, {
    value: fn,
    enumerable: false,
  });
  const res = ctx[key](...args);
  delete ctx[key];
  return res;
};

apply

Function.prototype.myApply = function (context, args) {
  if (typeof this !== "function") {
    console.error("type error");
  }

  if (args && !(args instanceof Array)) {
    console.error("type error");
    return;
  }

  context = context ?? window;
  context = Object(context);

  const fn = Symbol();
  context[fn] = this;

  let res;
  if (args) {
    res = context[fn](...args);
  } else {
    res = context[fn]();
  }

  delete context[fn];
  return res;
};

bind

Function.prototype.myBind = function (context, ...args) {
  if (typeof this !== "function") {
    console.error("type eeeor");
  }
  const fn = this;
  return function Fn(...paras) {
    return fn.apply(this instanceof Fn ? this : context, args.concat(paras));
  };
};

new

function myNew(Func, ...args) {
  const obj = Object.create(Func.prototype)
  const result = Func.apply(obj, args)
  return result instanceof Object ? result : obj
}

Promise

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

// 处理错误
function execFunctionWithCatchError(execFn, value, resolve, reject) {
  try {
    const result = execFn(value)
    resolve(result)
  } catch (err) {
    reject(err)
  }
}

class MyPromise {
  constructor(executor) {
    this.status = PROMISE_STATUS_PENDING
    this.value = undefined
    this.reason = undefined

    this.onFulfilledFns = []
    this.onRejectedFns = []

    const resolve = value => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return //防止resolve 和 reject 一起执行
          this.value = value
          this.status = PROMISE_STATUS_FULFILLED
          this.onFulfilledFns.forEach(fn => fn(this.value))
        })
      }
    }

    const reject = reason => {
      if (this.status === PROMISE_STATUS_PENDING) {
        queueMicrotask(() => {
          if (this.status !== PROMISE_STATUS_PENDING) return
          this.reason = reason
          this.status = PROMISE_STATUS_REJECTED
          this.onRejectedFns.forEach(fn => fn(this.reason))
        })
      }
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }
}

实现then方法

 then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      // 1.如果在then调用的时候, 状态已经确定下来
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      }
      // 2.将成功回调和失败的回调放到数组中
      this.onFulfilledFns.push(() =>
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
      )
      this.onRejectedFns.push(() =>
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
      )
    })
  }

实现catch方法

  catch(onRejected) {
    this.then(undefined, onRejected)
  }

实现finally

  finally(onFinally) {
    this.then(() => {
      onFinally()
    }, () => {
      onFinally()
    })
  }

实现 resolve和reject

  static resolve(value) {
    return new MyPromise((resolve) => resolve(value))
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }

Promise.all

Promise.myAll = function (proms) {
  let res;
  let rej;
  const p = new Promise((resolve, reject) => {
    res = resolve;
    rej = reject;
  });

  let result = [];
  //promise数量
  let count = 0;
  // promise完成数量
  let finished = 0;

  for (const prom of proms) {
    //保证promise返回结果的顺序
    const i = count;
    count++;
    //保证传的数组里面是promise
    // Promise.resolve(prom)传入一个promise,那它返回的promise的状态由传入的这个promise决定
    Promise.resolve(prom).then((data) => {
      // 汇总数据
      result[i] = data;
      // 判断是否全部执行完
      finished++;
      if (finished === count) {
        res(result);
      }
    }, rej);
  }

  if (count === 0) {
    res(result);
  }

  return p;
};

柯里化

// es6
function curry(fn, ...args) {
  return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
}

数组map

Array.prototype._map = function (exec) {
  if (typeof exec !== "function") {
    throw TypeError("need function");
  }
  const newArr = [];
  for (let i = 0; i < this.length; i++) {
    newArr[i] = exec(this[i], i, this);
  }
  return newArr;
};

数组filter

Array.prototype._filter = function (exec) {
  if (typeof exec !== "function") {
    throw TypeError("need function");
  }

  const newArr = [];
  for (let i = 0; i < this.length; i++) {
    if (exec(this[i], i, this)) {
      newArr.push(this[i]);
    }
  }
  return newArr;
};

数组reduce

Reduce 每一步都将当前元素的值与前一步的结果相加(该结果是之前所有步骤结果的总和)——直到没有更多需要相加的元素

Array.prototype._reduce = function (exec, initialVal) {
  if (typeof exec !== "function") {
    throw TypeError("need function");
  }

  for (let i = initialVal ? 0 : 1; i < this.length; i++) {
    initialVal = exec(initialVal ?? this[0], this[i], i, this);
  }

  return initialVal;
};