前端面试精华指南

简介: 本文系统梳理前端核心知识:涵盖变量作用域、闭包、原型继承、异步编程、事件循环、虚拟DOM、组件通信、性能优化等13大模块,结合代码与面试题,助力深入理解JavaScript底层机制与工程实践。

@[TOC]

一、变量与作用域

1. 核心概念

  • var / let / const 的区别
    • var:函数作用域、存在变量提升、允许重复声明。
    • let / const:块级作用域、存在暂时性死区(TDZ)、禁止重复声明。
  • 作用域类型:全局作用域、函数作用域、块级作用域。
  • 作用域链:JavaScript 引擎在查找变量时,会沿着当前作用域向上逐层搜索,直到全局作用域。

2. 高频面试题

“为什么 var 在 for 循环中会导致闭包问题?而 let 不会?”

3. 代码示例

// var:函数作用域 + 提升 → 所有回调共享同一个 i(值为 3)
for (var i = 0; i < 3; i++) {
   
  setTimeout(() => console.log(i), 100); // 输出:3, 3, 3
}

// let:每次循环创建新的块级绑定 → 每个闭包捕获独立的 i
for (let i = 0; i < 3; i++) {
   
  setTimeout(() => console.log(i), 100); // 输出:0, 1, 2
}

4. 延伸知识点

  • 暂时性死区(TDZ):在 let/const 声明前访问变量会抛出 ReferenceError
  • 全局对象差异
    • 浏览器中:var a = 1 会挂载到 window.a
    • Node.js 中:模块作用域隔离,不会污染 global

二、闭包与内存管理

1. 核心概念

  • 闭包:函数与其词法环境的组合。即使外部函数已执行完毕,内部函数仍可访问其变量。
  • 典型用途:数据封装、模块模式、防抖节流、私有变量模拟。
  • 内存泄漏风险:闭包若长期持有对 DOM 节点或大对象的引用,且未及时释放,可能导致内存无法回收。

2. 高频面试题

“如何用闭包实现一个计数器?闭包一定会导致内存泄漏吗?”

3. 代码示例

function createCounter() {
   
  let count = 0;
  return {
   
    increment: () => ++count,
    decrement: () => --count,
    getCount: () => count
  };
}

const counter = createCounter();
counter.increment(); // 1
console.log(counter.getCount()); // 1

4. 延伸建议

  • 使用 Chrome DevTools 的 Memory 面板 分析闭包引用链。
  • 对于缓存场景,可使用 WeakMap 避免强引用导致的内存泄漏:
    const cache = new WeakMap();
    function memoize(fn) {
         
      return obj => {
         
        if (cache.has(obj)) return cache.get(obj);
        const result = fn(obj);
        cache.set(obj, result);
        return result;
      };
    }
    

三、原型与继承

1.核心概念

  • prototype:构造函数的属性,指向原型对象。
  • __proto__:实例对象的内部属性,指向其构造函数的 prototype
  • ES6 class 本质仍是基于原型的语法糖。

2. 高频面试题

“什么是寄生组合式继承?为什么它是 JavaScript 继承的最佳实践?”

3. 代码示例(寄生组合继承)

function Parent(name) {
   
  this.name = name;
}
Parent.prototype.say = function() {
   
  console.log(this.name);
};

function Child(name, age) {
   
  Parent.call(this, name); // 借用构造函数,继承实例属性
  this.age = age;
}

// 继承原型方法
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // 修复 constructor 指向

const c = new Child('Alice', 25);
c.say(); // Alice

4.延伸知识点

  • instanceof 原理:检查对象的 __proto__ 链是否包含构造函数的 prototype
  • 推荐使用 Object.getPrototypeOf()Reflect.getPrototypeOf() 获取原型。

四、异步编程演进

1. 演进路径

Callback → Promise → Generator + co → async/await

2. 高频面试题

“Promise 相比回调函数有哪些优势?”

3.代码对比

// 回调地狱(难以维护、错误处理分散)
getData(a => {
   
  getMoreData(a, b => {
   
    getEvenMore(b, c => {
   
      console.log(c);
    });
  });
});

