LOADING

手写题

2024/9/11

call、apply、bind 手写

实现 call 方法:

Function.prototype.call2 = function (context, ...args) {
  //没传参数或者为 null 是默认是 window
  var context = context || (typeof window !== "undefined" ? window : global);
  // 首先要获取调用 call 的函数,用 this 可以获取
  context.fn = this;
  const result = eval("context.fn(...args)");
  delete context.fn;
  return result;
};

// 测试
var value = 3;
var foo = {
  value: 2,
};

function bar(name, age) {
  console.log(this.value);
  console.log(name);
  console.log(age);
}
bar.call2(null);
// 浏览器环境: 3 undefinde undefinde
// Node环境:undefinde undefinde undefinde

bar.call2(foo, "cc", 18); // 2  cc 18

实现 apply 方法:

Function.prototype.apply2 = function (context, arr) {
  var context = context || (typeof window !== "undefined" ? window : global);
  context.fn = this;

  var result;
  if (!arr) {
    result = context.fn();
  } else {
    result = eval("context.fn(...arr)");
  }
  delete context.fn;
  return result;
};

// 测试:

var value = 3;
var foo = {
  value: 2,
};

function bar(name, age) {
  console.log(this.value);
  console.log(name);
  console.log(age);
}
bar.apply2(null);
// 浏览器环境: 3 undefinde undefinde
// Node环境:undefinde undefinde undefinde

bar.apply2(foo, ["cc", 18]); // 2  cc 18

实现 bind 方法:

Function.prototype.bind2 = function (ctx, ...args) {
  const fn = this;
  return function (...restArgs) {
    return fn.apply(ctx, [...args, ...restArgs]);
  };
};

// 测试
var test = {
  name: "jack",
};
var demo = {
  name: "rose",
  getName: function () {
    return this.name;
  },
};

console.log(demo.getName()); // 输出 rose  这里的 this 指向 demo

// 运用 bind 方法更改 this 指向
var another2 = demo.getName.bind2(test);
console.log(another2()); // 输出 jack  这里 this 指向了 test 对象了

new 手写

// 构造器函数
let Parent = function (name, age) {
  this.name = name;
  this.age = age;
};
Parent.prototype.sayName = function () {
  console.log(this.name);
};
//自己定义的new方法
let newMethod = function (Parent, ...rest) {
  // 1.以构造器的prototype属性为原型,创建新对象;
  let child = Object.create(Parent.prototype);
  // 2.将this和调用参数传给构造器执行
  let result = Parent.apply(child, rest);
  // 3.如果构造器没有手动返回对象,则返回第一步的对象
  return typeof result === "object" ? result : child;
};
//创建实例,将构造函数Parent与形参作为参数传入
const child = newMethod(Parent, "echo", 26);
child.sayName(); //'echo';
//最后检验,与使用new的效果相同
console.log(child instanceof Parent); //true
console.log(child.hasOwnProperty("name")); //true
console.log(child.hasOwnProperty("age")); //true
console.log(child.hasOwnProperty("sayName")); //false

根据 ES6 构造函数的书写方式,写出 ES5 的

ES6 的 class 和普通构造函数的区别有以下几点:

  1. ES6 中的 class 必须通过 new 来调用,不能当做普通函数调用,否则报错因此,在答案中,加入了 new.target 来判断调用方式
  2. ES6 的 class 中的所有代码均处于严格模式之下因此,在答案中,无论是构造函数本身,还是原型方法,都使用了严格模式
  3. ES6 中的原型方法是不可被枚举的因此,在答案中,定义原型方法使用了属性描述符,让其不可枚举
class Example {
  constructor(name) {
    this.name = name;
  }
  init() {
    const fun = () => { console.log(this.name) }
    fun();
  }
}
const e = new Example('Hello');
e.init();
function Example(name) {
  "use strict";
  if (!new.target) {
    throw new TypeError("Class constructor cannot be invoked without new");
  }
  this.name = name;
}

Object.defineProperty(Example.prototype, "init", {
  enumerable: false,
  value: function () {
    "use strict";
    if (new.target) {
      throw new TypeError("init is not a constructor");
    }
    var fun = function () {
      console.log(this.name);
    };
    fun.call(this);
  },
});

防抖,节流手写

/**
 * 函数防抖
 * @param {function} fn 一段时间后,要调用的函数
 * @param {number} wait 等待的时间,单位毫秒
 */
function debounce(fn, wait) {
  // 设置变量,记录 setTimeout 得到的 id
  let timerId = null;
  return function (...args) {
    if (timerId) {
      // 如果有值,说明目前正在等待中,清除它
      clearTimeout(timerId);
    }
    // 重新开始计时
    timerId = setTimeout(() => {
      fn.call(this, ...args);
    }, wait);
  };
}

//节流
function throttle(fn, wait) {
  let previous = 0;
  return function (...args) {
    let now = +new Date();
    if (now - previous > wait) {
      fn.apply(this, ...args);
      previous = now;
    }
  };
}

async 手写

原始

function A() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(3);
    }, 1000);
  });
}

async function B() {
  const b = await A();
  const c = await A();
  return b + c;
}

B().then((data) => console.log(data));

转换结果

//不变
function A() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(3);
    }, 1000);
  });
}

//转换
function B() {
  var fn = function* () {
    const b = yield A();
    const c = yield A();
    return b + c;
  };
  return new Promise(function (resolve, reject) {
    var gen = fn();
    function _next(value) {
      asyncGeneratorStep(gen, resolve, reject, _next, value);
    }
    _next(undefined);
  });
}

