一些代码练习

35 阅读4分钟

深拷贝

// 方式一
fucntion deepClone(value) {
    const cache = new WeakMap(); // 解决 循环引用 问题
    fucntion _deepClone(value) {
        if(typeof value !== 'object' || value === null) {
            return value;
        }
        if(cache.has(value)) {
            return cache.get(value);
        }
        const result = Array.isArray(value) ? [] : {};
        Object.setPrototypeOf(result, Object.getPrototypeOf(value)); // 考虑原型链
        cache.set(value, result);
        for(let key in value) {
            if(value.hasOwnProperty(key)) { // 排除掉原型上的东西
                result[key] = _deepClone(value[key]);
            }
        }
        return result;
    }
    return _deepClone(value);
}

// 方式二
fucntion deepClone(value) {
    return new Promise(resolve => {
        const { port1, port2 } = new MessageChannel();
        port1.postMessage(value);
        port2.onmessage = (e) => {
            resolve(e.data);
        } 
    })
}

并发任务控制

class ControlTask() {
    constructor(parallelCount = 2) {
        this.parallelCount = parallelCount;
        this.tasks = [];
        this.runningNum = 0;
    }
    
    add(task) {
        return new Promise((resolve, reject) => {
            this.tasks.push({
                task,
                resolve,
                reject
            });
            this.run();
        })
    }
    
    run() {
        if(this.runningNum < this.parallelCount && this.tasks.length) {
            const { task, resolve, reject } = this.tasks.shift();
            this.runningNum++;
            Promise.resolev(task()).then(resolve, reject).finally(() => {
                this.runningNum--;
                this.run();
            });
        }
    }
}

function timeout(time) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, time);
  });
}

const superTask = new SuperTask();
function addTask(time, name) {
  superTask
    .add(() => timeout(time))
  	.then(() => {
    	console.log(`任务${name}完成`)
  	})
}
addTask(1000, 1);
addTask(1000, 2);
addTask(1000, 3);
addTask(1000, 4);
addTask(1000, 5);

高量级任务优化

function runTask(task, callback) {
    // 第一种方式
    // 用于在浏览器空闲时执行低优先级的任务,避免阻塞主线程,从而提升页面的性能和响应速度
    requestIdleCalback((idle) => {
        // 16.6毫秒一帧,当前渲染后一帧还有没有剩余时间
        if(idle.timeRemaining() > 0) {
            task();
            callback();
        }else {
            runTask(task, callback);
        }
    })
    
    // 第二种方式(兼容性更好)
    let start = Date.now();
    // 该回调函数会在浏览器下一次重绘之前执行(一帧执行一次)
    requestAnimationFrame(() => {
        if(Date.now - start < 16.6) {
          task();
          callback();
        }else {
          runTask(task, callback);
        }
    });
}

发布订阅模式

class EventEmitter {
  constructor() {
    this.events = {};
  }
  // 实现订阅
  on(type, callBack) {
    if (!this.events[type]) {
      this.events[type] = [callBack];
    } else {
      this.events[type].push(callBack);
    }
  }
  // 删除订阅
  off(type, callBack) {
    if (!this.events[type]) return;
    this.events[type] = this.events[type].filter((item) => {
      return item !== callBack;
    });
  }
  // 只执行一次订阅事件
  once(type, callBack) {
    function fn() {
      callBack();
      this.off(type, fn);
    }
    this.on(type, fn);
  }
  // 触发事件
  emit(type, ...rest) {
    this.events[type] &&
      this.events[type].forEach((fn) => fn.apply(this, rest));
  }
}
// 使用如下
// const event = new EventEmitter();
// const handle = (...rest) => {
//   console.log(rest);
// };
// event.on("click", handle);

// event.emit("click", 1, 2, 3, 4);

// event.off("click", handle);

// event.emit("click", 1, 2);

// event.once("dbClick", () => {
//   console.log(123456);
// });
// event.emit("dbClick");
// event.emit("dbClick");

flat方法递归实现

function flat(arr, depth = 1) {
  return arr.reduce((result, item) => {
    if (item === undefined || item === null) {
      return result; // 跳过空位
    }
    if (Array.isArray(item)) {
      return result.concat(flat(item, depth - 1));
    }
    return result.concat(item);
  }, []);
}

提取URL中的query参数

let url = 'xxxx';
const str = url.slice(url.indexOf('?'));
// 创建一个URLSearchParams实例
const urlSearchParams = new URLSearchParams(window.location.search); // 传入?xx=xx&xx=xxx
// 把键值对列表转换为一个对象
const params = Object.fromEntries(urlSearchParams.entries());

手写简易Promise

// 记录Promise的三种状态
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

/**
 * 运行一个微队列任务
 * 把传递的回调函数放到微队列中
 * @param {Function} callback
 */
function runMicroTask(callback) {
  if (process && process.nextTick) {
    // 判断node环境
    process.nextTick(callback);
  } else if (MutationObserver) {
    // 浏览器环境
    const observer = new MutationObserver(callback);
    const textNode = document.createTextNode("1");
    observer.observe(textNode, {
      childList: true, // 观察该元素内部的变化
    });
    textNode.data = "2";
  } else {
    setTimeout(callback, 0);
  }
}

/**
 * 判断一个数据是否是Promise对象(符合PromiseA+规范)
 * @param {any} obj
 * @returns
 */
function isPromise(obj) {
  return !!(obj && typeof obj === "object" && typeof obj.then === "function");
}

