前端js手写面试题看这篇就够了

本文涵盖了前端面试中常见的手写题目,包括JSON.stringify的实现、Promise、函数柯里化、字符串最长不重复长度、Add方法、Object.is、setTimeout实现setInterval等。还涉及到图片懒加载、AJAX、排序算法、深拷贝以及简易MVVM实现,是JS开发者面试的绝佳参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现一个JSON.stringify

JSON.stringify(value[, replacer [, space]])
  • Boolean | Number| String类型会自动转换成对应的原始值。
  • undefined、任意函数以及symbol,会被忽略(出现在非数组对象的属性值中时),或者被转换成 null(出现在数组中时)。
  • 不可枚举的属性会被忽略如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略
  • 如果一个对象的属性值通过某种间接的方式指回该对象本身,即循环引用,属性也会被忽略
function jsonStringify(obj) {
   
    let type = typeof obj;
    if (type !== "object") {
   
        if (/string|undefined|function/.test(type)) {
   
            obj = '"' + obj + '"';
        }
        return String(obj);
    } else {
   
        let json = []
        let arr = Array.isArray(obj)
        for (let k in obj) {
   
            let v = obj[k];
            let type = typeof v;
            if (/string|undefined|function/.test(type)) {
   
                v = '"' + v + '"';
            } else if (type === "object") {
   
                v = jsonStringify(v);
            }
            json.push((arr ? "" : '"' + k + '":') + String(v));
        }
        return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}")
    }
}
jsonStringify({
   x : 5}) // "{"x":5}"
jsonStringify([1, "false", false]) // "[1,"false",false]"
jsonStringify({
   b: undefined}) // "{"b":"undefined"}"

手写 Promise

const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";

function MyPromise(fn) {
   
  // 保存初始化状态
  var self = this;

  // 初始化状态
  this.state = PENDING;

  // 用于保存 resolve 或者 rejected 传入的值
  this.value = null;

  // 用于保存 resolve 的回调函数
  this.resolvedCallbacks = [];

  // 用于保存 reject 的回调函数
  this.rejectedCallbacks = [];

  // 状态转变为 resolved 方法
  function resolve(value) {
   
    // 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
    if (value instanceof MyPromise) {
   
      return value.then(resolve, reject);
    }

    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
   
      // 只有状态为 pending 时才能转变,
      if (self.state === PENDING) {
   
        // 修改状态
        self.state = RESOLVED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.resolvedCallbacks.forEach(callback => {
   
          callback(value);
        });
      }
    }, 0);
  }

  // 状态转变为 rejected 方法
  function reject(value) {
   
    // 保证代码的执行顺序为本轮事件循环的末尾
    setTimeout(() => {
   
      // 只有状态为 pending 时才能转变
      if (self.state === PENDING) {
   
        // 修改状态
        self.state = REJECTED;

        // 设置传入的值
        self.value = value;

        // 执行回调函数
        self.rejectedCallbacks.forEach(callback => {
   
          callback(value);
        });
      }
    }, 0);
  }

  // 将两个方法传入函数执行
  try {
   
    fn(resolve, reject);
  } catch (e) {
   
    // 遇到错误时,捕获错误,执行 reject 函数
    reject(e);
  }
}

MyPromise.prototype.then = function(onResolved, onRejected) {
   
  // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
  onResolved =
    typeof onResolved === "function"
      ? onResolved
      : function(value) {
   
          return value;
        };

  onRejected =
    typeof onRejected === "function"
      ? onRejected
      : function(error) {
   
          throw error;
        };

  // 如果是等待状态,则将函数加入对应列表中
  if (this.state === PENDING) {
   
    this.resolvedCallbacks.push(onResolved);
    this.rejectedCallbacks.push(onRejected);
  }

  // 如果状态已经凝固,则直接执行对应状态的函数

  if (this.state === RESOLVED) {
   
    onResolved(this.value);
  }

  if (this.state === REJECTED) {
   
    onRejected(this.value);
  }
};

实现Promise

var PromisePolyfill = (function () {
   
  // 和reject不同的是resolve需要尝试展开thenable对象
  function tryToResolve (value) {
   
    if (this === value) {
   
    // 主要是防止下面这种情况
    // let y = new Promise(res => setTimeout(res(y)))
      throw TypeError('Chaining cycle detected for promise!')
    }

    // 根据规范2.32以及2.33 对对象或者函数尝试展开
    // 保证S6之前的 polyfill 也能和ES6的原生promise混用
    if (value !== null &&
      (typeof value === 'object' || typeof value === 'function')) {
   
      try {
   
      // 这里记录这次then的值同时要被try包裹
      // 主要原因是 then 可能是一个getter, 也也就是说
      //   1. value.then可能报错
      //   2. value.then可能产生副作用(例如多次执行可能结果不同)
        var then = value.then

        // 另一方面, 由于无法保证 then 确实会像预期的那样只调用一个onFullfilled / onRejected
        // 所以增加了一个flag来防止resolveOrReject被多次调用
        var thenAlreadyCalledOrThrow = false
        if (typeof then === 'function') {
   
        // 是thenable 那么尝试展开
        // 并且在该thenable状态改变之前this对象的状态不变
          then.bind(value)(
          // onFullfilled
            function (value2) {
   
              if (thenAlreadyCalledOrThrow) return
              thenAlreadyCalledOrThrow = true
              tryToResolve.bind(this, value2)()
            }.bind(this),

            // onRejected
            function (reason2) {
   
              if (thenAlreadyCalledOrThrow) return
              thenAlreadyCalledOrThrow = true
              resolveOrReject.bind(this, 'rejected', reason2)()
            }.bind(this)
          )
        } else {
   
        // 拥有then 但是then不是一个函数 所以也不是thenable
          resolveOrReject.bind(this, 'resolved', value)()
        }
      } catch (e) {
   
        if (thenAlreadyCalledOrThrow) return
        thenAlreadyCalledOrThrow = true
        resolveOrReject.bind(this, 'rejected', e)()
      }
    } else {
   
    // 基本类型 直接返回
      resolveOrReject.bind(this, 'resolved', value)()
    }
  }

  function resolveOrReject (status, data) {
   
    if (this.status !== 'pending') return
    this.status = status
    this.data = data
    if (status === 'resolved') {
   
      for (var i = 0; i < this.resolveList.length; ++i) {
   
        this.resolveList[i]()
      }
    } else {
   
      for (i = 0; i < this.rejectList.length; ++i) {
   
        this.rejectList[i]()
      }
    }
  }

  function Promise (executor) {
   
    if 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值