编程题

h7ml2022年3月20日
大约 6 分钟

new 运算符

new 运算符原理

  1. 创建一个全新的对象
  2. 为新创建的对象添加 __proto__ 属模拟实现 new 运算符性并指向构造函数的原型对象
  3. 将新创建的对象作为函数调用的 this
  4. 如果构造函数没有返回对象类型,则返回新创建的对象
模拟实现 new 运算符
function myNew() {
  // 获取构造函数
  const Constructor = [].shift.call(arguments);

  // 创建空对象并设置原型
  const obj = Object.create(Constructor.prototype);

  // 绑定 this 并执行构造函数
  const result = Constructor.apply(obj, arguments);

  // 返回构造函数显示返回的值或新对象
  const type = typeof result;
  return result && (type === "object" || type === "function") ? result : obj;
}

instanceof 运算符

instanceof 运算符原理

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

模拟实现 instanceof 运算符
function myInstanceof(left, right) {
  // 取右边构造函数的 prototype 值
  const prototype = right.prototype;
  // 取左边实例的 __proto__ 值
  left = left.__proto__;

  while (true) {
    // 当左边实例的 __proto__ 为 null 时返回 false
    if (left === null) {
      return false;
    }
    // 判断左右两边的原型是否一致
    if (left === prototype) {
      return true;
    }
    // 修改 __proto__
    left = left.__proto__;
  }
}

Object.create()

Object.create()

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的 __proto__

模拟实现 Object.create()
function create(proto, properties) {
  // 如果 proto 不是 null 或非原始包装对象,抛出 TypeError 异常
  const type = typeof proto;
  if (type !== "object" && type !== "function") {
    throw new TypeError(
      "Object prototype may only be an Object or null: " + proto
    );
  }

  function F() {}
  // 将 proto 的原型设置为 F 的原型
  F.prototype = proto;
  // 创建新对象
  const result = new F();

  // 兼容 null 的处理
  if (proto === null) {
    result.__proto__ = null;
    // OR Reflect.setPrototypeOf(result, null)
  }

  // 将 properties 的属性设置到新对象上
  if (properties !== null && properties !== undefined) {
    Object.defineProperties(result, properties);
  }

  return result;
}

Object.create() —— MDNopen in new window

Function.prototype.call()

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数

Function.prototype.apply()

apply() 方法调用一个具有给定 this 值的函数,以及以一个数组(或类数组对象)的形式提供的参数

Function.prototype.bind()

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数供调用时使用。

工具方法系列

debounce 函数防抖

函数防抖

作用: 一个函数在一段时间内多次触发都只执行最后一次
原理: 利用定时器,在函数第一次执行时设定一个定时器,再次调用时如果已经设定过定时器就清空之前的定时器并设定一个新的定时器,当定时器结束后执行传入的回调函数
应用: 搜索输入框获取用户输入的联想结果

实现防抖函数
function debounce(fn, wait) {
  // 通过闭包缓存定时器 id
  let timer = null;
  return function (...args) {
    // 如果定时器已经存在,清除定时器
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    // 设定定时器,定时器结束后执行传入的回调函数 fn
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, wait);
  };
}

throttle 函数节流

函数节流

作用: 函数节流指指的是在一段时间内只允许函数执行一次 (例如 3 秒执行一次那么在函数第一次调用后的 3 秒内后面的函数调用将被忽略)
原理: 利用时间戳来判断,记录上次执行的时间戳,在每次触发事件时判断当前时间是否大于上次执行的时间 + 设置的间隔 ,如果是则执行回调并更新上次执行的时间戳
应用: 降低 scroll resize 事件的触发频率

实现节流函数
function throttle(fn, wait) {
  // 通过闭包缓存上一次的调用时间 (默认为 0)
  let lastCallTime = 0;
  return function () {
    const now = Date.now();
    // 判断当前调用时间和上次调用时间的差值是否大于 wait
    if (now - lastCallTime >= wait) {
      // 更新调用时间
      lastCallTime = now;
      // 执行回调函数
      fn.apply(this, arguments);
    }
  };
}