《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux... 。

文章目录
- 一、本文面试题目录
-
-
- [46. 如何实现对象的深拷贝(考虑多种数据类型)?](#46. 如何实现对象的深拷贝(考虑多种数据类型)?)
- [47. 什么是模块化?CommonJS和ES6 Module有什么区别?](#47. 什么是模块化?CommonJS和ES6 Module有什么区别?)
- [48. 如何判断两个对象是否相等(深相等)?](#48. 如何判断两个对象是否相等(深相等)?)
- [49. 什么是事件委托(事件代理)?有什么好处?](#49. 什么是事件委托(事件代理)?有什么好处?)
- [50. 如何阻止事件冒泡和默认行为?](#50. 如何阻止事件冒泡和默认行为?)
- [51. 什么是变量提升和函数提升?](#51. 什么是变量提升和函数提升?)
- [52. 什么是JSON?JSON和JavaScript对象有什么区别?](#52. 什么是JSON?JSON和JavaScript对象有什么区别?)
- [53. 如何获取当前时间并格式化?](#53. 如何获取当前时间并格式化?)
- [54. 什么是递归函数?使用递归要注意什么?](#54. 什么是递归函数?使用递归要注意什么?)
- [55. 如何实现数组的findIndex方法?](#55. 如何实现数组的findIndex方法?)
- [56. 什么是Set和Map?它们有什么特点?](#56. 什么是Set和Map?它们有什么特点?)
- [57. 如何实现数组的filter方法?](#57. 如何实现数组的filter方法?)
- [58. 什么是严格模式(strict mode)?如何启用?有什么限制?](#58. 什么是严格模式(strict mode)?如何启用?有什么限制?)
- [59. 什么是迭代器(Iterator)?它有什么作用?](#59. 什么是迭代器(Iterator)?它有什么作用?)
- [60. 如何实现数组的reduce方法?](#60. 如何实现数组的reduce方法?)
-
- 二、150道面试题目录列表
一、本文面试题目录
46. 如何实现对象的深拷贝(考虑多种数据类型)?
实现一个能处理多种数据类型(对象、数组、日期、正则等)的深拷贝函数:
javascript
function deepClone(obj, hash = new WeakMap()) {
if (obj === null || typeof obj!== 'object') return obj;
if (hash.has(obj)) return hash.get(obj); // 处理循环引用
let cloneObj;
// 处理日期
if (obj instanceof Date) {
cloneObj = new Date(obj);
hash.set(obj, cloneObj);
return cloneObj;
}
// 处理正则
if (obj instanceof RegExp) {
cloneObj = new RegExp(obj.source, obj.flags);
hash.set(obj, cloneObj);
return cloneObj;
}
// 处理数组和对象
cloneObj = Array.isArray(obj)? [] : {};
hash.set(obj, cloneObj);
// 递归拷贝属性
Reflect.ownKeys(obj).forEach(key => {
cloneObj[key] = deepClone(obj[key], hash);
});
return cloneObj;
}
47. 什么是模块化?CommonJS和ES6 Module有什么区别?
模块化是将代码拆分为独立的模块,便于复用和维护。
区别:
- 语法:CommonJS用require()导入、module.exports导出;ES6 Module用import导入、export导出。
- 加载方式:CommonJS是运行时加载(动态),ES6 Module是编译时加载(静态)。
- 作用域:CommonJS模块中,模块是一个函数作用域;ES6 Module是块级作用域。
- this指向:CommonJS模块中this指向当前模块;ES6 Module中this是undefined。
示例:
javascript
// CommonJS
const moduleA = require('./moduleA');
module.exports = { foo: 'bar' };
// ES6 Module
import { moduleB } from './moduleB';
export const baz = 'baz';
48. 如何判断两个对象是否相等(深相等)?
深相等是指两个对象的所有层级属性都相等。实现方法:
javascript
function deepEqual(a, b) {
// 基本类型直接比较
if (a === b) return true;
// 处理null
if (a === null || b === null) return false;
// 类型不同返回false
if (typeof a!== typeof b) return false;
// 处理对象和数组
if (typeof a === 'object') {
// 获取属性名
const keysA = Reflect.ownKeys(a);
const keysB = Reflect.ownKeys(b);
if (keysA.length!== keysB.length) return false;
// 递归比较每个属性
return keysA.every(key => deepEqual(a[key], b[key]));
}
return false;
}
49. 什么是事件委托(事件代理)?有什么好处?
事件委托是将子元素的事件处理委托给父元素,利用事件冒泡原理,当子元素触发事件时,父元素能捕获到并处理。
好处:
- 减少事件监听器数量,提高性能。
- 动态添加的子元素也能触发事件,无需重新绑定。
示例代码:
javascript
// 父元素ul代理li的点击事件
const ul = document.querySelector('ul');
ul.addEventListener('click', (e) => {
if (e.target.tagName === 'LI') { // 判断触发事件的是li
console.log('点击了li:', e.target.textContent);
}
});
50. 如何阻止事件冒泡和默认行为?
- 阻止事件冒泡:
- 标准浏览器:
event.stopPropagation()
- IE低版本:
event.cancelBubble = true
- 标准浏览器:
- 阻止默认行为(如表单提交、链接跳转):
- 标准浏览器:
event.preventDefault()
- IE低版本:
event.returnValue = false
- 也可在事件处理函数中返回false(仅适用于部分事件,如onclick)。
- 标准浏览器:
示例代码:
javascript
document.getElementById('btn').addEventListener('click', (e) => {
e.stopPropagation(); // 阻止冒泡
e.preventDefault(); // 阻止默认行为(若按钮有默认行为)
});
51. 什么是变量提升和函数提升?
- 变量提升:使用var声明的变量,会被提升到作用域顶部,声明提升但赋值不提升。
- 函数提升:函数声明会被整体提升到作用域顶部,可在声明前调用。函数表达式(如var fn = function(){})只有变量提升,没有函数提升。
示例代码:
javascript
// 变量提升
console.log(x); // undefined(var声明提升,赋值未提升)
var x = 10;
// 函数提升
foo(); // 能调用,函数声明提升
function foo() {
console.log('foo');
}
// 函数表达式不提升
bar(); // 报错,bar是变量,此时值为undefined
var bar = function() {
console.log('bar');
};
52. 什么是JSON?JSON和JavaScript对象有什么区别?
JSON(JavaScript Object Notation)是一种轻量级数据交换格式,基于JavaScript对象字面量语法,但有严格规定。
区别:
- JSON是字符串,用于数据传输;JavaScript对象是内存中的数据结构。
- JSON的键必须用双引号包裹;JavaScript对象的键可不用引号或用单/双引号。
- JSON的值不能有函数、undefined、Symbol;JavaScript对象可以。
- JSON的尾逗号不允许;JavaScript对象允许。
示例:
json
// JSON
{ "name": "Jack", "age": 25 }
javascript
// JavaScript对象
const obj = { name: 'Jack', age: 25, sayHi() {} };
53. 如何获取当前时间并格式化?
可使用Date对象获取当前时间,然后自定义格式化函数。
示例代码:
javascript
function formatDate(date = new Date(), format = 'yyyy-MM-dd HH:mm:ss') {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return format.replace('yyyy', year)
.replace('MM', month)
.replace('dd', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
console.log(formatDate()); // 2025-07-21 10:30:45(当前时间)
54. 什么是递归函数?使用递归要注意什么?
递归函数是在函数内部调用自身的函数,用于解决可分解为相同子问题的问题(如阶乘、斐波那契数列)。
注意:
- 必须有终止条件,否则会导致栈溢出。
- 递归深度不宜过深,可能影响性能。
示例代码:
javascript
// 计算阶乘
function factorial(n) {
if (n === 1) return 1; // 终止条件
return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
55. 如何实现数组的findIndex方法?
findIndex()返回数组中满足条件的第一个元素的索引,若没有则返回-1。
示例代码:
javascript
Array.prototype.myFindIndex = function(callback, thisArg) {
for (let i = 0; i < this.length; i++) {
if (callback.call(thisArg, this[i], i, this)) {
return i;
}
}
return -1;
};
const arr = [10, 20, 30];
console.log(arr.myFindIndex(item => item > 15)); // 1
56. 什么是Set和Map?它们有什么特点?
- Set:是值的集合,特点是值唯一,没有重复值,可用于数组去重。
常用方法:add()、delete()、has()、clear()、size属性。 - Map:是键值对的集合,键可以是任意类型(对象、基本类型等),与对象不同,对象的键只能是字符串或Symbol。
常用方法:set()、get()、delete()、has()、clear()、size属性。
示例代码:
javascript
// Set
const set = new Set([1, 2, 2, 3]);
console.log(set.size); // 3
set.add(4);
console.log(set.has(2)); // true
// Map
const map = new Map();
const key = { id: 1 };
map.set(key, 'value');
console.log(map.get(key)); // value
57. 如何实现数组的filter方法?
filter()创建一个新数组,包含所有通过测试(回调函数返回true)的元素。
示例代码:
javascript
Array.prototype.myFilter = function(callback, thisArg) {
const result = [];
for (let i = 0; i < this.length; i++) {
if (callback.call(thisArg, this[i], i, this)) {
result.push(this[i]);
}
}
return result;
};
const arr = [1, 2, 3, 4, 5];
console.log(arr.myFilter(item => item % 2 === 0)); // [2, 4]
58. 什么是严格模式(strict mode)?如何启用?有什么限制?
严格模式是JavaScript的一种限制性更强的执行模式,使代码更规范、更安全。
启用:在脚本或函数顶部添加'use strict';
。
限制:
- 变量必须声明后使用。
- 不能删除变量、函数或函数参数。
- 禁止this指向全局对象(普通函数调用时this为undefined)。
- 禁止在函数内部声明eval和arguments为变量。
示例代码:
javascript
'use strict';
// x = 10; // 报错,变量未声明
function strictFunc() {
'use strict';
console.log(this); // undefined(普通调用时)
}
strictFunc();
59. 什么是迭代器(Iterator)?它有什么作用?
迭代器是一种接口,为各种数据结构提供统一的遍历方式,任何实现了Iterator接口的数据结构都可被for...of循环遍历。
迭代器有一个next()方法,返回一个包含value(当前值)和done(是否遍历结束)的对象。
作用:使不同数据结构(数组、Set、Map等)的遍历方式统一。
示例代码:
javascript
const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
60. 如何实现数组的reduce方法?
reduce()对数组中的每个元素执行回调函数,将其缩减为单个值,可指定初始值。
示例代码:
javascript
Array.prototype.myReduce = function(callback, initialValue) {
let accumulator = initialValue;
let startIndex = 0;
// 若未提供初始值,使用数组第一个元素作为初始值
if (initialValue === undefined) {
accumulator = this[0];
startIndex = 1;
}
for (let i = startIndex; i < this.length; i++) {
accumulator = callback(accumulator, this[i], i, this);
}
return accumulator;
};
const arr = [1, 2, 3, 4];
console.log(arr.myReduce((acc, cur) => acc + cur, 0)); // 10