算法-手写

2 阅读5分钟
1.手写实现new
function myNew(constructor, ...args) {
    // 步骤1: 创建一个空对象,并且这个对象继承自构造函数的prototype属性。
    const obj = Object.create(constructor.prototype);
    

    // 步骤2: 将构造函数的作用域赋给新对象(因此this就指向了这个新对象)。
    const result = constructor.apply(obj, args);
    
    // 步骤3: 如果构造函数返回了一个对象,则返回这个对象;否则,返回刚创建的新对象。
    return result instanceof Object ? result : obj;

}
2.手写 instanceof 方法
function myInstanceof(left, right) {
    // 获取对象的原型
    let proto = Object.getPrototypeOf(left);
    // 获取构造函数的 prototype 对象
    const prototype = right.prototype;

    // 遍历原型链
    while (proto) {
        // 检查构造函数的 prototype 是否出现在实例对象的原型链上
        if (proto === prototype) {
            return true;
        }
        // 沿原型链向上移动
        proto = Object.getPrototypeOf(proto);
    }

    // 如果没有找到,返回 false
    return false;
}
3.手写promise
class MyPromise {
    constructor(executor) {
        this.state = 'pending'; // Promise的初始状态
        this.value = undefined; // 成功时的值
        this.reason = undefined; // 失败时的原因

        // 成功
        const resolve = (value) => {
            if (this.state === 'pending') {
                this.state = 'fulfilled';
                this.value = value;
                this.onFulfilledCallbacks.forEach(fn => fn());
            }
        };

        // 失败
        const reject = (reason) => {
            if (this.state === 'pending') {
                this.state = 'rejected';
                this.reason = reason;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        };

        try {
            executor(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    // 存储成功和失败的回调
    onFulfilledCallbacks = [];
    onRejectedCallbacks = [];

    then(onFulfilled, onRejected) {
        // 在then中根据状态执行相应操作
        if (this.state === 'fulfilled') {
            onFulfilled(this.value);
        }
        if (this.state === 'rejected') {
            onRejected(this.reason);
        }

        // 处理异步
        if (this.state === 'pending') {
            this.onFulfilledCallbacks.push(() => {
                onFulfilled(this.value);
            });
            this.onRejectedCallbacks.push(() => {
                onRejected(this.reason);
            });
        }
    }
}

4. 手写 Promise.all
function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('arguments must be an array'));
    }
    let resolvedCounter = 0;
    let promiseNum = promises.length;
    let resolvedValues = new Array(promiseNum);
    for (let i = 0; i < promiseNum; i++) {
      // 直接调用 Promise.resolve 确保每个元素都被当作 Promise 对待
      Promise.resolve(promises[i]).then(value => {
        resolvedCounter++;
        resolvedValues[i] = value;
        // 当所有元素都被解决后,Promise.all 返回的 promise 状态变为 resolved
        if (resolvedCounter === promiseNum) {
          return resolve(resolvedValues);
        }
      }, reason => {
        // 任何一个元素被 reject 时,Promise.all 返回的 promise 状态变为 rejected
        return reject(reason);
      });
    }
    // 如果输入的是空数组,直接解决
    if (promiseNum === 0) {
      resolve(resolvedValues);
    }
  });
}

5.手写 Promise.race
function promiseRace(promises) {
  return new Promise((resolve, reject) => {
    // 检查输入是否为数组
    if (!Array.isArray(promises)) {
      return reject(new TypeError('arguments must be an array'));
    }
    // 遍历所有的 Promise 对象
    for (let i = 0; i < promises.length; i++) {
      // 对每个 Promise 使用 Promise.resolve() 包装以确保它们是 Promise 对象
      // 然后使用 .then 方法订阅其解决或拒绝状态
      Promise.resolve(promises[i]).then(resolve, reject);
    }
  });
}