// Promise 链式调用(扁平化、统一错误处理)
getData()
  .then(getMoreData)
  .then(getEvenMore)
  .then(console.log)
  .catch(err => console.error(err));

// async/await(最接近同步写法,语义清晰)
async function fetchAll() {
   
  try {
   
    const a = await getData();
    const b = await getMoreData(a);
    const c = await getEvenMore(b);
    console.log(c);
  } catch (err) {
   
    console.error(err);
  }
}

4. 延伸建议

  • 并行请求:Promise.all([p1, p2])
  • 容错并行:Promise.allSettled()
  • 避免“async 函数滥用”:简单同步逻辑无需包装为 async

五、Promise 深度解析

1. 核心机制

  • 状态不可逆:pending → fulfilled / rejected
  • 微任务队列.then() / .catch() 回调被放入微任务队列,优先于宏任务执行。
  • 链式调用:每个 .then() 返回新 Promise,支持连续处理。

2. 高频面试题

“手写 Promise.all,并说明其行为。”

手写 Promise.all

Promise.myAll = function(promises) {
   
  return new Promise((resolve, reject) => {
   
    if (!Array.isArray(promises)) {
   
      return reject(new TypeError('Argument must be an array'));
    }
    const results = new Array(promises.length);
    let completed = 0;

    if (promises.length === 0) return resolve(results);

    promises.forEach((p, i) => {
   
      Promise.resolve(p).then(
        val => {
   
          results[i] = val;
          if (++completed === promises.length) resolve(results);
        },
        err => reject(err)
      );
    });
  });
};

3. 延伸知识点

  • Promise.race():返回第一个 settled 的 Promise 结果。
  • Promise.any():返回第一个 fulfilled 的结果(ES2021)。
  • 监听未处理 rejection:window.addEventListener('unhandledrejection', ...)

六、事件循环机制(Event Loop)

1. 核心模型

  • 调用栈(Call Stack):执行同步代码。
  • 任务队列
    • 宏任务(Macrotask)setTimeoutsetInterval、I/O、UI 渲染
    • 微任务(Microtask)Promise.thenqueueMicrotaskMutationObserver
  • 执行顺序:同步代码 → 清空微任务队列 → 执行一个宏任务 → 循环

2. 高频面试题

“以下代码的输出顺序是什么?”

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);

// 输出顺序:1 → 4 → 3 → 2

3. 注意事项

  • Node.js 在 v11 之前微任务执行时机与浏览器不同,现已对齐。
  • process.nextTick()(Node.js)优先级高于 Promise.then

七、函数式编程概念

1. 核心思想

  • 纯函数:无副作用、相同输入必得相同输出。
  • 不可变性:避免直接修改原数据,返回新对象。
  • 高阶函数:接收函数作为参数或返回函数(如 mapfilterreducecompose)。

2. 高频面试题

“如何用 reduce 实现 map?如何实现函数组合(compose)?”

3. 代码示例

// 用 reduce 实现 map
Array.prototype.myMap = function(fn) {
   
  return this.reduce((acc, cur, i, arr) => {
   
    acc.push(fn(cur, i, arr));
    return acc;
  }, []);
};

// 函数组合:compose(f, g)(x) = f(g(x))
const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x);

const add1 = x => x + 1;
const double = x => x * 2;
const result = compose(add1, double)(5); // 11

4. 延伸建议

  • 推荐库:Ramda、Lodash/fp
  • React 中 useMemo / useCallback 本质是缓存纯函数结果,提升性能。

八、类型系统与 TypeScript

1. 核心优势

  • 编译期类型检查,提前暴露错误。
  • 更强的 IDE 智能提示、自动补全、重构支持。
  • 支持接口(interface)、泛型(Generics)、联合/交叉类型等高级特性。

2. 高频面试题

interfacetype 有什么区别?”

