前端 JavaScript 面试题汇总及深度解析(2025版)

542 阅读4分钟

前端 JavaScript 面试题汇总及深度解析(2025版)

一、JavaScript 基础核心

1. 解释 JavaScript 中的执行上下文(Execution Context)

问题:请描述 JavaScript 执行上下文的创建过程及其核心组成

答案: 执行上下文是 JavaScript 代码执行时的环境抽象概念,包含三个核心部分:

  1. 变量环境(Variable Environment
    • 存储 var 声明的变量和函数声明
    • 在编译阶段完成初始化(变量提升)
  2. 词法环境(Lexical Environment
    • 存储 let/const 声明的变量
    • 具有块级作用域特性
  3. this 绑定
    • 在运行时确定指向

解析

function demo() {
    console.log(a); // undefined
    var a = 10;
    let b = 20;
}
  • 编译阶段:创建变量环境(a=undefined),初始化函数声明
  • 执行阶段:执行赋值操作,词法环境中的 b 在声明前访问会报错(TDZ)

2. 闭包与内存泄漏

问题:如何理解闭包导致的内存泄漏?如何检测和避免?

答案
闭包内存泄漏的典型场景:

function createClosure() {
    const largeData = new Array(1000000).fill('*');
    return function() {
        console.log('Closure created');
    };
}
let fn = createClosure();

即使不再使用fnlargeData仍被闭包引用无法回收

解决方案

  1. 使用 Chrome DevTools 的 Memory 面板进行堆快照对比
  2. 主动解除引用:fn = null
  3. 使用 WeakMap 代替常规对象存储大数据

3. 原型链继承的多种实现方式

问题:实现一个继承方案,要求:

  • 子类继承父类属性
  • 子类可扩展方法
  • 避免引用类型共享问题

答案及解析

// 最佳实践:寄生组合继承
function Parent(name) {
    this.name = name;
    this.colors = ['red'];
}
Parent.prototype.say = function() {};

function Child(name) {
    Parent.call(this, name); // 继承实例属性
}

// 原型继承
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;

// 测试
const c1 = new Child('Tom');
c1.colors.push('blue'); 
const c2 = new Child('Jerry');
console.log(c2.colors); // ['red']

二、异步编程与事件循环

4. 宏任务与微任务执行顺序

问题:分析以下代码执行顺序

console.log('script start');

setTimeout(() => {
    console.log('setTimeout');
}, 0);

Promise.resolve().then(() => {
    console.log('promise1');
}).then(() => {
    console.log('promise2');
});

console.log('script end');

答案

  1. script start
  2. script end
  3. promise1
  4. promise2
  5. setTimeout

解析

  • 主线程任务(宏任务)先执行
  • 微任务队列会在每个宏任务结束后立即清空
  • 流程示意图:
宏任务队列: [主脚本]
微任务队列: []

执行主脚本 → 
宏任务队列: []
微任务队列: [promise1回调]
执行微任务 → 
宏任务队列: [setTimeout回调]

5. async/await 实现原理

问题:async 函数在低版本浏览器如何兼容?

答案
通过 Babel 转换后的代码:

async function demo() {
    await fetchData();
}
// 转换为:
function demo() {
    return _asyncToGenerator(function* () {
        yield fetchData();
    })();
}

function _asyncToGenerator(fn) {
    return function() {
        const gen = fn.apply(this, arguments);
        return new Promise((resolve, reject) => {
            function step(key, arg) {
                try {
                    const info = gen[key](arg);
                    const value = info.value;
                    if (info.done) {
                        resolve(value);
                    } else {
                        return Promise.resolve(value).then(
                            v => step("next", v),
                            e => step("throw", e)
                        );
                    }
                } catch (e) {
                    reject(e);
                }
            }
            return step("next");
        });
    };
}

三、ES6+ 新特性深度解析

6. Proxy 与响应式实现

问题:使用 Proxy 实现简易 Vue 数据响应

答案

const handler = {
    get(target, key) {
        track(target, key); // 依赖收集
        return Reflect.get(...arguments);
    },
    set(target, key, value) {
        const result = Reflect.set(...arguments);
        trigger(target, key); // 触发更新
        return result;
    }
};

function reactive(obj) {
    return new Proxy(obj, handler);
}

// 使用示例
const state = reactive({ count: 0 });
effect(() => {
    console.log('Count changed:', state.count);
});
state.count++; // 触发日志输出

四、浏览器原理与性能优化

7. Event Loop 渲染时机

问题:requestAnimationFrame 与 requestIdleCallback 的区别

解析

特性requestAnimationFramerequestIdleCallback
触发时机每次重绘前主线程空闲时
执行频率60fps(约16.6ms)不确定,可能被高优先级阻断
适用场景动画更新非关键后台任务
是否可能丢帧

五、高频手写代码题

8. 实现 Promise.all

要求

  • 处理可迭代对象
  • 保持结果顺序
  • 快速失败机制
Promise.myAll = function(iterable) {
    const tasks = Array.from(iterable);
    if (tasks.length === 0) return Promise.resolve([]);
    
    return new Promise((resolve, reject) => {
        const results = new Array(tasks.length);
        let count = 0;
        
        tasks.forEach((task, index) => {
            Promise.resolve(task).then(res => {
                results[index] = res;
                if (++count === tasks.length) {
                    resolve(results);
                }
            }).catch(reject);
        });
    });
};

六、TypeScript 高级类型

9. 条件类型与 infer 关键字

问题:实现提取 Promise 泛型类型

type UnpackPromise<T> = T extends Promise<infer R> ? R : never;

// 测试
type Test = UnpackPromise<Promise<string>>; // string

七、设计模式实战

10. 观察者模式 vs 发布订阅模式

区别

  • 观察者:Subject 直接维护 Observer 列表
  • 发布订阅:通过中间通道(Event Channel)解耦

发布订阅实现

class EventBus {
    constructor() {
        this.events = new Map();
    }
    
    on(type, handler) {
        const handlers = this.events.get(type) || [];
        handlers.push(handler);
        this.events.set(type, handlers);
    }
    
    emit(type, ...args) {
        const handlers = this.events.get(type) || [];
        handlers.forEach(fn => fn(...args));
    }
    
    off(type, handler) {
        const handlers = this.events.get(type) || [];
        this.events.set(
            type,
            handlers.filter(fn => fn !== handler)
        );
    }
}