Javascript面试题及详细答案150道之(046-060)
《前后端面试题
》专栏集合了前后端各个知识模块的面试题,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目录
- 一、本文面试题目录
- 46. 如何实现对象的深拷贝(考虑多种数据类型)?
- 47. 什么是模块化?CommonJS和ES6 Module有什么区别?
- 48. 如何判断两个对象是否相等(深相等)?
- 49. 什么是事件委托(事件代理)?有什么好处?
- 50. 如何阻止事件冒泡和默认行为?
- 51. 什么是变量提升和函数提升?
- 52. 什么是JSON?JSON和JavaScript对象有什么区别?
- 53. 如何获取当前时间并格式化?
- 54. 什么是递归函数?使用递归要注意什么?
- 55. 如何实现数组的findIndex方法?
- 56. 什么是Set和Map?它们有什么特点?
- 57. 如何实现数组的filter方法?
- 58. 什么是严格模式(strict mode)?如何启用?有什么限制?
- 59. 什么是迭代器(Iterator)?它有什么作用?
- 60. 如何实现数组的reduce方法?
- 二、150道面试题目录列表
一、本文面试题目录
46. 如何实现对象的深拷贝(考虑多种数据类型)?
实现一个能处理多种数据类型(对象、数组、日期、正则等)的深拷贝函数:
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。
示例:
// CommonJS
const moduleA = require('./moduleA');
module.exports = { foo: 'bar' };// ES6 Module
import { moduleB } from './moduleB';
export const baz = 'baz';
48. 如何判断两个对象是否相等(深相等)?
深相等是指两个对象的所有层级属性都相等。实现方法:
function deepEqual(a, b) {// 基本类型直接比较if (a === b) return true;// 处理nullif (a === null || b === null) return false;// 类型不同返回falseif (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. 什么是事件委托(事件代理)?有什么好处?
事件委托是将子元素的事件处理委托给父元素,利用事件冒泡原理,当子元素触发事件时,父元素能捕获到并处理。
好处:
- 减少事件监听器数量,提高性能。
- 动态添加的子元素也能触发事件,无需重新绑定。
示例代码:
// 父元素ul代理li的点击事件
const ul = document.querySelector('ul');
ul.addEventListener('click', (e) => {if (e.target.tagName === 'LI') { // 判断触发事件的是liconsole.log('点击了li:', e.target.textContent);}
});
50. 如何阻止事件冒泡和默认行为?
- 阻止事件冒泡:
- 标准浏览器:
event.stopPropagation()
- IE低版本:
event.cancelBubble = true
- 标准浏览器:
- 阻止默认行为(如表单提交、链接跳转):
- 标准浏览器:
event.preventDefault()
- IE低版本:
event.returnValue = false
- 也可在事件处理函数中返回false(仅适用于部分事件,如onclick)。
- 标准浏览器:
示例代码:
document.getElementById('btn').addEventListener('click', (e) => {e.stopPropagation(); // 阻止冒泡e.preventDefault(); // 阻止默认行为(若按钮有默认行为)
});
51. 什么是变量提升和函数提升?
- 变量提升:使用var声明的变量,会被提升到作用域顶部,声明提升但赋值不提升。
- 函数提升:函数声明会被整体提升到作用域顶部,可在声明前调用。函数表达式(如var fn = function(){})只有变量提升,没有函数提升。
示例代码:
// 变量提升
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
{ "name": "Jack", "age": 25 }
// JavaScript对象
const obj = { name: 'Jack', age: 25, sayHi() {} };
53. 如何获取当前时间并格式化?
可使用Date对象获取当前时间,然后自定义格式化函数。
示例代码:
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. 什么是递归函数?使用递归要注意什么?
递归函数是在函数内部调用自身的函数,用于解决可分解为相同子问题的问题(如阶乘、斐波那契数列)。
注意:
- 必须有终止条件,否则会导致栈溢出。
- 递归深度不宜过深,可能影响性能。
示例代码:
// 计算阶乘
function factorial(n) {if (n === 1) return 1; // 终止条件return n * factorial(n - 1);
}
console.log(factorial(5)); // 120
55. 如何实现数组的findIndex方法?
findIndex()返回数组中满足条件的第一个元素的索引,若没有则返回-1。
示例代码:
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属性。
示例代码:
// 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)的元素。
示例代码:
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为变量。
示例代码:
'use strict';
// x = 10; // 报错,变量未声明
function strictFunc() {'use strict';console.log(this); // undefined(普通调用时)
}
strictFunc();
59. 什么是迭代器(Iterator)?它有什么作用?
迭代器是一种接口,为各种数据结构提供统一的遍历方式,任何实现了Iterator接口的数据结构都可被for…of循环遍历。
迭代器有一个next()方法,返回一个包含value(当前值)和done(是否遍历结束)的对象。
作用:使不同数据结构(数组、Set、Map等)的遍历方式统一。
示例代码:
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()对数组中的每个元素执行回调函数,将其缩减为单个值,可指定初始值。
示例代码:
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
二、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) |