JavaScript面试核心:从基础到高级特性全解析

📅 2026/8/24 2:57:39
JavaScript面试核心:从基础到高级特性全解析
1. JavaScript面试题全解析从基础到实战作为一名前端开发者我深知JavaScript面试题的重要性。它们不仅是检验候选人技术水平的试金石更是我们日常开发中实际问题的缩影。在过去的几年里我参与过数十场技术面试也帮助过不少朋友准备面试。今天我将系统性地梳理JavaScript面试中的核心知识点分享一些常见但容易出错的问题以及它们背后的原理和最佳实践。JavaScript作为前端开发的基石语言其面试题往往涵盖了语言特性、异步编程、作用域、原型链等基础概念同时也包括性能优化、设计模式等高级话题。理解这些题目不仅能帮助你在面试中脱颖而出更能提升你的日常开发能力。2. JavaScript基础核心概念2.1 数据类型与类型转换JavaScript有7种原始数据类型Undefined、Null、Boolean、Number、String、Symbol(ES6新增)、BigInt(ES2020新增)以及Object类型。理解这些类型及其转换规则至关重要。// 类型检测的几种方式 typeof null // object (历史遗留问题) typeof [] // object typeof function(){} // function // 更可靠的类型检测 Object.prototype.toString.call([]) // [object Array]类型转换是面试中的高频考点特别是和的区别 0 // false 0 // true 0 0 // true false false // false false 0 // true提示在实际开发中建议始终使用以避免隐式类型转换带来的意外行为。2.2 作用域与闭包作用域决定了变量的可见性。JavaScript采用词法作用域(静态作用域)函数的作用域在定义时就已确定而非执行时。function outer() { const x 10; function inner() { console.log(x); // 可以访问外部变量x } return inner; } const myFunc outer(); myFunc(); // 10闭包是指函数能够记住并访问其词法作用域即使函数在其词法作用域之外执行。闭包在实际开发中有广泛应用如模块模式、私有变量等。// 模块模式示例 const counter (function() { let privateCounter 0; return { increment: function() { privateCounter; }, getValue: function() { return privateCounter; } }; })(); counter.increment(); console.log(counter.getValue()); // 12.3 原型与继承JavaScript使用原型继承而非类继承(尽管ES6引入了class语法糖)。每个对象都有一个内部链接指向另一个对象称为原型。function Person(name) { this.name name; } Person.prototype.greet function() { console.log(Hello, my name is ${this.name}); }; const john new Person(John); john.greet(); // Hello, my name is JohnES6的class语法让原型继承更易理解class Person { constructor(name) { this.name name; } greet() { console.log(Hello, my name is ${this.name}); } } class Student extends Person { constructor(name, grade) { super(name); this.grade grade; } study() { console.log(${this.name} is studying); } }3. 异步编程与事件循环3.1 回调、Promise与async/awaitJavaScript是单线程的异步编程是其核心特性之一。从早期的回调地狱到现代的async/await异步编程方式不断演进。// 回调地狱示例 getData(function(a) { getMoreData(a, function(b) { getMoreData(b, function(c) { console.log(c); }); }); }); // Promise链式调用 getData() .then(a getMoreData(a)) .then(b getMoreData(b)) .then(c console.log(c)) .catch(err console.error(err)); // async/await async function processData() { try { const a await getData(); const b await getMoreData(a); const c await getMoreData(b); console.log(c); } catch (err) { console.error(err); } }3.2 事件循环机制事件循环是JavaScript实现非阻塞I/O的核心机制。理解事件循环能帮助你更好地处理异步代码的执行顺序。console.log(Start); setTimeout(() console.log(Timeout), 0); Promise.resolve().then(() console.log(Promise)); console.log(End); // 输出顺序 // Start // End // Promise // Timeout事件循环的执行顺序执行同步代码执行微任务(Promise回调、MutationObserver等)执行宏任务(setTimeout、setInterval、I/O等)重复上述过程4. 高级特性与性能优化4.1 函数式编程特性JavaScript支持函数式编程风格高阶函数、纯函数、不可变性等概念在实际开发中非常有用。// 高阶函数示例 const multiplyBy factor number number * factor; const double multiplyBy(2); console.log(double(5)); // 10 // 函数组合 const compose (...fns) x fns.reduceRight((v, f) f(v), x); const add5 x x 5; const multiply2 x x * 2; const addThenMultiply compose(multiply2, add5); console.log(addThenMultiply(3)); // 164.2 内存管理与性能优化JavaScript的垃圾回收是自动的但不当的代码仍可能导致内存泄漏。常见内存泄漏场景意外的全局变量被遗忘的定时器或回调DOM引用未清理闭包不当使用// 内存泄漏示例 function createLeak() { const hugeArray new Array(1000000).fill(*); return function() { console.log(Leaking...); }; } const leakyFunction createLeak(); // hugeArray不会被回收因为被闭包引用性能优化技巧避免频繁的DOM操作使用事件委托减少事件监听器数量合理使用防抖和节流使用Web Worker处理CPU密集型任务// 防抖函数实现 function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId setTimeout(() { func.apply(this, args); }, delay); }; } // 节流函数实现 function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle true; setTimeout(() inThrottle false, limit); } }; }5. 常见面试题解析与实战5.1 高频面试题解析实现一个深拷贝函数function deepClone(obj, map new WeakMap()) { if (obj null || typeof obj ! object) return obj; if (map.has(obj)) return map.get(obj); let clone Array.isArray(obj) ? [] : {}; map.set(obj, clone); for (let key in obj) { if (obj.hasOwnProperty(key)) { clone[key] deepClone(obj[key], map); } } return clone; }手写Promise实现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) { onFulfilled typeof onFulfilled function ? onFulfilled : value value; onRejected typeof onRejected function ? onRejected : err { throw err }; const promise2 new MyPromise((resolve, reject) { if (this.state fulfilled) { setTimeout(() { try { const x onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } else if (this.state rejected) { setTimeout(() { try { const x onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); } else { this.onFulfilledCallbacks.push(() { setTimeout(() { try { const x onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); this.onRejectedCallbacks.push(() { setTimeout(() { try { const x onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); } }); return promise2; } } function resolvePromise(promise2, x, resolve, reject) { if (x promise2) { return reject(new TypeError(Chaining cycle detected for promise)); } if (x instanceof MyPromise) { x.then(resolve, reject); } else { resolve(x); } }5.2 实际开发场景题实现一个图片懒加载组件class LazyLoad { constructor(selector img[data-src], options {}) { this.images document.querySelectorAll(selector); this.options { rootMargin: 0px, threshold: 0.1, ...options }; this.init(); } init() { if (IntersectionObserver in window) { this.observeWithIntersection(); } else { this.observeWithScroll(); } } observeWithIntersection() { const observer new IntersectionObserver((entries) { entries.forEach(entry { if (entry.isIntersecting) { this.loadImage(entry.target); observer.unobserve(entry.target); } }); }, this.options); this.images.forEach(img observer.observe(img)); } observeWithScroll() { const loadImages () { this.images.forEach(img { if (this.isInViewport(img)) { this.loadImage(img); } }); }; window.addEventListener(scroll, throttle(loadImages, 200)); loadImages(); } isInViewport(element) { const rect element.getBoundingClientRect(); return ( rect.top window.innerHeight * 1.5 rect.bottom 0 rect.left window.innerWidth rect.right 0 ); } loadImage(img) { const src img.getAttribute(data-src); if (!src) return; img.src src; img.removeAttribute(data-src); } }实现一个简单的虚拟DOM diff算法function diff(oldVNode, newVNode) { if (!oldVNode) { return { type: CREATE, node: newVNode }; } if (!newVNode) { return { type: REMOVE }; } if (isVNodeChanged(oldVNode, newVNode)) { return { type: REPLACE, node: newVNode }; } if (newVNode.type) { const patches []; const oldChildren oldVNode.children || []; const newChildren newVNode.children || []; const len Math.max(oldChildren.length, newChildren.length); for (let i 0; i len; i) { patches.push(diff(oldChildren[i], newChildren[i])); } const attrsPatch diffAttrs(oldVNode.props, newVNode.props); if (attrsPatch.length 0) { patches.push({ type: UPDATE_ATTRS, attrs: attrsPatch }); } return patches.length 0 ? { type: PATCH, patches } : null; } } function isVNodeChanged(oldVNode, newVNode) { return ( typeof oldVNode ! typeof newVNode || (typeof oldVNode string oldVNode ! newVNode) || oldVNode.type ! newVNode.type ); } function diffAttrs(oldProps {}, newProps {}) { const patches []; // 找出变化的属性 for (const [key, value] of Object.entries(newProps)) { if (oldProps[key] ! value) { patches.push({ key, value }); } } // 找出被删除的属性 for (const key in oldProps) { if (!(key in newProps)) { patches.push({ key, value: null }); } } return patches; }6. 面试准备与技巧6.1 如何有效准备JavaScript面试系统复习核心概念作用域、闭包、原型链、事件循环等基础必须牢固掌握练习算法题LeetCode、Codewars等平台上的JavaScript题目手写常见功能Promise、防抖节流、深拷贝等了解框架原理虚拟DOM、响应式原理等准备项目经验能清晰描述项目中的技术挑战和解决方案6.2 面试中的沟通技巧明确问题不确定题意时主动询问确认分步解答复杂问题先给出思路再逐步实现考虑边界注意异常处理和边界条件优化思路先给出基础解法再考虑优化方案保持交流解释你的思考过程让面试官了解你的思路6.3 常见陷阱与避坑指南变量提升与暂时性死区console.log(a); // undefined var a 1; console.log(b); // ReferenceError let b 2;this指向问题const obj { name: Alice, greet: function() { console.log(Hello, ${this.name}); } }; const greet obj.greet; greet(); // Hello, undefined (this指向全局或undefined)异步与同步混淆for (var i 0; i 5; i) { setTimeout(() console.log(i), 0); } // 输出5个5而不是0,1,2,3,4浮点数精度问题0.1 0.2 0.3 // false数组去重的几种方式// 使用Set const unique arr [...new Set(arr)]; // 使用filter const unique arr arr.filter((item, index) arr.indexOf(item) index); // 使用reduce const unique arr arr.reduce((acc, cur) acc.includes(cur) ? acc : [...acc, cur], []);7. 最新JavaScript特性与趋势7.1 ES2023新特性Array.prototype.findLast/findLastIndex从数组末尾开始查找const arr [1, 2, 3, 4, 5]; arr.findLast(x x % 2 0); // 4 arr.findLastIndex(x x % 2 0); // 3Hashbang语法支持Shebang语法#!/usr/bin/env node console.log(Hello from Node.js);Symbol作为WeakMap键允许使用Symbol作为WeakMap的键const weak new WeakMap(); const key Symbol(key); weak.set(key, value);7.2 即将到来的ES2024特性Record和Tuple不可变的数据结构const record #{ x: 1, y: 2 }; const tuple #[1, 2, 3];管道操作符函数式编程风格的数据处理const result x | double | increment | square;装饰器(Decorators)元编程能力log class MyClass { readonly method() {} }7.3 JavaScript生态趋势TypeScript普及越来越多的项目采用TypeScriptWebAssembly集成高性能计算场景微前端架构大型前端应用的模块化方案Serverless前端边缘计算与无服务器架构构建工具演进Vite、esbuild等新一代工具8. 实战项目与代码质量8.1 代码规范与最佳实践命名约定变量、函数、类使用一致的命名风格模块化组织合理拆分代码避免全局污染错误处理适当的try-catch和错误边界代码注释清晰的文档注释和实现注释单元测试为关键逻辑编写测试用例8.2 性能优化实战减少重绘与回流// 不好的做法 for (let i 0; i 100; i) { element.style.left ${i}px; } // 好的做法 let left 0; function animate() { left; element.style.left ${left}px; if (left 100) { requestAnimationFrame(animate); } } animate();内存优化技巧// 避免内存泄漏 window.addEventListener(scroll, debounce(handleScroll, 100)); // 记得在适当时候移除 window.removeEventListener(scroll, debounce(handleScroll, 100));代码分割与懒加载// 动态导入 const module await import(./module.js);8.3 调试与问题排查Chrome DevTools高级用法性能分析(Performance)内存分析(Memory)网络请求分析(Network)Source Map调试// webpack配置 module.exports { devtool: source-map, // ... };错误监控与上报window.addEventListener(error, (event) { // 上报错误信息 reportError({ message: event.message, filename: event.filename, lineno: event.lineno, colno: event.colno, stack: event.error?.stack }); }); window.addEventListener(unhandledrejection, (event) { reportError({ reason: event.reason, stack: event.reason?.stack }); });9. 前端工程化与JavaScript9.1 模块化发展历程IIFE时代(function() { // 模块代码 })();CommonJS// 导出 module.exports { ... }; // 导入 const module require(./module);AMD/RequireJSdefine([dependency], function(dependency) { return { ... }; });ES Modules// 导出 export const name value; export default function() { ... }; // 导入 import { name } from ./module; import func from ./module;9.2 现代构建工具Webpack配置要点module.exports { entry: ./src/index.js, output: { filename: bundle.js, path: path.resolve(__dirname, dist) }, module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: babel-loader } ] } };Babel转译配置// .babelrc { presets: [ [babel/preset-env, { targets: { browsers: [last 2 versions] } }] ], plugins: [babel/plugin-transform-runtime] }ESLint代码检查// .eslintrc.js module.exports { env: { browser: true, es2021: true }, extends: [eslint:recommended, plugin:prettier/recommended], parserOptions: { ecmaVersion: 12, sourceType: module }, rules: { no-console: warn, no-unused-vars: error } };9.3 测试策略与工具单元测试(Jest)// sum.js function sum(a, b) { return a b; } module.exports sum; // sum.test.js const sum require(./sum); test(adds 1 2 to equal 3, () { expect(sum(1, 2)).toBe(3); });端到端测试(Cypress)describe(My First Test, () { it(Visits the app, () { cy.visit(/); cy.contains(Welcome).should(exist); }); });组件测试(Testing Library)import React from react; import { render, screen } from testing-library/react; import Button from ./Button; test(renders button with text, () { render(ButtonClick me/Button); const buttonElement screen.getByText(/click me/i); expect(buttonElement).toBeInTheDocument(); });10. JavaScript安全最佳实践10.1 常见安全漏洞与防护XSS(跨站脚本攻击)防护// 使用textContent而非innerHTML element.textContent userInput; // 使用DOMPurify清理HTML const clean DOMPurify.sanitize(userInput); element.innerHTML clean;CSRF(跨站请求伪造)防护// 服务端设置SameSite Cookie Set-Cookie: sessionabc123; SameSiteStrict // 客户端添加CSRF Token fetch(/api/data, { method: POST, headers: { Content-Type: application/json, X-CSRF-Token: getCSRFToken() }, body: JSON.stringify(data) });JSON注入防护// 避免直接eval JSON const data JSON.parse(jsonString); // 设置正确的Content-Type res.setHeader(Content-Type, application/json); res.end(JSON.stringify(data));10.2 安全编码实践避免使用eval// 不好的做法 eval(var x userInput); // 好的做法 const x JSON.parse(userInput);严格内容安全策略(CSP)meta http-equivContent-Security-Policy contentdefault-src self; script-src self unsafe-inline安全的第三方库使用// 定期检查依赖漏洞 npm audit // 使用固定版本号 dependencies: { lodash: 4.17.21 }10.3 数据保护与隐私敏感数据处理// 避免在客户端存储敏感数据 localStorage.removeItem(token); // 使用HttpOnly Cookie存储会话标识 Set-Cookie: tokenabc123; HttpOnly; Secure; SameSiteStrict密码安全处理// 使用bcrypt等库哈希密码 const bcrypt require(bcrypt); const saltRounds 10; const hashedPassword await bcrypt.hash(password, saltRounds); // 验证密码 const match await bcrypt.compare(inputPassword, hashedPassword);加密通信// 强制HTTPS if (location.protocol ! https:) { location.href https: location.href.substring(location.protocol.length); }