//转换
function asyncGeneratorStep(gen, resolve, reject, _next, arg) {
  try {
    var info = gen.next(arg);
    var value = info.value;
  } catch (error) {
    reject(error);
    return;
  }

  if (info.done) {
    resolve(value);
  } else {
    Promise.resolve(value).then((data) => {
      _next(data);
    });
  }
}

//不变
B().then(function (data) {
  return console.log(data);
});

深拷贝手写

export function deepClone(obj) {
  let newObj = {};
  if (Array.isArray(obj)) {
    newObj = [];
  }
  for (let k in obj) {
    let value = obj[k];
    if (typeof value === "object" && value !== null) {
      newObj[k] = deepClone(value);
    } else {
      //其他值 包括function的情况 都直接赋值
      newObj[k] = value;
    }
  }
  return newObj;
}

flat 手写

let arr = [
  1,
  [2, 3, [4, 5, [12, 3, "zs"], 7, [8, 9, [10, 11, [1, 2, [3, 4]]]]]],
];

//自定义flat方法,注意:不可以使用箭头函数,使用后内部的this会指向window
Array.prototype.myFlat = function (num) {
  //遍历所有子元素并判断类型,若为数组则继续递归,若不为数组则直接加入新数组
  const result = [];
  this.forEach((item) => {
    if (Array.isArray(item)) {
      //形参num,表示当前需要拆分多少层数组,传入Infinity则将多维直接降为一维

      num--;
      if (num < 0) {
        let newArr = result.push(item);
        return newArr;
      }
      //使用三点运算符解构,递归函数返回的数组,并加入新数组
      result.push(...item.myFlat(num));
    } else {
      result.push(item);
    }
  });
  return result;
};
console.log(arr.flat(Infinity)); //[1, 2, 3, 4, 5, 12, 3, "zs", 7, 8, 9, 10, 11, 1, 2, 3, 4];
function flat(arr, depth = 1) {
  let result = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i]) && depth > 0) {
      result = result.concat(flat(arr[i], depth - 1));
    } else {
      result.push(arr[i]);
    }
  }
  return result;
}

reduce 手写

Array.prototype.myReduce = function (callback, initialValue) {
  if (!Array.isArray(this) || !this.length || typeof callback !== "function") {
    return [];
  } else {
    let value = initialValue ? initialValue : this[0];
    for (let index = initialValue ? 0 : 1; index < this.length; index++) {
      const element = this[index];
      value = callback(value, element, index, this);
    }
    return value;
  }
};

let arr = [1, 2, 3, 4, 5];
let res = arr.myReduce((pre, cur, i, arr) => {
  console.log(pre, cur, i, arr);
  return pre + cur;
}, 10);
console.log(res); //25

promise 手写

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

/**
 * 运行一个微队列任务
 * 把传递的函数放到微队列中
 * @param {Function} callback
 */
function runMicroTask(callback) {
  // 判断node环境
  // 为了避免「变量未定义」的错误,这里最好加上前缀globalThis
  // globalThis是一个关键字,指代全局对象,浏览器环境为window,node环境为global
  if (globalThis.process && globalThis.process.nextTick) {
    process.nextTick(callback);
  } else if (globalThis.MutationObserver) {
    const p = document.createElement("p");
    const observer = new MutationObserver(callback);
    observer.observe(p, {
      childList: true, // 观察该元素内部的变化
    });
    p.innerHTML = "1";
  } else {
    setTimeout(callback, 0);
  }
}

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

class MyPromise {
  /**
   * 创建一个Promise
   * @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);
      console.error(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]) {
      const handler = this._handlers[0];
      this._runOneHandler(handler);
      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);
        console.error(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;
    }
    // 下面这个判断是为了处理value为Promise的情况
    // 这一段代码课程中没有涉及,特此注释说明
    if (isPromise(value)) {
      value.then(this._resolve.bind(this), this._reject.bind(this));
      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
   * 特殊情况:
   * 1. 传递的data本身就是ES6的Promise对象
   * 2. 传递的data是PromiseLike(Promise A+),返回新的Promise,状态和其保持一致即可
   * @param {any} data
   */
  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的执行
   * proms是一个迭代器,包含多个Promise
   * 全部Promise成功,则返回的Promise成功,数据为所有Promise成功的数据,并且顺序是按照传入的顺序排列
   * 只要有一个Promise失败,则返回的Promise失败,原因是第一个失败的Promise的原因
   * @param {iterator} proms
   */
  static all(proms) {
    return new MyPromise((resolve, reject) => {
      try {
        const results = [];
        let count = 0; // Promise的总数
        let fulfilledCount = 0; // 已完成的数量
        for (const p of proms) {
          let i = count;
          count++;
          MyPromise.resolve(p).then((data) => {
            fulfilledCount++;
            results[i] = data;
            if (fulfilledCount === count) {
              // 当前是最后一个Promise完成了
              resolve(results);
            }
          }, reject);
        }
        if (count === 0) {
          resolve(results);
        }
      } catch (error) {
        reject(error);
        console.error(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: REJECTED,
            reason,
          })
        )
      );
    }
    return MyPromise.all(ps);
  }

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

instanceof 手写

instanceof (A,B) = {
    let L = A.__proto__;
    let R = B.prototype;
    if(L === R) {
        // A的内部属性 __proto__ 指向 B 的原型对象
        return true;
    }
    return false;
}