以下是基于多个权威资源整理的 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)
- 反转字符串
js
function reverseString(str) {
return str.split('').reverse().join('');
}
- 判断回文
js
function isPalindrome(s) {
const t = s.replace(/\W/g, '').toLowerCase();
return t === t.split('').reverse().join('');
}
- 统计字符频率并找高频字符
js
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];
}
- 统计子字符串出现次数
js
function countSubstring(str, sub) {
let cnt = 0, i = 0;
while ((i = str.indexOf(sub, i)) !== -1) {
cnt++; i += sub.length;
}
return cnt;
}
- 按频率排序字符串
js
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
js
const toCamel = s => s.replace(/[-_](\w)/g, (_, c) => c.toUpperCase());
🔹 数组与对象处理(7--13)
- 数组去重
js
const unique = arr => [...new Set(arr)];
- 数组元素*累加前缀和
js
const cumulativeSum = arr =>
arr.reduce((acc, n, i) => (acc[i] = (acc[i-1]||0) + n, acc), []);
- 交换变量值(一行)
js
[a, b] = [b, a];
- 数组 chunk 拆分
js
const chunk = (arr, size) =>
Array.from({ length: Math.ceil(arr.length / size) },
(_, i) => arr.slice(i * size, i * size + size));
- 矩阵转置
js
const transpose = m => m[0].map((_, i) => m.map(r => r[i]));
- 合并对象(不修改原对象)
js
const merge = (o1,o2) => ({ ...o1, ...o2 });
- 数组排序:基于对象某属性总分排序
js
students.sort((a, b) => (b.chinese+b.math+b.english) - (a.chinese+a.math+a.english));
🔹 作用域、闭包 与 this(14--19)
- 闭包示例:私有计数器
js
function makeCounter() {
let count = 0;
return () => ++count;
}
const counter = makeCounter();
- 理解 this:方法 vs 抽出调用
js
const obj = {a: 'foo', b() { console.log(this.a); }};
const fn = obj.b;
obj.b(); // 'foo'
fn(); // undefined(非对象调用 this 指向 undefined)
- call / apply / bind 示例
js
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
js
const obj2 = {
id: 100,
f: () => console.log(this.id), // undefined
g() { console.log(this.id); } // 正确绑定 obj2
};
- 防抖(debounce)实现
js
function debounce(fn, ms) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
- 节流(throttle)实现
js
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now; fn(...args);
}
};
}
🔹 异步编程与事件循环(20--23)
- Promise 基本用法及状态切换
js
new Promise((resolve, reject) => {
setTimeout(() => resolve('done'), 100);
}).then(v => console.log(v));
- async/await 异步处理与错误捕获
js
async function f() {
try {
const res = await fetch('/api');
const data = await res.json();
return data;
} catch (e) {
console.error(e);
}
}
- 事件循环:Promise 微任务优先于 setTimeout(宏任务)执行
js
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4);
// 输出:1 4 3 2
- generator 示例
js
function* gen() {
yield 1;
yield 2;
return 3;
}
for (const v of gen()) console.log(v); // 1 then 2
🔹 经典"猜输出"题(24--30)
- 对象作为键覆盖(两次对象转为 "[object Object]")
js
let a = {}, b = {}, c = {};
a[b] = 1; a[c] = 2;
console.log(a[b]); // 2
- 闭包 + var 异步中的经典输出
js
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出:3 3 3
- 使用 let 改写后输出
js
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// 输出:0 1 2
- 函数提升与局部变量遮蔽
js
const x = 10;
function foo() {
console.log(x);
const x = 20;
}
foo(); // ReferenceError
- 默认参数
js
const add = (a = 1, b = 2) => a + b;
console.log(add(), add(5), add(undefined, 10)); // 3, 7, 11
- 解构赋值
js
const [a1, b1] = [10, 20];
const {x: xx, y: yy} = {x:1, y:2};
- 扩展运算符浅拷贝
js
const o1 = {a:1}, o2 = {...o1, b:2};
这些题目来源于多个权威资源总结,包括 Built In、GeeksforGeeks、roadmap.sh、AskJS 社区反馈等 (builtin.com, reddit.com, en.wikipedia.org)。如果你需要后续再列出剩余 20 道,或对某题做深度解析/复杂度分析、优化建议,请随时告诉我!
数组、对象和字符串
以下是一些 JavaScript 字符串、对象和数组 相关的常见手撕面试题及其参考答案。这些题目涵盖了常考知识点,适合笔试和面试场景。
💡 字符串相关题目
1. 反转字符串
js
function reverseString(str) {
return str.split('').reverse().join('');
}
console.log(reverseString("hello")); // "olleh"
2. 判断字符串是否为回文
js
function isPalindrome(str) {
return str === str.split('').reverse().join('');
}
console.log(isPalindrome("abba")); // true
console.log(isPalindrome("abc")); // false
3. 找出字符串中第一个不重复的字符
js
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. 判断两个对象是否相等(浅比较)
js
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. 实现对象扁平化
js
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写法)
js
function unique(arr) {
return [...new Set(arr)];
}
console.log(unique([1, 2, 2, 3, 4, 4])); // [1,2,3,4]
7. 数组扁平化(多层嵌套)
js
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. 数组中出现次数最多的元素
js
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
方法
js
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
方法
js
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. 统计字符出现频率(返回最高频字符及次数)
javascript
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字符)
javascript
function reverseString(str) {
return Array.from(str).reverse().join('');
}
// 示例:reverseString("你好abc") → "cba好你"
3. 驼峰命名转换(border-bottom-color → borderBottomColor
)
javascript
function toCamelCase(str) {
return str.replace(/-(\w)/g, (_, c) => c.toUpperCase());
}
// 示例:toCamelCase("border-bottom-color") → "borderBottomColor"
4. 千分位格式化(123456789 → 123,456,789
)
javascript
function formatThousands(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
// 示例:formatThousands(123456789) → "123,456,789"
📦 二、对象操作题
1. 对象深拷贝(支持嵌套对象与数组)
javascript
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. 合并多个对象(同名属性后覆盖前)
javascript
function mergeObjects(...objs) {
return Object.assign({}, ...objs);
}
// 示例:mergeObjects({ a: 1 }, { b: 2 }, { a: 3 }) → { a: 3, b: 2 }
3. 实现instanceof
运算符
javascript
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. 数组扁平化(递归实现无限层级)
javascript
function flattenArray(arr) {
return arr.reduce((acc, cur) =>
acc.concat(Array.isArray(cur) ? flattenArray(cur) : cur)
, []);
}
// 示例:flattenArray([1, [2, [3]]]) → [1, 2, 3]
2. 数组去重(支持对象类型去重)
javascript
function uniqueArray(arr) {
return [...new Set(arr)];
}
// 示例:uniqueArray([1, 2, 2, {a:1}, {a:1}]) → [1, 2, {a:1}, {a:1}]
3. 实现数组filter
方法
javascript
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大的数(快速选择算法)
javascript
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参数为对象
javascript
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. 实现函数柯里化(支持多参数)
javascript
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运行机制的理解。