class MyPromise {
  /**
   *
   * @param {Function} executor 任务执行器,立即执行
   */
  constructor(executor) {
    this._state = PENDING; // 状态
    this._value = undefined; // 数据
    this._handlers = []; // 处理函数形成的队列
    try {
      executor(this._resolve.bind(this), this._reject.bind(this));
    } catch (error) {
      this._reject(error);
    }
  }

  /**
   * 向处理队列中添加一个函数
   * @param {Function} executor 添加的函数
   * @param {String} state 该函数什么状态下执行
   * @param {Function} resolve 让then函数返回的Promise成功
   * @param {Function} reject 让then函数返回的Promise失败
   */
  _pushHandler(executor, state, resolve, reject) {
    this._handlers.push({
      executor,
      state,
      resolve,
      reject,
    });
  }

  /**
   * 根据实际情况执行队列
   */
  _runHandlers() {
    if (this._state === PENDING) {
      // 目前任务仍在挂起
      return;
    }
    while (this._handlers[0]) {
      this._runOneHandler(this._handlers[0]);
      this._handlers.shift();
    }
  }

  /**
   * 处理一个handler任务
   * @param {Object} handler
   */
  _runOneHandler({ executor, state, resolve, reject }) {
    runMicroTask(() => {
      if (this._state !== state) {
        // 状态不一致,不处理
        return;
      }
      if (typeof executor !== "function") {
        // 传递的后续处理不是一个函数
        this._state === FULFILLED ? resolve(this._value) : reject(this._value);
        return;
      }
      try {
        const result = executor(this._value);
        if (isPromise(result)) {
          result.then(resolve, reject);
        } else {
          resolve(result);
        }
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * Promise A+规范的then
   * @param {Function} onFulfilled
   * @param {Function} onRejected
   */
  then(onFulfilled, onRejected) {
    return new MyPromise((resolve, reject) => {
      this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
      this._pushHandler(onRejected, REJECTED, resolve, reject);
      this._runHandlers(); // 执行队列
    });
  }

  /**
   * 仅处理失败的场景
   * @param {Function} onRejected
   */
  catch(onRejected) {
    return this.then(null, onRejected);
  }

  /**
   * 无论成功还是失败都会执行回调
   * @param {Function} onSettled
   */
  finally(onSettled) {
    return this.then(
      (data) => {
        onSettled();
        return data;
      },
      (reason) => {
        onSettled();
        throw reason;
      }
    );
  }

  /**
   * 更改任务状态
   * @param {String} newState 新状态
   * @param {any} value 相关数据
   */
  _changeState(newState, value) {
    if (this._state !== PENDING) {
      // 目前状态已经更改,无需变化
      return;
    }
    this._state = newState;
    this._value = value;
    this._runHandlers(); // 状态变化,执行队列
  }

  /**
   * 标记当前任务完成
   * @param {any} data 任务完成的相关数据
   */
  _resolve(data) {
    // 改变状态和数据
    this._changeState(FULFILLED, data);
  }

  /**
   * 标记当前任务失败
   * @param {any} reason 任务失败的原因
   */
  _reject(reason) {
    // 改变状态和数据
    this._changeState(REJECTED, reason);
  }

  /**
   * 返回一个已完成的Promise
   * @param {any} data
   * 特殊情况:
   * 1. 传递的data本身就是ES6的Promise对象
   * 2. 传递的data是PromiseLike(符合PromiseA+规范)
   */
  static resolve(data) {
    if (data instanceof MyPromise) {
      return data;
    }
    return new MyPromise((resolve, reject) => {
      if (isPromise(data)) {
        data.then(resolve, reject);
      } else {
        resolve(data);
      }
    });
  }

  /**
   * 返回一个被拒绝的Promise
   * @param {any} reason
   */
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }

  /**
   * 得到一个新的Promise
   * 该Promise的状态取决于proms的执行
   * @param {iterator} proms 是一个迭代器,包含多个Promise
   */
  static all(proms) {
    return new MyPromise((resolve, reject) => {
      try {
        const results = [];
        let count = 0; // Promise总数
        let fulfilledCount = 0; // Promise已完成的数量
        for (const promise of proms) {
          let i = count;
          count++;
          MyPromise.resolve(promise).then((data) => {
            fulfilledCount++;
            results[i] = data;
            if (fulfilledCount === count) {
              // 当前是最后一个promise完成了
              resolve(results);
            }
          }, reject);
        }
        if (count === 0) {
          resolve(results);
        }
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 等待所有Promise有结果后,返回Promise完成
   * @param {iterator} proms
   */
  static allSettled(proms) {
    const ps = [];
    for (const p of proms) {
      ps.push(
        MyPromise.resolve(p).then(
          (value) => ({
            status: FULFILLED,
            value,
          }),
          (reason) => ({
            status: FULFILLED,
            reason,
          })
        )
      );
    }
    return MyPromise.all(ps);
  }

  /**
   * 返回的Promise与第一个有结果的一致
   * @param {iterator} proms
   */
  static race(proms) {
    return MyPromise((resolve, reject) => {
      for (const p of proms) {
        MyPromise.resolve(p).then(resolve, reject);
      }
    });
  }
}

// MyPromise.prototype.catch = function() {}
// MyPromise.resolve = function() {}

const promise = new MyPromise((resolve, reject) => {
  resolve(123);
  // throw new Error(123)
});
console.log(promise);