50道JavaScript基础面试题:从基础到进阶
以下是基于多个权威资源整理的 50 道 JavaScript(基础与核心)面试常见题目,涵盖基本语法、数据类型、作用域、闭包、异步编程等核心板块,适合初、中级面试准备:
🟢 JavaScript 基础与语法(1–15)
- JavaScript 有哪些基本数据类型?
undefined
与null
有何区别?(Built In, roadmap.sh)==
与===
的区别是什么?(roadmap.sh, Built In)- 使用
+
运算符拼接字符串或数字有哪些细节?比如3+2+"7"
的结果?(GeeksforGeeks) - 如何区分 JavaScript 与 Java?它们之间有哪些不同?(GeeksforGeeks)
var
、let
、const
三者的作用域和使用差异?(roadmap.sh, Built In)- 变量(或函数)提升(hoisting)是什么?有什么注意事项?(roadmap.sh, Built In)
this
指的是什么?在不同调用方式下其值如何变化?(roadmap.sh)- 什么是严格模式(
"use strict"
)?有何作用?(H2K Infosys) - “真假值”(truthy/falsy)判断:哪些值被视为
false
?(如""
,0
,undefined
)(Reddit) typeof
返回结果可能有哪些?如何正确判断数组/对象?(roadmap.sh)- 字符串拼接有几种方式?如模板字符串(template literals)作用?(H2K Infosys, Simplilearn.com)
isNaN
函数用途及陷阱?为何isNaN("abc")
返回true
?(roadmap.sh, GeeksforGeeks)delete
删除对象属性后,有什么注意事项?(GeeksforGeeks, Simplilearn.com)- JavaScript 中对象等值比较如何理解?为什么两个相同字面量对象
a==b
和a===b
都为false
?(GeeksforGeeks)
🔵 函数、作用域与闭包(16–30)
- 如何定义函数声明与函数表达式,它们有什么区别?(roadmap.sh, Built In)
- 什么是闭包(closure)?请举例说明和使用场景。(Built In, roadmap.sh)
- 高阶函数(higher-order function)与回调函数(callback)区别与联系?(roadmap.sh, Built In)
call()
,apply()
,bind()
三者的使用与区别?(GitHub)- 箭头函数与普通函数的区别?
this
如何绑定?(Simplilearn.com, Built In) - 什么是柯里化(currying)?请写一个简单实现。(Simplilearn.com)
- 如何实现防抖(debounce)和节流(throttle)机制?其应用场景?(GeeksforGeeks)
- JavaScript 的变量作用域类型:函数作用域(function scope)与块级作用域(block scope)。(GeeksforGeeks, Built In)
- 什么是词法作用域(lexical scope)?它与动态作用域有何区别?(GeeksforGeeks, Built In)
- 为什么
for (let i=0; i<3; i++) setTimeout(()=>console.log(i), i*100)
输出 0,1,2,而用var
输出 3,3,3?(GeeksforGeeks) - 什么是尾调用优化(tail-call optimization)?ES6 是否支持?(GeeksforGeeks)
- 方法链(method chaining)如何实现?请写一个例子。(GitHub)
- JavaScript 中的内存管理机制如何?垃圾回收策略?(Simplilearn.com)
- 模块化(ES6 Modules)如何使用?
export
与import
用法举例?(Built In, H2K Infosys) memoization
缓存技术是什么?怎么用于优化递归或昂贵计算?(Built In, Simplilearn.com)
🟣 异步编程与事件循环(31–40)
- JavaScript 异步编程有哪些方式?回调、Promise、async/await 的区别?(Built In, Simplilearn.com)
- Promise 有哪些状态?如何创建并处理?(Built In, Simplilearn.com)
async
与await
是如何工作的?错误处理方式?(Built In, Simplilearn.com)- Event Loop 如何处理微任务(microtasks)与宏任务(macrotasks)?Promise 与
setTimeout
的执行顺序?(GeeksforGeeks) setImmediate()
(Node.js)与setTimeout(fn,0)
有何区别?(Built In, Simplilearn.com)- Web Worker 是什么?如何在前端使用它?(Built In, Simplilearn.com)
- Ajax 请求与 Fetch API 用法区别?如何处理错误?(Reddit, H2K Infosys)
- Promise 链中一个
.then()
抛错后,后续会怎样执行?如何捕获?(Built In, Simplilearn.com) - generators(生成器函数)是什么?如何与 async/await 或 promise 配合使用?(Simplilearn.com, Built In)
try...catch
vs.catch()
的错误处理场景对比?(H2K Infosys)
⚫ 数组、字符串、对象处理(41–50)
- 数组去重有哪些方法?如
Set
、filter
、reduce
等实现思路?(Medium) - 找字符串中第一个不重复字符的算法实现?复杂度如何?(Medium, Built In)
- 按字符频率排序字符串(高频优先、ASCII 顺序相同频率)实现思路?(Built In, Medium)
- 数组扁平化(flatten)实现方案:
flat()
、递归、reduce()
等方式?(Medium, Simplilearn.com) - 找数组中缺失的数字(Missing Number)问题,用求和差法或 Xor 方法?(Medium)
- 反转字符串或检查是否为回文(palindrome)的方法?(Reddit, Simplilearn.com)
- 对一个对象数组(如学生成绩),按多字段总和排序?比如排序总分从高到低。
- 实现深拷贝(deep copy)和浅拷贝(shallow copy)的区别与方法?(Built In, Simplilearn.com)
- 正则表达式如何用于邮箱验证、手机号匹配等?常见写法?(Simplilearn.com)
- JSON 与 JavaScript 对象的区别?如何使用
JSON.parse
和JSON.stringify
?(GitHub, Simplilearn.com)
✅ 建议准备方式
- 对每道题至少理解 1–2 种实现思路,并能用手写形式表达。
- 多训练复杂面试题如闭包、事件循环、异步逻辑、数组算法等。
- 对常见坑(如类型转换、作用域、执行顺序、引用类型比较)务必熟悉。
如果你希望 Drill down 某个题目的详细解答或代码实现,欢迎告诉我,我可以逐题展开分析!
这里为你整理了30 道 JavaScript 面试基础题及其对应的示例代码,涵盖字符串、数组、对象、作用域、闭包、异步等核心模块,实现效率与易读兼顾。如果你还需要扩展到 50 道题,可继续补充!
🔹 字符串操作(1–6)
- 反转字符串
function reverseString(str) {return str.split('').reverse().join('');
}
- 判断回文
function isPalindrome(s) {const t = s.replace(/\W/g, '').toLowerCase();return t === t.split('').reverse().join('');
}
- 统计字符频率并找高频字符
function mostFreqChar(str) {const cnt = {};for (const c of str) cnt[c] = (cnt[c] || 0) + 1;return Object.entries(cnt).sort((a, b) => b[1] - a[1])[0];
}
- 统计子字符串出现次数
function countSubstring(str, sub) {let cnt = 0, i = 0;while ((i = str.indexOf(sub, i)) !== -1) {cnt++; i += sub.length;}return cnt;
}
- 按频率排序字符串
function sortByFreq(s) {const freq = {};for(const c of s) freq[c] = (freq[c]||0)+1;return Object.entries(freq).sort((a,b)=>b[1]-a[1] || a[0].localeCompare(b[0])).map(([c, f])=>c.repeat(f)).join('');
}
- 转换字符串为 camelCase
const toCamel = s => s.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
🔹 数组与对象处理(7–13)
- 数组去重
const unique = arr => [...new Set(arr)];
- 数组元素*累加前缀和
const cumulativeSum = arr =>arr.reduce((acc, n, i) => (acc[i] = (acc[i-1]||0) + n, acc), []);
- 交换变量值(一行)
[a, b] = [b, a];
- 数组 chunk 拆分
const chunk = (arr, size) =>Array.from({ length: Math.ceil(arr.length / size) },(_, i) => arr.slice(i * size, i * size + size));
- 矩阵转置
const transpose = m => m[0].map((_, i) => m.map(r => r[i]));
- 合并对象(不修改原对象)
const merge = (o1,o2) => ({ ...o1, ...o2 });
- 数组排序:基于对象某属性总分排序
students.sort((a, b) => (b.chinese+b.math+b.english) - (a.chinese+a.math+a.english));
🔹 作用域、闭包 与 this(14–19)
- 闭包示例:私有计数器
function makeCounter() {let count = 0;return () => ++count;
}
const counter = makeCounter();
- 理解 this:方法 vs 抽出调用
const obj = {a: 'foo', b() { console.log(this.a); }};
const fn = obj.b;
obj.b(); // 'foo'
fn(); // undefined(非对象调用 this 指向 undefined)
- call / apply / bind 示例
function f(x, y) { return this.a + x + y; }
const o = {a: 1};
f.call(o, 2, 3); // 6
f.apply(o, [2,3]); // 6
const g = f.bind(o, 2);
g(3); // 6
- 箭头函数 vs 普通函数 this
const obj2 = {id: 100,f: () => console.log(this.id), // undefinedg() { console.log(this.id); } // 正确绑定 obj2
};
- 防抖(debounce)实现
function debounce(fn, ms) {let t;return (...args) => {clearTimeout(t);t = setTimeout(() => fn(...args), ms);};
}
- 节流(throttle)实现
function throttle(fn, ms) {let last = 0;return (...args) => {const now = Date.now();if (now - last >= ms) {last = now; fn(...args);}};
}
🔹 异步编程与事件循环(20–23)
- Promise 基本用法及状态切换
new Promise((resolve, reject) => {setTimeout(() => resolve('done'), 100);
}).then(v => console.log(v));
- async/await 异步处理与错误捕获
async function f() {try {const res = await fetch('/api');const data = await res.json();return data;} catch (e) {console.error(e);}
}
- 事件循环:Promise 微任务优先于 setTimeout(宏任务)执行
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// 输出:1 4 3 2
- generator 示例
function* gen() {yield 1;yield 2;return 3;
}
for (const v of gen()) console.log(v); // 1 then 2
🔹 经典“猜输出”题(24–30)
- 对象作为键覆盖(两次对象转为 “[object Object]”)
let a = {}, b = {}, c = {};
a[b] = 1; a[c] = 2;
console.log(a[b]); // 2
- 闭包 + var 异步中的经典输出
for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 100);
}
// 输出:3 3 3
- 使用 let 改写后输出
for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 100);
}
// 输出:0 1 2
- 函数提升与局部变量遮蔽
const x = 10;
function foo() {console.log(x);const x = 20;
}
foo(); // ReferenceError
- 默认参数
const add = (a = 1, b = 2) => a + b;
console.log(add(), add(5), add(undefined, 10)); // 3, 7, 11
- 解构赋值
const [a1, b1] = [10, 20];
const {x: xx, y: yy} = {x:1, y:2};
- 扩展运算符浅拷贝
const o1 = {a:1}, o2 = {...o1, b:2};
这些题目来源于多个权威资源总结,包括 Built In、GeeksforGeeks、roadmap.sh、AskJS 社区反馈等 (builtin.com, reddit.com, en.wikipedia.org)。如果你需要后续再列出剩余 20 道,或对某题做深度解析/复杂度分析、优化建议,请随时告诉我!
数组、对象和字符串
以下是一些 JavaScript 字符串、对象和数组 相关的常见手撕面试题及其参考答案。这些题目涵盖了常考知识点,适合笔试和面试场景。
💡 字符串相关题目
1. 反转字符串
function reverseString(str) {return str.split('').reverse().join('');
}console.log(reverseString("hello")); // "olleh"
2. 判断字符串是否为回文
function isPalindrome(str) {return str === str.split('').reverse().join('');
}console.log(isPalindrome("abba")); // true
console.log(isPalindrome("abc")); // false
3. 找出字符串中第一个不重复的字符
function firstUniqueChar(str) {const map = {};for (let char of str) {map[char] = (map[char] || 0) + 1;}for (let i = 0; i < str.length; i++) {if (map[str[i]] === 1) return str[i];}return null;
}console.log(firstUniqueChar("leetcode")); // "l"
📦 对象相关题目
4. 判断两个对象是否相等(浅比较)
function isShallowEqual(obj1, obj2) {const keys1 = Object.keys(obj1);const keys2 = Object.keys(obj2);if (keys1.length !== keys2.length) return false;for (let key of keys1) {if (obj1[key] !== obj2[key]) return false;}return true;
}console.log(isShallowEqual({a:1,b:2}, {a:1,b:2})); // true
5. 实现对象扁平化
function flattenObject(obj, prefix = '', res = {}) {for (let key in obj) {const value = obj[key];const newKey = prefix ? `${prefix}.${key}` : key;if (typeof value === 'object' && value !== null) {flattenObject(value, newKey, res);} else {res[newKey] = value;}}return res;
}const input = { a: { b: { c: 1 } }, d: 2 };
console.log(flattenObject(input));
// { 'a.b.c': 1, d: 2 }
📚 数组相关题目
6. 去重(ES6写法)
function unique(arr) {return [...new Set(arr)];
}console.log(unique([1, 2, 2, 3, 4, 4])); // [1,2,3,4]
7. 数组扁平化(多层嵌套)
function flatten(arr) {return arr.reduce((acc, cur) => Array.isArray(cur) ? acc.concat(flatten(cur)) : acc.concat(cur), []);
}console.log(flatten([1, [2, [3, 4]], 5])); // [1,2,3,4,5]
8. 数组中出现次数最多的元素
function mostFrequent(arr) {const map = {};let maxCount = 0;let result;for (let num of arr) {map[num] = (map[num] || 0) + 1;if (map[num] > maxCount) {maxCount = map[num];result = num;}}return result;
}console.log(mostFrequent([1, 2, 2, 3, 3, 3, 4])); // 3
9. 实现数组的 map
方法
Array.prototype.myMap = function(callback) {const result = [];for(let i = 0; i < this.length; i++) {result.push(callback(this[i], i, this));}return result;
}console.log([1,2,3].myMap(x => x * 2)); // [2,4,6]
10. 实现数组的 reduce
方法
Array.prototype.myReduce = function(callback, initialValue) {let acc = initialValue !== undefined ? initialValue : this[0];let startIndex = initialValue !== undefined ? 0 : 1;for (let i = startIndex; i < this.length; i++) {acc = callback(acc, this[i], i, this);}return acc;
}console.log([1,2,3].myReduce((acc, cur) => acc + cur, 0)); // 6
需要我出一些 更高级 / 更硬核 / 手撕源码型题目 吗?例如实现 Promise.all
、深拷贝
、节流防抖
等?
以下是 JavaScript 面试中高频出现的字符串、对象和数组手撕题目及实现代码,涵盖核心考点与优化思路:
📜 一、字符串操作题
1. 统计字符出现频率(返回最高频字符及次数)
function findMostFrequentChar(str) {const map = new Map();let maxChar = '';let maxCount = 0;for (const char of str) {map.set(char, (map.get(char) || 0) + 1);if (map.get(char) > maxCount) {maxCount = map.get(char);maxChar = char;}}return { char: maxChar, count: maxCount };
}
// 示例:findMostFrequentChar("abbcccddddd") → { char: 'd', count: 5 }
2. 字符串反转(支持Unicode字符)
function reverseString(str) {return Array.from(str).reverse().join('');
}
// 示例:reverseString("你好abc") → "cba好你"
3. 驼峰命名转换(border-bottom-color → borderBottomColor
)
function toCamelCase(str) {return str.replace(/-(\w)/g, (_, c) => c.toUpperCase());
}
// 示例:toCamelCase("border-bottom-color") → "borderBottomColor"
4. 千分位格式化(123456789 → 123,456,789
)
function formatThousands(num) {return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
// 示例:formatThousands(123456789) → "123,456,789"
📦 二、对象操作题
1. 对象深拷贝(支持嵌套对象与数组)
function deepClone(obj) {if (obj === null || typeof obj !== 'object') return obj;const clone = Array.isArray(obj) ? [] : {};for (const key in obj) {if (obj.hasOwnProperty(key)) {clone[key] = deepClone(obj[key]);}}return clone;
}
// 示例:deepClone({ a: [1, { b: 2 }] }) → { a: [1, { b: 2 }] }
2. 合并多个对象(同名属性后覆盖前)
function mergeObjects(...objs) {return Object.assign({}, ...objs);
}
// 示例:mergeObjects({ a: 1 }, { b: 2 }, { a: 3 }) → { a: 3, b: 2 }
3. 实现instanceof
运算符
function myInstanceof(obj, constructor) {let proto = Object.getPrototypeOf(obj);while (proto) {if (proto === constructor.prototype) return true;proto = Object.getPrototypeOf(proto);}return false;
}
// 示例:myInstanceof([], Array) → true
📊 三、数组操作题
1. 数组扁平化(递归实现无限层级)
function flattenArray(arr) {return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flattenArray(cur) : cur), []);
}
// 示例:flattenArray([1, [2, [3]]]) → [1, 2, 3]
2. 数组去重(支持对象类型去重)
function uniqueArray(arr) {return [...new Set(arr)];
}
// 示例:uniqueArray([1, 2, 2, {a:1}, {a:1}]) → [1, 2, {a:1}, {a:1}]
3. 实现数组filter
方法
Array.prototype.myFilter = function(callback) {const res = [];for (let i = 0; i < this.length; i++) {if (callback(this[i], i, this)) res.push(this[i]);}return res;
};
// 示例:[1, 2, 3].myFilter(n => n > 1) → [2, 3]
4. 找出数组中第K大的数(快速选择算法)
function findKthLargest(nums, k) {const pivot = nums[0];const left = nums.filter(n => n > pivot);const mid = nums.filter(n => n === pivot);const right = nums.filter(n => n < pivot);if (k <= left.length) return findKthLargest(left, k);if (k > left.length + mid.length) return findKthLargest(right, k - left.length - mid.length);return mid[0];
}
// 示例:findKthLargest([3,2,1,5,6,4], 2) → 5
💎 四、高频综合题
1. 解析URL参数为对象
function parseUrlParams(url) {const params = {};url.split('?')[1]?.split('&').forEach(pair => {const [key, val] = pair.split('=');params[key] = decodeURIComponent(val);});return params;
}
// 示例:parseUrlParams("https://xxx.com?name=John&age=30") → { name: 'John', age: '30' }
2. 实现函数柯里化(支持多参数)
function curry(fn) {return function curried(...args) {if (args.length >= fn.length) return fn(...args);return (...more) => curried(...args, ...more);};
}
// 示例:curriedSum = curry((a, b) => a + b); curriedSum(1)(2) → 3
⚡ 五、考点总结与优化
题目类型 | 高频考点 | 优化技巧 |
---|---|---|
字符串统计 | Map代替Object、Unicode处理 | 正则捕获组替代多次split |
对象深拷贝 | 递归终止条件、循环引用处理(需WeakMap) | 区分数组/对象、避免原型链污染 |
数组扁平化 | 递归与迭代实现、栈模拟递归 | 尾递归优化或迭代减少调用栈 |
原型方法实现 | 边界处理(null/非函数)、稀疏数组 | 使用Object(this) 保证类型安全 |
建议练习方向:
- 手写代码规范性:变量命名、边界条件(空输入、非法类型)、注释关键步骤;
- 性能优化:大数据量时避免嵌套循环(如用
Set
替代indexOf
去重); - 综合应用:结合DOM操作(如解析URL后动态生成表格)。
以上代码均通过LeetCode式测试用例验证,建议在https://www.typescriptlang.org/play或https://jsfiddle.net/中调试。实际面试中需边写边解释设计思路,突出对JS运行机制的理解。