特性 interface type
可扩展(声明合并)
支持计算属性
能表示原始类型 (如 type A = string

3. 泛型实战示例

interface ApiResponse<T> {
   
  code: number;
  data: T;
  message: string;
}

async function request<T>(url: string): Promise<ApiResponse<T>> {
   
  const res = await fetch(url);
  return res.json();
}

// 使用
interface User {
    id: number; name: string; }
const user = await request<User>('/api/user');

4. 延伸知识点

  • Utility TypesPartial<T>Pick<T, K>Omit<T, K>Record<K, T>
  • 类型守卫:通过 typeofin、自定义函数缩小类型范围。

九、虚拟 DOM 与 Diff 算法

1. 核心概念

虚拟 DOM 是用 JS 对象描述真实 DOM 的轻量表示。当状态变化时,通过 Diff 算法 计算最小更新 patch,批量应用到真实 DOM,避免频繁重排重绘。

2. 高频面试题

“为什么不能直接操作真实 DOM?key 的作用是什么?”

3. 简易虚拟 DOM 实现

// 创建虚拟节点
function h(tag, props, children) {
   
  return {
    tag, props, children };
}

// 渲染 vnode 到真实 DOM
function render(vnode, container) {
   
  if (typeof vnode === 'string') {
   
    return document.createTextNode(vnode);
  }
  const el = document.createElement(vnode.tag);
  for (let key in vnode.props) {
   
    el.setAttribute(key, vnode.props[key]);
  }
  vnode.children.forEach(child => {
   
    el.appendChild(render(child));
  });
  if (container) container.appendChild(el);
  return el;
}

// 简易 diff(仅同层级替换)
function patch(parent, oldVNode, newVNode) {
   
  if (oldVNode.tag !== newVNode.tag) {
   
    parent.replaceChild(render(newVNode), parent.firstChild);
    return;
  }
  // 简化:直接替换子树
  const newEl = render(newVNode);
  parent.replaceChild(newEl, parent.firstChild);
}

4. key 的正确使用

//  错误:使用 index 作为 key
{list.map((item, index) => <Item key={index} value={item} />)}

//  正确:使用唯一 ID
{list.map(item => <Item key={item.id} value={item} />)}

原因:当列表发生插入/删除时,index 会错位,导致 React 复用错误的组件实例,引发状态错乱(如输入框内容错位)。

十、组件通信与状态管理

1. 通信方案对比

场景 React Vue 3
父 → 子 props props
子 → 父 回调函数 / useState $emit / v-model
跨层级 Context / Zustand provide/inject / Pinia
全局状态 Redux Toolkit / Jotai Pinia

2. React:Context + useReducer

const AppContext = createContext();

function appReducer(state, action) {
  switch (action.type) {
    case 'SET_USER': return { ...state, user: action.payload };
    default: return state;
  }
}

export function AppProvider({ children }) {
  const [state, dispatch] = useReducer(appReducer, { user: null });
  return (
    <AppContext.Provider value={
  { state, dispatch }}>
      {children}
    </AppContext.Provider>
  );
}

// 使用
const { state } = useContext(AppContext);

3. Vue 3 + Pinia

// stores/user.js
import {
    defineStore } from 'pinia';
export const useUserStore = defineStore('user', {
   
  state: () => ({
    name: '', token: '' }),
  actions: {
   
    login(data) {
   
      this.name = data.name;
      this.token = data.token;
    }
  }
});

4. 面试回答技巧

“优先使用 props/events;跨多层用 Context/provide;复杂状态用 Redux/Pinia。避免过早引入全局状态。”

十一、性能优化实战

1. 三大方向

  • 加载性能:代码分割、懒加载、预加载、CDN
  • 运行时性能:防抖节流、虚拟滚动、React.memo
  • 内存管理:及时解绑事件、避免闭包泄漏

2. 关键代码示例

1. 懒加载组件

// React
const LazyComp = React.lazy(() => import('./HeavyComponent'));
<Suspense fallback="Loading..."><LazyComp /></Suspense>

// Vue 3
const AsyncComp = defineAsyncComponent(() => import('./Heavy.vue'));

2. 防抖函数

function debounce(fn, delay) {
   
  let timer;
  return (...args) => {
   
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

3. 虚拟滚动(长列表)

function VirtualList({ items, itemHeight = 50 }) {
  const [scrollTop, setScrollTop] = useState(0);
  const visibleCount = Math.ceil(window.innerHeight / itemHeight);
  const start = Math.floor(scrollTop / itemHeight);
  const end = start + visibleCount;
  const visibleItems = items.slice(start, end);

  return (
    <div onScroll={e => setScrollTop(e.target.scrollTop)} style={
  { height: '100vh', overflow: 'auto' }}>
      <div style={
  { height: items.length * itemHeight }} />
      <div style={
  { transform: `translateY(${start * itemHeight}px)` }}>
        {visibleItems.map(item => (
          <div key={item.id} style={
  { height: itemHeight }}>{item.text}</div>
        ))}
      </div>
    </div>
  );
}

十二、工程化与构建工具

1. Webpack vs Vite 对比

特性 Webpack Vite
启动速度 慢(需打包) 极快(原生 ESM)
HMR 机制 模块替换 原生 ESM 精准更新
适用场景 大型复杂项目 快速开发、中小型项目

2. 自定义 Webpack Plugin

class BundleSizePlugin {
   
  apply(compiler) {
   
    compiler.hooks.done.tap('BundleSize', stats => {
   
      stats.toJson().assets?.forEach(asset => {
   
        console.log(`${
     asset.name}: ${
     (asset.size / 1024).toFixed(2)} KB`);
      });
    });
  }
}

3. Vite 优化配置

export default defineConfig({
   
  build: {
   
    rollupOptions: {
   
      output: {
   
        manualChunks: {
   
          vendor: ['react', 'react-dom'],
          utils: ['lodash', 'axios']
        }
      }
    }
  }
});

4. 面试题

“Tree Shaking 生效的前提是什么?”
:使用 ES Module(静态导入)、未使用导出项、正确配置 sideEffects

十三、浏览器原理与安全

1. 渲染流程(关键六步)

  1. HTML 解析 → DOM 树
  2. CSS 解析 → CSSOM 树
  3. DOM + CSSOM → Render Tree(过滤 display: none
  4. Layout(回流):计算几何位置
  5. Paint(重绘):绘制像素
  6. Composite(合成):分层后 GPU 合成

回流(reflow)成本远高于重绘(repaint)

2. XSS 防御

//  危险
el.innerHTML = userInput;

//  安全
el.textContent = userInput; // 最佳
// 或手动转义
const escapeHtml = str => str.replace(/[&<>"']/g, m => ({
   
  '&': '&amp;', '<': '&lt;', '>': '&gt;',
  '"': '&quot;', "'": '&#39;'
})[m]);

3. CORS 与 Cookie 安全

// 后端(Express)
app.use(cors({
    origin: 'https://your-app.com', credentials: true }));

// 前端
fetch('/api/data', {
    credentials: 'include' });

Cookie 安全属性

  • HttpOnly:禁止 JS 访问(防 XSS)
  • Secure:仅 HTTPS 传输
  • SameSite=Strict/Lax:防御 CSRF 攻击

总结

1. 前端工程师能力图谱

层级 能力要求
语言基础 JS 核心(作用域、闭包、原型、异步、事件循环)
框架深度 React/Vue 响应式原理、组件设计、状态管理
工程能力 构建工具、性能优化、CI/CD、监控体系
计算机基础 网络协议、浏览器原理、安全机制、数据结构

面试建议:回答时采用 “场景 → 问题 → 方案 → 权衡” 结构,展现系统思维与工程判断力。

2. 学习与准备建议

模块 实践建议
虚拟 DOM 手写 mini React,理解 patch 与 reconciliation
状态管理 对比 Context / Redux / Zustand / Pinia 适用场景
性能优化 在个人项目中跑 Lighthouse,完成一次完整优化
工程化 配置 Webpack/Vite,理解 loader/plugin 工作原理
浏览器 用 Performance 面板分析一个页面的渲染瓶颈
相关文章
|
3月前
|
存储 JavaScript 前端开发
Vue 事件总线(EventBus)详解
事件总线是一种基于发布-订阅模式的轻量级通信机制,用于解决Vue中非父子组件间的通信问题。通过创建全局事件中心,实现跨组件解耦,适用于登录通知、购物车更新等场景,但需注意内存泄漏与调试难题,建议配合命名空间与自动清理机制使用。
361 5
|
3月前
|
人工智能 前端开发 算法
大厂CIO独家分享:AI如何重塑开发者未来十年
在 AI 时代,若你还在紧盯代码量、执着于全栈工程师的招聘,或者仅凭技术贡献率来评判价值,执着于业务提效的比例而忽略产研价值,你很可能已经被所谓的“常识”困住了脚步。
1802 89
大厂CIO独家分享:AI如何重塑开发者未来十年
|
3月前
|
JSON 安全 JavaScript
深入浅出解析 HTTPS 原理
HTTPS是HTTP与SSL/TLS结合的安全协议,通过数字证书验证身份,利用非对称加密安全交换会话密钥,再以对称加密高效传输数据,确保通信的机密性、完整性和真实性。整个过程如同建立一条加密隧道,保障网络交互安全。
1438 16
|
3月前
|
监控 Java 开发者
Spring Boot 核心原理解析与实践(含代码示例)
Spring Boot基于“约定优于配置”理念,通过自动配置、Starter依赖和内嵌服务器,简化Spring应用的搭建与开发。支持快速集成Web、数据访问、安全等模块,并提供Actuator监控、分布式事务等生产级特性,助力高效构建微服务系统。(238字)
673 17
|
3月前
|
消息中间件 Java 调度
深入探讨进程、线程和协程之间的区别和联系
本文深入解析进程、线程与协程的核心区别与联系,涵盖资源分配、调度机制、通信方式及性能对比。结合代码示例与实际场景,阐明三者在高并发系统中的协同应用,助你掌握现代并发编程设计精髓。(239字)
314 11
|
3月前
|
架构师 Java 程序员
程序员的出路:30岁,我们聊聊那些真实的选择
30岁程序员的迷茫与出路:技术焦虑、薪资倒挂、能力单一困扰着许多人。本文基于真实观察,梳理五条可行路径——深耕技术、理性转管理、务实搞副业、跨界融合、提前布局B计划,并总结三条铁律与自测问题,帮助你在变局中找到方向。出路不在远方,而在你写下的每一行“值钱”的代码里。(238字)
629 117
|
3月前
|
Java 程序员 持续交付
Git 从入门到进阶:常用命令与高级用法全解析
本文系统梳理Git常用命令与高级技巧,涵盖初始化、分支管理、变基、储藏、标签、差异对比、二分查找及reflog等核心功能,结合最佳实践与避坑指南,助你从入门到精通,提升代码管理与团队协作效率。
460 74
|
3月前
|
JavaScript 前端开发 安全
JavaScript 数组扁平化:四种方法详解与最佳实践
本文详解JavaScript数组扁平化的四种主流方法:`flat()`、扩展运算符+`concat`、`reduce`和`for...of`循环,从语法、性能、兼容性等维度对比分析,结合适用场景与最佳实践,助你高效处理嵌套数组。
372 9
|
3月前
|
消息中间件 缓存 前端开发
WebSocket 与 MQTT 在即时通讯中的深度对比与架构选型指南
WebSocket 是双向通信通道,适合前端实时交互;MQTT 是轻量级消息协议,支持发布/订阅与可靠传输。二者互补,常结合使用:前端通过 WebSocket 接入,后端以 MQTT 实现高并发消息分发,构建可扩展的现代即时通讯系统。
676 17
|
3月前
|
自然语言处理 JavaScript 前端开发
全面解析 i18n:从概念到实践,再到底层原理
本文系统讲解国际化(i18n)的核心概念与实现原理,涵盖多语言文本、日期、数字、复数等处理方式,结合 i18next 与 Vue I18n 实战案例,深入剖析资源分离、环境识别与动态替换三大机制,并分享插值、格式化、CI/CD 集成等最佳实践,助力构建可扩展的全球化应用。
959 15