6.手写防抖函数
function debounce(fn, delay) {
  let timeoutID = null;

  return function(...args) {
    // 如果此前已经设置了延时调用,则取消之前的调用
    if (timeoutID) {
      clearTimeout(timeoutID);
    }

    // 设置一个新的延时调用
    timeoutID = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

7.手写节流函数
function throttle(fn, interval) {
  let lastTime = 0;

  return function(...args) {
    const now = new Date().getTime();

    if (now - lastTime > interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

8.手写类型判断函数
function getType(value) {
    // 首先排除 null 的情况
    if (value === null) {
        return "Null";
    }
    // 排除非对象类型的情况
    else if (typeof value !== "object") {
        return typeof value;
    }
    // 对于 Array, Function, RegExp 等,使用 Object.prototype.toString
    else {
        return Object.prototype.toString.call(value).slice(8, -1);
    }
}

9.手写 call 函数
Function.prototype.myCall = function(context = window, ...args) {
    if (typeof this !== 'function') {
        throw new TypeError('Type error');
    }
    // 避免与现有属性冲突,使用 Symbol
    const fnSymbol = Symbol();
    // this 指向调用 call 的函数
    context[fnSymbol] = this;
    // 执行函数
    const result = context[fnSymbol](...args);
    // 删除属性
    delete context[fnSymbol];
    // 返回执行结果
    return result;
}

10.手写 apply 函数
Function.prototype.myApply = function(context = window, args = []) {
    if (this === Function.prototype) {
        return undefined; // 防止 Function.prototype.myApply() 直接调用
    }
    context = context || window;
    const fnSymbol = Symbol(); // 使用 Symbol 确保唯一性
    context[fnSymbol] = this; // 将调用函数设为对象的方法
    let result;
    // 判断 args 是否为有效数组
    if (Array.isArray(args)) {
        result = context[fnSymbol](...args); // 执行函数
    } else {
        throw new TypeError('CreateListFromArrayLike called on non-object');
    }
    delete context[fnSymbol]; // 删除刚才赋值的属性
    return result; // 返回执行结果
};

11.手写 bind 函数
Function.prototype.myBind = function(context, ...args) {
    if (this === Function.prototype) {
        throw new TypeError('Error');
    }
    
    const _this = this; // 保存当前函数的引用
    
    return function F(...bindArgs) {
        // 处理函数使用 new 操作符调用的情况
        if (this instanceof F) {
            return new _this(...args, ...bindArgs);
        }
      // ***** context
        return _this.apply(context, [...args, ...bindArgs]);
    };
};

12.函数柯里化的实现

重点返回两层函数

一层记录

一层判断执行

function curry(fn) {
    // 检查需要的参数数量   .length
    const arity = fn.length;

    // `nextCurried` 函数负责收集参数,直到参数数量足够,然后执行 `fn`
    function nextCurried(prevArgs) {
      
        return function(arg) {
            // 合并之前收集的参数和当前参数
            const args = [...prevArgs, arg];

            // 如果参数数量足够,则执行 `fn`
            if (args.length >= arity) {
                return fn(...args);
            }
            // 否则,返回一个新的柯里化函数,继续收集参数
            else {
                return nextCurried(args);
            }
        };
    }

    // 开始柯里化过程,初始参数为空
    return nextCurried([]);
}

13.实现深拷贝
function deepCopy(obj, hash = new WeakMap()) {
    // 处理 null、undefined 和基本数据类型
    if (obj === null || typeof obj !== 'object') return obj;

    // 处理日期类型
    if (obj instanceof Date) return new Date(obj);

    // 处理 Map 类型
    if (obj instanceof Map) return new Map([...obj]);

    // 避免循环引用
    if (hash.has(obj)) return hash.get(obj);

    // 处理数组和对象,使用 Array.isArray() 检查是否为数组
    const result = Array.isArray(obj) ? [] : {};

    // 保存拷贝的对象,用于处理循环引用
    hash.set(obj, result);

    // 递归拷贝所有属性
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            result[key] = deepCopy(obj[key], hash);
        }
    }

    // 返回拷贝后的对象
    return result;
}

15.实现数组的扁平化
let arr = [1, [2, [3, 4, 5]]];
function flatten(arr) {
  let result = [];

  for(let i = 0; i < arr.length; i++) {
    if(Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}
flatten(arr);  //  [1, 2, 3, 4,5]
16.发布订阅模式
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");
17.数组去重
function uniqueArr(arr) {
  return [...new Set(arr)];
}
18.数组扁平化
function flatter(arr) {
  if (!arr.length) return;
  return arr.reduce(
    (pre, cur) =>
      Array.isArray(cur) ? [...pre, ...flatter(cur)] : [...pre, cur],
    []
  );
}

function flatter(arr) {
  if (!arr.length) return;
  while (arr.some((item) => Array.isArray(item))) {
    arr = [].concat(...arr);
  }
  return arr;
}
19.寄生组合继承
function Parent(name) {
  this.name = name;
  this.say = () => {
    console.log(111);
  };
}
Parent.prototype.play = () => {
  console.log(222);
};
function Children(name) {
  Parent.call(this);
  this.name = name;
}
Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();
20.实现有并行限制的 Promise 调度器
class Scheduler {
  constructor(limit) {
    this.queue = [];
    this.maxCount = limit;
    this.runCounts = 0;
  }
  add(time, order) {
    const promiseCreator = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(order);
          resolve();
        }, time);
      });
    };
    this.queue.push(promiseCreator);
  }
  taskStart() {
    for (let i = 0; i < this.maxCount; i++) {
      this.request();
    }
  }
  request() {
    if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) {
      return;
    }
    this.runCounts++;
    this.queue
      .shift()()
      .then(() => {
        this.runCounts--;
        this.request();
      });
  }
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
  scheduler.add(time, order);
};
addTask(1000, "1");
addTask(500, "2");
addTask(300, "3");
addTask(400, "4");
scheduler.taskStart();
19.实现 LazyMan