面试备战录

58 阅读5分钟

1、Vue的响应式原理是什么?Vue2Vue3的实现机制有何区别?

答:Vue响应式其核心目标:当数据变化时,自动触发依赖该数据的视图更新。实现方式主要有两步:依赖收集数据变化派发更新

Vue2 的实现机制

  • 技术核心:Object.defineProperty劫持数据的getter/setter
  • 流程:
    • 初始化数据时,遍历对象的每个属性,用Object.defineProperty定义getter/setter
    • getter中收集依赖(Dep存储Watcher
    • setter中通知依赖更新(Dep.notify
  • 依赖管理:
    • Dep:每个属性对应一个依赖管理器
    • Watcher:订阅者(组件渲染函数、计算属性等)
  • 缺点:
    • 无法监听属性新增/删除
    • 无法监听数组索引修改和length变化
    • 初始化时需要递归遍历所有属性,性能差

Vue3 的实现机制

  • 技术核心:Proxy+Reflect
  • 流程:
    • 使用reactive()时返回一个Proxy对象
    • get时调用track收集依赖(按需追踪)
    • set时调用trigger触发依赖更新
  • 优化:
    • 按需追踪:只有访问过的属性才收集依赖
    • 懒代理:深层对象只有在访问时才会递归代理
    • 批量更新:依赖更新合并到微任务队列中执行,减少渲染次数
  • 优点:
    • 支持新增 / 删除属性、数组索引、length 变化
    • 性能更好,内存占用更少

2、Vue 生命周期中 created 和 mounted 区别?分别适用于什么?

  • created:实例创建完成(data、methods 已初始化),但 DOM 还没生成;
    • 适合数据初始化、调用API请求数据
    • 不能操作DOM(因为还没生成)
    • 更早执行,可以减少首屏延迟
  • mounted:模板渲染完成,真实DOM挂载到页面
    • 适合需要访问真实DOM的操作(如操作 canvas调用第三方DOM插件
    • 数据和DOM都可用
    • 在首次渲染之后执行

3、什么是作用域链?与原型链的区别是什么?

答:**作用域链(Scope Chain)**是 JS 执行环境(函数、块等)查找变量的一种机制。当访问一个变量时,JS引擎会沿着作用域链向上查找,直到找到为止,找不到则报ReferenceError

  • 每个执行上下文(函数、全局等)都有一个 变量环境(Variable Environment)
  • 当在函数内部访问变量时:
    • 先查找当前上下文的变量环境。
    • 如果找不到,则查找父级作用域的变量环境。
    • 一直向上直到全局作用域。
  • 所有这些变量环境按 链式结构连接起来,就形成了作用域链。

原型链:是对象查找属性的方法。当访问对象的某个属性时,如果对象本身没有,则会沿着原型链查找,直到找到或到达null

  • 每个对象都有一个隐藏属性[[Prototype]](通常用__proto__表示)。
  • 查找属性时:
    • 先查找对象自身的属性。
    • 如果没有,则查找 [[Prototype]] 指向的对象。
    • 一直向上查找直到null

作用域链:解决 “变量在哪里找” 的问题; 原型链:解决 “对象属性在哪里找” 的问题 两者都是链式结构,但作用域链是静态的(函数定义时决定父作用域),原型链是动态的(对象创建时决定原型)。

4、如何实现函数防抖与节流?应用在哪些场景?

答:函数防抖(Debounce):在事件被频繁触发时,只执行最后一次。事件触发后,延迟一段时间执行函数,如果在延迟时间内又触发了事件,就重新计时

应用场景

  • 搜索输入框:用户停止输入一段时间后再发请求,避免每个字都请求一次。
  • 窗口resize事件:停止调整窗口后再执行逻辑。
  • 表单验证:用户停止输入时再校验。
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

函数节流(Throttle):在事件被频繁触发时,保证一定时间间隔内只执行一次。通过时间戳或定时器,让函数在一个固定间隔内执行。

应用场景

  • 滚动事件(scroll):页面滚动时,每隔一段时间触发一次,比如懒加载。
  • 按钮点击:防止用户短时间内多次点击提交按钮。
  • 鼠标移动事件(mousemove):限制触发频率,提升性能。
function Throttle(fn, interval) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      fn.apply(this, args);
      lastTime = now;
    }
  };
}

5、说说Event Loop的运行机制?宏任务与微任务的区别是什么?

答:JavaScript单线程的,所有任务在同一个主线程上排队执行。为了协调同步任务异步任务的执行,浏览器Node.js实现了 事件循环机制(Event Loop)。运行机制大致是:

  • 同步任务 → 直接在主线程执行,进入 执行栈(Call Stack)
  • 异步任务 → 比如定时器PromiseDOM事件,会先交给浏览器NodeAPI处理。
  • 执行栈清空后,事件循环从**任务队列(Task Queue)**中取任务放到执行栈中执行。

宏任务 MacroTask:每次从事件队列中取出来的任务就是一个宏任务。常见的宏任务:setTimeoutsetIntervalsetImmediate(Node.js)I/O 操作UI 渲染

微任务 MicroTask:在当前宏任务执行完毕后、下一个宏任务开始前,会清空所有微任务队列。常见的微任务:Promise.then / catch / finallyqueueMicrotaskMutationObserver

执行顺序

  • 执行全局脚本(这也是一个宏任务)。
  • 执行过程中如果遇到微任务,放到微任务队列
  • 当前宏任务执行完毕 → 立即清空所有微任务队列
  • 再从宏任务队列中取出下一个宏任务执行。
  • 如此循环。