前端面试精华指南

一、变量与作用域

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 有什么区别?”

特性interfacetype
可扩展(声明合并)
支持计算属性
能表示原始类型(如 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. 通信方案对比

场景ReactVue 3
父 → 子propsprops
子 → 父回调函数 / useState$emit / v-model
跨层级Context / Zustandprovide/inject / Pinia
全局状态Redux Toolkit / JotaiPinia

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 对比

特性WebpackVite
启动速度慢(需打包)极快(原生 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 面板分析一个页面的渲染瓶颈
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木易 士心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值