Javascript面试题及详细答案150道之(106-120)
《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目录
- 一、本文面试题目录
- 106. 如何在JavaScript中实现一个简单的哈希表(Hash Table)?
- 107. JavaScript中`String.prototype.codePointAt()`和`charCodeAt()`的区别是什么?
- 108. 如何在JavaScript中实现一个简单的图(Graph)数据结构?
- 109. JavaScript中`Array.prototype.some()`和`Array.prototype.every()`的区别是什么?
- 110. 如何在JavaScript中实现一个简单的状态机(State Machine)?
- 111. JavaScript中`Intl`对象的作用是什么?
- 112. 如何在JavaScript中实现一个简单的LRU缓存(Least Recently Used Cache)?
- 113. JavaScript中`String.prototype.replaceAll()`和`replace()`的区别是什么?
- 114. 如何在JavaScript中实现一个简单的WebSocket客户端?
- 115. JavaScript中`Array.prototype.reduceRight()`的作用是什么?
- 116. 如何在JavaScript中实现一个简单的发布-订阅模式?
- 117. JavaScript中`Object.is()`和`===`的区别是什么?
- 118. 如何在JavaScript中实现一个简单的防抖函数(Debounce)?
- 119. JavaScript中`async`函数和普通函数的区别是什么?
- 120. 如何在JavaScript中实现一个简单的节流函数(Throttle)?
- 二、150道面试题目录列表
一、本文面试题目录
106. 如何在JavaScript中实现一个简单的哈希表(Hash Table)?
哈希表实现:
使用数组和哈希函数,处理冲突采用链地址法(每个槽位存储链表)。
代码示例:
class HashTable {constructor(size = 53) {this.keyMap = new Array(size);}// 哈希函数(将键转换为数组索引)_hash(key) {let total = 0;const WEIRD_PRIME = 31;for (let i = 0; i < Math.min(key.length, 100); i++) {const char = key[i];const value = char.charCodeAt(0) - 96;total = (total * WEIRD_PRIME + value) % this.keyMap.length;}return total;}// 设置键值对set(key, value) {const index = this._hash(key);if (!this.keyMap[index]) {this.keyMap[index] = [];}// 处理键已存在的情况(更新值)for (let i = 0; i < this.keyMap[index].length; i++) {if (this.keyMap[index][i][0] === key) {this.keyMap[index][i][1] = value;return;}}this.keyMap[index].push([key, value]);}// 获取键对应的值get(key) {const index = this._hash(key);if (this.keyMap[index]) {for (let i = 0; i < this.keyMap[index].length; i++) {if (this.keyMap[index][i][0] === key) {return this.keyMap[index][i][1];}}}return undefined;}// 获取所有键keys() {const keysArr = [];for (let i = 0; i < this.keyMap.length; i++) {if (this.keyMap[i]) {for (let j = 0; j < this.keyMap[i].length; j++) {if (!keysArr.includes(this.keyMap[i][j][0])) {keysArr.push(this.keyMap[i][j][0]);}}}}return keysArr;}
}// 使用示例
const ht = new HashTable(3);
ht.set('apple', 10);
ht.set('banana', 20);
ht.set('cherry', 30);
console.log(ht.get('banana')); // 20
console.log(ht.keys()); // ["apple", "banana", "cherry"]
107. JavaScript中String.prototype.codePointAt()
和charCodeAt()
的区别是什么?
核心区别:
方法 | 返回值范围 | 处理Unicode补充字符 | 示例("😀".codePointAt(0) vs "😀".charCodeAt(0) ) |
---|---|---|---|
codePointAt(index) | 完整码点值(0~0x10FFFF) | ✅ | 128512 (对应U+1F600 ) |
charCodeAt(index) | UTF-16编码单元值(0~65535) | ❌(需两个代码单元表示补充字符) | 55357 (高代理部分,需结合charCodeAt(1) 获取完整值) |
代码示例:
const str = "😀"; // Unicode补充字符(U+1F600)// codePointAt()
console.log(str.codePointAt(0)); // 128512(完整码点值)
console.log(str.codePointAt(1)); // 56832(低代理部分的值)// charCodeAt()
console.log(str.charCodeAt(0)); // 55357(高代理部分)
console.log(str.charCodeAt(1)); // 56832(低代理部分)// 判断是否为高代理部分
function isHighSurrogate(charCode) {return charCode >= 0xD800 && charCode <= 0xDBFF;
}// 判断是否为补充字符
function isSupplementaryCodePoint(codePoint) {return codePoint > 0xFFFF;
}
应用场景:
处理包含表情符号、罕见字符等补充字符的字符串时,需使用codePointAt()
确保获取完整码点值。
108. 如何在JavaScript中实现一个简单的图(Graph)数据结构?
图的实现:
使用邻接表表示,支持添加顶点、添加边、遍历等操作。
代码示例:
class Graph {constructor() {this.adjacencyList = {};}// 添加顶点addVertex(vertex) {if (!this.adjacencyList[vertex]) {this.adjacencyList[vertex] = [];}}// 添加边(无向图)addEdge(vertex1, vertex2) {this.adjacencyList[vertex1].push(vertex2);this.adjacencyList[vertex2].push(vertex1);}// 移除边removeEdge(vertex1, vertex2) {this.adjacencyList[vertex1] = this.adjacencyList[vertex1].filter(v => v!== vertex2);this.adjacencyList[vertex2] = this.adjacencyList[vertex2].filter(v => v!== vertex1);}// 移除顶点removeVertex(vertex) {while (this.adjacencyList[vertex].length) {const adjacentVertex = this.adjacencyList[vertex].pop();this.removeEdge(vertex, adjacentVertex);}delete this.adjacencyList[vertex];}// 深度优先遍历(递归)depthFirstRecursive(start) {const result = [];const visited = {};const adjacencyList = this.adjacencyList;(function dfs(vertex) {if (!vertex) return null;visited[vertex] = true;result.push(vertex);adjacencyList[vertex].forEach(neighbor => {if (!visited[neighbor]) {return dfs(neighbor);}});})(start);return result;}
}// 使用示例
const graph = new Graph();
graph.addVertex('A');
graph.addVertex('B');
graph.addVertex('C');
graph.addEdge('A', 'B');
graph.addEdge('A', 'C');
console.log(graph.depthFirstRecursive('A')); // ["A", "B", "C"]
109. JavaScript中Array.prototype.some()
和Array.prototype.every()
的区别是什么?
核心区别:
方法 | 判断逻辑 | 返回值 | 终止条件 |
---|---|---|---|
some(callback) | 至少有一个元素满足条件 | true 或false | 找到第一个满足条件的元素 |
every(callback) | 所有元素都满足条件 | true 或false | 找到第一个不满足条件的元素 |
代码示例:
const numbers = [1, 3, 5, 7, 8];// some()
console.log(numbers.some(num => num % 2 === 0)); // true(存在偶数8)// every()
console.log(numbers.every(num => num % 2 === 0)); // false(并非所有都是偶数)
console.log(numbers.every(num => num < 10)); // true(所有元素都小于10)// 终止条件演示
const arr = [2, 4, 5, 6];
arr.some(num => {console.log(num); // 输出2后终止(找到偶数)return num % 2 === 0;
});arr.every(num => {console.log(num); // 输出2、4、5后终止(找到奇数)return num % 2 === 0;
});
110. 如何在JavaScript中实现一个简单的状态机(State Machine)?
状态机实现:
定义状态、转换规则和动作,支持状态转移和事件处理。
代码示例:
class StateMachine {constructor(initialState, states) {this.currentState = initialState;this.states = states;}// 触发事件,转移到下一个状态transition(event) {const currentStateConfig = this.states[this.currentState];const nextState = currentStateConfig.transitions[event];if (!nextState) {console.error(`Invalid event "${event}" for state "${this.currentState}"`);return;}// 执行退出动作if (currentStateConfig.onExit) {currentStateConfig.onExit(event);}// 转移到新状态this.currentState = nextState;// 执行进入动作const newStateConfig = this.states[this.currentState];if (newStateConfig.onEnter) {newStateConfig.onEnter(event);}return this.currentState;}// 获取当前状态getState() {return this.currentState;}
}// 使用示例
const trafficLightStates = {green: {transitions: {timer: 'yellow'},onEnter: () => console.log('绿灯亮起'),onExit: () => console.log('绿灯熄灭')},yellow: {transitions: {timer: 'red'},onEnter: () => console.log('黄灯亮起'),onExit: () => console.log('黄灯熄灭')},red: {transitions: {timer: 'green'},onEnter: () => console.log('红灯亮起'),onExit: () => console.log('红灯熄灭')}
};const trafficLight = new StateMachine('green', trafficLightStates);
trafficLight.transition('timer'); // 绿灯 → 黄灯
trafficLight.transition('timer'); // 黄灯 → 红灯
111. JavaScript中Intl
对象的作用是什么?
Intl对象:
ES6引入的国际化API,提供语言敏感的字符串比较、数字格式化和日期时间格式化。
核心功能:
-
字符串比较:
const str1 = 'ä'; const str2 = 'z';// 按英语排序(ä在z后) const enCollator = new Intl.Collator('en'); console.log(enCollator.compare(str1, str2)); // 1(大于)// 按德语排序(ä在a后,z前) const deCollator = new Intl.Collator('de'); console.log(deCollator.compare(str1, str2)); // -1(小于)
-
数字格式化:
const num = 1234567.89;// 美国格式 const usFormatter = new Intl.NumberFormat('en-US'); console.log(usFormatter.format(num)); // "1,234,567.89"// 德国格式 const deFormatter = new Intl.NumberFormat('de-DE'); console.log(deFormatter.format(num)); // "1.234.567,89"// 货币格式化 const currencyFormatter = new Intl.NumberFormat('zh-CN', {style: 'currency',currency: 'CNY' }); console.log(currencyFormatter.format(1000)); // "¥1,000.00"
-
日期时间格式化:
const date = new Date();// 美国格式 const usDateFormatter = new Intl.DateTimeFormat('en-US'); console.log(usDateFormatter.format(date)); // "7/21/2025"// 日本格式 const jpDateFormatter = new Intl.DateTimeFormat('ja-JP'); console.log(jpDateFormatter.format(date)); // "2025/7/21"// 自定义格式 const customFormatter = new Intl.DateTimeFormat('zh-CN', {year: 'numeric',month: 'long',day: 'numeric',hour: '2-digit',minute: '2-digit' }); console.log(customFormatter.format(date)); // "2025年7月21日 14:30"
112. 如何在JavaScript中实现一个简单的LRU缓存(Least Recently Used Cache)?
LRU缓存实现:
使用Map(保持插入顺序)实现,最近访问的元素移到末尾,容量满时删除头部元素。
代码示例:
class LRUCache {constructor(capacity) {this.capacity = capacity;this.cache = new Map();}// 获取缓存项get(key) {if (!this.cache.has(key)) return -1;// 将访问的元素移到Map末尾(表示最近使用)const value = this.cache.get(key);this.cache.delete(key);this.cache.set(key, value);return value;}// 添加缓存项put(key, value) {// 如果键已存在,先删除if (this.cache.has(key)) {this.cache.delete(key);} // 添加新项到Map末尾this.cache.set(key, value);// 如果超过容量,删除最久未使用的项(Map头部)if (this.cache.size > this.capacity) {this.cache.delete(this.cache.keys().next().value);}}
}// 使用示例
const cache = new LRUCache(2);
cache.put(1, 'one');
cache.put(2, 'two');
console.log(cache.get(1)); // "one"
cache.put(3, 'three'); // 容量满,删除最久未使用的2
console.log(cache.get(2)); // -1(未找到)
原理:
Map的迭代顺序与插入顺序一致,通过删除并重新插入元素,将其移到末尾,头部元素即为最久未使用的项。
113. JavaScript中String.prototype.replaceAll()
和replace()
的区别是什么?
核心区别:
方法 | 匹配方式 | 正则表达式标志 | 替换全部匹配 | 兼容性 |
---|---|---|---|---|
replace(pattern, replacement) | 单个匹配或正则匹配 | 支持g 标志 | 需手动加g | 所有浏览器 |
replaceAll(pattern, replacement) | 全部匹配 | 不支持g 标志 | ✅ | ES2021(现代浏览器) |
代码示例:
const str = "Hello World, Hello JavaScript";// replace() 只替换第一个匹配(无g标志)
console.log(str.replace("Hello", "Hi")); // "Hi World, Hello JavaScript"// replace() 替换所有匹配(需g标志)
console.log(str.replace(/Hello/g, "Hi")); // "Hi World, Hi JavaScript"// replaceAll() 直接替换所有匹配
console.log(str.replaceAll("Hello", "Hi")); // "Hi World, Hi JavaScript"// replaceAll() 使用正则(但不能有g标志)
console.log(str.replaceAll(/Hello/, "Hi")); // "Hi World, Hi JavaScript"
console.log(str.replaceAll(/Hello/g, "Hi")); // 错误:正则不能有g标志
注意:
replaceAll()
在不支持的浏览器中需使用replace()
加g
标志替代。
114. 如何在JavaScript中实现一个简单的WebSocket客户端?
WebSocket客户端实现:
使用WebSocket
API创建实时双向通信。
代码示例:
// 创建WebSocket连接
const socket = new WebSocket('ws://echo.websocket.org');// 连接建立时触发
socket.onopen = (event) => {console.log('WebSocket连接已建立');// 发送消息到服务器socket.send('Hello, server!');
};// 接收到消息时触发
socket.onmessage = (event) => {console.log('收到服务器消息:', event.data);
};// 连接关闭时触发
socket.onclose = (event) => {console.log('WebSocket连接已关闭');if (event.wasClean) {console.log(`关闭码: ${event.code}, 原因: ${event.reason}`);} else {console.log('连接异常关闭');}
};// 发生错误时触发
socket.onerror = (error) => {console.error('WebSocket错误:', error);
};// 关闭连接
function closeConnection() {socket.close(1000, '用户主动关闭');
}
处理二进制数据:
// 接收二进制数据
socket.binaryType = 'arraybuffer';
socket.onmessage = (event) => {if (event.data instanceof ArrayBuffer) {const buffer = event.data;// 处理ArrayBuffer...}
};// 发送二进制数据
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
socket.send(uint8Array);
115. JavaScript中Array.prototype.reduceRight()
的作用是什么?
reduceRight()
:
与reduce()
类似,但从数组的最后一个元素开始,向前遍历到第一个元素。
语法:
array.reduceRight((accumulator, currentValue, index, array) => {// 返回累加值
}, initialValue);
代码示例:
// 数组扁平化(从右到左)
const nested = [[1, 2], [3, 4], [5, 6]];
const flattened = nested.reduceRight((acc, arr) => acc.concat(arr), []);
console.log(flattened); // [5, 6, 3, 4, 1, 2]// 与reduce()对比
const result1 = [1, 2, 3].reduce((acc, num) => acc - num, 0); // (0-1)-2-3 = -6
const result2 = [1, 2, 3].reduceRight((acc, num) => acc - num, 0); // (0-3)-2-1 = -6// 字符串连接顺序
const words = ['Hello', ' ', 'World', '!'];
const str1 = words.reduce((acc, word) => acc + word); // "Hello World!"
const str2 = words.reduceRight((acc, word) => acc + word); // "!World Hello"
应用场景:
- 需要从右到左处理数组时(如解析嵌套结构)。
- 操作顺序敏感的累加器(如字符串连接)。
116. 如何在JavaScript中实现一个简单的发布-订阅模式?
发布-订阅模式实现:
通过事件中心解耦发布者和订阅者,支持事件注册、发布和取消。
代码示例:
class EventEmitter {constructor() {this.events = {};}// 订阅事件on(eventName, callback) {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);return this;}// 发布事件emit(eventName, ...args) {if (this.events[eventName]) {this.events[eventName].forEach(callback => callback(...args));}return this;}// 取消订阅(移除特定回调)off(eventName, callback) {if (this.events[eventName]) {this.events[eventName] = this.events[eventName].filter(cb => cb!== callback);}return this;}// 只执行一次的订阅once(eventName, callback) {const wrapper = (...args) => {callback(...args);this.off(eventName, wrapper);};this.on(eventName, wrapper);return this;}
}// 使用示例
const emitter = new EventEmitter();// 订阅事件
const callback = (data) => console.log('接收到:', data);
emitter.on('message', callback);// 发布事件
emitter.emit('message', 'Hello, Subscribers!'); // 输出:接收到: Hello, Subscribers!// 取消订阅
emitter.off('message', callback);
emitter.emit('message', '再次发送'); // 无输出// 一次性订阅
emitter.once('single', () => console.log('只执行一次'));
emitter.emit('single'); // 输出:只执行一次
emitter.emit('single'); // 无输出
117. JavaScript中Object.is()
和===
的区别是什么?
核心区别:
比较操作 | NaN 与NaN | +0 与-0 | 普通值比较 |
---|---|---|---|
Object.is(a, b) | true | false | 同=== |
a === b | false | true | 同Object.is |
代码示例:
// NaN比较
console.log(Object.is(NaN, NaN)); // true
console.log(NaN === NaN); // false// +0与-0比较
console.log(Object.is(+0, -0)); // false
console.log(+0 === -0); // true// 普通值比较
console.log(Object.is(5, 5)); // true
console.log(5 === 5); // trueconsole.log(Object.is('hello', 'hello')); // true
console.log('hello' === 'hello'); // trueconsole.log(Object.is(null, null)); // true
console.log(null === null); // true
实现Object.is
的等效逻辑:
function myObjectIs(a, b) {if (a === b) {// 处理+0和-0return a!== 0 || 1 / a === 1 / b;} else {// 处理NaNreturn a!== a && b!== b;}
}
118. 如何在JavaScript中实现一个简单的防抖函数(Debounce)?
防抖函数:
延迟执行函数,若在延迟期间再次触发,则重新计时,直到最后一次触发后才执行。
代码示例:
function debounce(func, delay) {let timer = null;return function(...args) {// 清除上一次的定时器if (timer) {clearTimeout(timer);}// 设置新的定时器,延迟执行函数timer = setTimeout(() => {func.apply(this, args);timer = null;}, delay);};
}// 使用示例
const searchInput = document.getElementById('search');// 防抖处理搜索输入
const debouncedSearch = debounce((event) => {console.log('搜索:', event.target.value);// 执行搜索逻辑...
}, 300);searchInput.addEventListener('input', debouncedSearch);
带立即执行选项的防抖:
function debounce(func, delay, immediate = false) {let timer = null;return function(...args) {const shouldCallNow = immediate &&!timer;// 清除上一次的定时器if (timer) {clearTimeout(timer);}// 设置新的定时器timer = setTimeout(() => {timer = null;if (!immediate) {func.apply(this, args);}}, delay);// 立即执行if (shouldCallNow) {func.apply(this, args);}};
}
119. JavaScript中async
函数和普通函数的区别是什么?
核心区别:
特性 | async 函数 | 普通函数 |
---|---|---|
返回值 | 始终返回Promise | 返回指定值或undefined |
await 支持 | 可使用await 暂停执行 | 不能使用await |
错误处理 | 可使用try...catch 捕获异步错误 | 需依赖回调或Promise的.catch() |
执行方式 | 异步执行(但代码结构像同步) | 同步执行(除非显式使用异步API) |
代码示例:
// 普通函数
function fetchData() {return fetch('https://api.example.com/data').then(response => response.json());
}// async函数
async function fetchDataAsync() {try {const response = await fetch('https://api.example.com/data');const data = await response.json();return data;} catch (error) {console.error('获取数据失败:', error);throw error;}
}// 调用对比
fetchData().then(data => console.log(data)).catch(error => console.error(error));fetchDataAsync().then(data => console.log(data)).catch(error => console.error(error));
异步执行示例:
async function asyncExample() {console.log('开始');await new Promise(resolve => setTimeout(resolve, 1000));console.log('等待1秒后');return '完成';
}// 调用async函数
console.log('调用前');
asyncExample().then(result => console.log(result));
console.log('调用后');// 输出顺序:
// 调用前
// 开始
// 调用后
// 等待1秒后
// 完成
120. 如何在JavaScript中实现一个简单的节流函数(Throttle)?
节流函数:
限制函数在一定时间内最多执行一次,即使触发多次也只执行一次。
代码示例:
// 时间戳实现(立即执行)
function throttle(func, limit) {let lastExecTime = 0;return function(...args) {const now = Date.now();// 如果距离上次执行超过限制时间,则执行函数if (now - lastExecTime >= limit) {func.apply(this, args);lastExecTime = now;}};
}// 使用示例
window.addEventListener('scroll', throttle(() => {console.log('滚动事件被触发(节流)');
}, 500));
定时器实现(延迟执行):
function throttle(func, limit) {let timer = null;return function(...args) {// 如果定时器不存在,则设置定时器if (!timer) {timer = setTimeout(() => {func.apply(this, args);timer = null;}, limit);}};
}
结合版(立即执行+延迟执行):
function throttle(func, limit) {let lastExecTime = 0;let timer = null;return function(...args) {const now = Date.now();const remaining = limit - (now - lastExecTime);// 如果距离上次执行超过限制时间,立即执行if (remaining <= 0) {if (timer) {clearTimeout(timer);timer = null;}func.apply(this, args);lastExecTime = now;} // 否则设置定时器,在剩余时间后执行else if (!timer) {timer = setTimeout(() => {func.apply(this, args);lastExecTime = Date.now();timer = null;}, remaining);}};
}
二、150道面试题目录列表
文章序号 | Javascript面试题150道 |
---|---|
1 | Javascript面试题及答案150道(001-015) |
2 | Javascript面试题及答案150道(016-030) |
3 | Javascript面试题及答案150道(031-045) |
4 | Javascript面试题及答案150道(046-060) |
5 | Javascript面试题及答案150道(061-075) |
6 | Javascript面试题及答案150道(076-090) |
7 | Javascript面试题及答案150道(091-105) |
8 | Javascript面试题及答案150道(106-120) |
9 | Javascript面试题及答案150道(121-135) |
10 | Javascript面试题及答案150道(136-150) |