高级 JavaScript 面试题
执行上下文与作用域
1. 执行上下文类型
- 全局执行上下文:默认上下文,创建全局对象(浏览器中是 window)
- 函数执行上下文:每次函数调用创建一个新的执行上下文
- Eval 执行上下文:eval 函数内部的上下文
2. 作用域链
当查找变量时,会沿着作用域链向上查找:
当前作用域 → 外层函数作用域 → ... → 全局作用域
3. 闭包
闭包是指有权访问另一个函数作用域中变量的函数。
应用场景:
- 数据私有化
- 函数柯里化
- 节流防抖
- 缓存计算结果
原型与继承
1. 原型链
对象.__proto__ → 构造函数.prototype → Object.prototype → null2. 继承方式
| 方式 | 优点 | 缺点 |
|---|---|---|
| 原型链继承 | 简单 | 引用类型共享 |
| 构造函数继承 | 不共享引用 | 不能继承原型方法 |
| 组合继承 | 综合优点 | 调用两次父类构造函数 |
| 寄生组合继承 | 最优方案 | 稍复杂 |
| ES6 class extends | 语法简洁 | 本质是语法糖 |
3. 手写 new 操作符
js
function myNew(fn, ...args) {
const obj = Object.create(fn.prototype)
const result = fn.apply(obj, args)
return result instanceof Object ? result : obj
}异步编程
1. Promise 原理
Promise 是异步编程的一种解决方案,代表一个异步操作的最终完成或失败。
状态:
- pending(等待)
- fulfilled(成功)
- rejected(失败)
手写 Promise:
js
class MyPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
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 (err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
// 实现链式调用...
}
}2. async/await 原理
async/await 是 Promise 的语法糖:
- async 函数返回一个 Promise
- await 会暂停 async 函数的执行,等待 Promise 完成
3. 宏任务与微任务
宏任务 (macrotask):
- setTimeout / setInterval
- setImmediate(Node.js)
- I/O 操作
- UI 渲染
微任务 (microtask):
- Promise.then / catch / finally
- process.nextTick(Node.js,优先级最高)
- MutationObserver
执行顺序:同步代码 → 所有微任务 → 一个宏任务 → 所有微任务 → 一个宏任务...
手写代码题
1. 防抖 (Debounce)
js
function debounce(fn, delay, immediate = false) {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
if (immediate && !timer) {
fn.apply(this, args)
}
timer = setTimeout(() => {
if (!immediate) {
fn.apply(this, args)
}
timer = null
}, delay)
}
}2. 节流 (Throttle)
js
function throttle(fn, delay) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime >= delay) {
fn.apply(this, args)
lastTime = now
}
}
}3. 深拷贝
js
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (hash.has(obj)) return hash.get(obj)
const cloneObj = new obj.constructor()
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}4. Promise.all
js
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = []
let completed = 0
promises.forEach((promise, index) => {
Promise.resolve(promise)
.then((value) => {
results[index] = value
completed++
if (completed === promises.length) {
resolve(results)
}
})
.catch(reject)
})
})
}5. 数组扁平化
js
function flatten(arr, depth = Infinity) {
return depth > 0
? arr.reduce((acc, val) => acc.concat(Array.isArray(val) ? flatten(val, depth - 1) : val), [])
: arr.slice()
}内存管理
1. 垃圾回收机制
标记清除(Mark-Sweep):
- 标记阶段:遍历所有对象,标记可达对象
- 清除阶段:清除未标记的对象
引用计数:
- 跟踪每个值的引用次数
- 引用次数为 0 时回收
- 缺点:无法处理循环引用
2. 内存泄漏场景
- 未清理的定时器和事件监听
- 全局变量
- 闭包引用
- DOM 引用未释放
- console.log 缓存
设计模式
常用设计模式
| 模式 | 应用场景 |
|---|---|
| 单例模式 | 全局唯一实例(如 Vuex store) |
| 工厂模式 | 创建复杂对象 |
| 观察者模式 | 事件监听、发布订阅 |
| 策略模式 | 表单验证、不同算法切换 |
| 代理模式 | Vue 响应式、图片懒加载 |
| 装饰器模式 | 扩展功能、AOP |
观察者模式 vs 发布订阅模式
- 观察者模式:Subject 直接通知 Observer,耦合度较高
- 发布订阅模式:通过 EventBus 中间件通信,完全解耦
