网站后台模板关联自己做的网站电商网站楼层 设计
JavaScript 相关面试题及答案
-
JavaScript 的数据类型有哪些?如何区分基本数据类型和引用数据类型?
JavaScript 的数据类型分为两类:
-
基本数据类型(值类型):
Number、String、Boolean、Null、Undefined、Symbol(ES6)、BigInt(ES11)。 -
引用数据类型:
Object(包括Array、Function、Date、RegExp等)。区分方式:
① 存储方式:基本类型存储值本身,引用类型存储地址(指向堆内存中的对象);
② 赋值行为:基本类型赋值是值拷贝,引用类型赋值是地址拷贝(修改新变量会影响原变量)。
null和undefined的区别是什么?分别在什么场景下使用?
-
undefined:表示 “未定义”,变量声明后未赋值、函数无返回值、访问不存在的属性时返回此值。 -
null:表示 “空值”,主动赋值给变量,表示该变量有意为空(如释放对象引用)。区别:
① 类型不同:typeof undefined返回"undefined",typeof null返回"object";
② 转换为数字:Number(undefined)为NaN,Number(null)为0。
-
什么是变量提升?函数提升和变量提升有何区别?
变量提升指 JavaScript 引擎在执行代码前,将变量和函数声明 “提升” 到当前作用域顶部的行为。
区别:
① 函数提升:函数声明整体被提升(可在声明前调用);
② 变量提升:仅声明被提升,赋值留在原地(声明前访问返回undefined)。示例:
console.log(a); // undefined(变量提升)var a = 1;fn(); // "hello"(函数提升)function fn() { console.log("hello"); }
this关键字在不同场景下的指向是什么?
-
全局作用域 / 普通函数:指向全局对象(浏览器为
window,Node.js 为global),严格模式下为undefined。 -
对象方法:指向调用方法的对象(如
obj.fn()中this指向obj)。 -
构造函数(
new调用):指向新创建的实例对象。 -
事件绑定:指向触发事件的元素(如
btn.onclick中this指向btn)。 -
箭头函数:无自身
this,继承外层作用域的this(固定不变)。 -
call/apply/bind:手动指定this指向(第一个参数)。
-
什么是闭包?闭包有哪些应用场景和缺点?
闭包指嵌套函数中,内部函数引用外部函数的变量,且内部函数被外部访问,导致外部变量不被销毁的现象。
应用场景:
① 实现私有变量(如计数器:function createCounter() { let n = 0; return () => ++n; });
② 保存变量状态(如防抖节流的定时器);
③ 模块化(隔离作用域)。缺点:
① 变量长期驻留内存,可能导致内存泄漏;
② 过度使用会增加内存消耗。 -
原型和原型链的概念是什么?它们在 JavaScript 中的作用是什么?
-
原型:每个函数都有
prototype属性(原型对象),实例对象通过__proto__(隐式原型)指向该对象,用于共享方法和属性。 -
原型链:实例访问属性 / 方法时,若自身不存在,会沿
__proto__向上查找,直到null,形成的链条即原型链。作用:
① 实现继承(子实例共享父原型的属性方法);
② 节省内存(方法定义在原型上,而非每个实例)。
- 什么是事件冒泡和事件捕获?如何阻止事件冒泡?
-
事件冒泡:事件从触发元素(目标)向上传播到父元素、根元素(由内向外)。
-
事件捕获:事件从根元素向下传播到触发元素(由外向内)。
阻止冒泡:
① 标准浏览器:event.stopPropagation();
② IE 低版本:event.cancelBubble = true。注意:
event.stopImmediatePropagation()还会阻止当前元素后续事件监听器执行。
==和===的区别是什么?请举例说明。
-
==:抽象相等,会自动转换类型后比较(如1 == "1"返回true)。 -
===:严格相等,不转换类型,类型和值均相同才返回true(如1 === "1"返回false)。特殊情况:
null == undefined为true,null === undefined为false;NaN == NaN为false(需用isNaN()判断)。
- 什么是防抖和节流?它们的应用场景和实现方式是什么?
-
防抖:触发事件后延迟 n 秒执行,若 n 秒内再次触发则重新计时(如搜索框输入联想)。
实现:
function debounce(fn, delay) {let timer = null;return (...args) => {clearTimeout(timer);timer = setTimeout(() => fn.apply(this, args), delay);};}
-
节流:触发事件后,每隔 n 秒最多执行一次(如滚动加载)。
实现(时间戳版):
function throttle(fn, interval) {let lastTime = 0;return (...args) => {const now = Date.now();if (now - lastTime >= interval) {fn.apply(this, args);lastTime = now;}};}
- JavaScript 中如何实现继承?请列举常见方式。
-
原型链继承:
Child.prototype = new Parent(),缺点是共享父类引用属性。 -
构造函数继承:
Parent.call(this),缺点是无法继承父类原型方法。 -
组合继承:原型链 + 构造函数(
Parent.call(this)+Child.prototype = new Parent()),较常用但父构造函数调用两次。 -
寄生组合继承:
Child.prototype = Object.create(Parent.prototype),优化组合继承,避免重复调用。 -
ES6
class继承:class Child extends Parent { constructor() { super() } },语法糖,本质是原型链。
-
什么是 Promise?它解决了什么问题?常用方法有哪些?
Promise 是处理异步操作的对象,有
pending(进行中)、fulfilled(成功)、rejected(失败)三种状态,状态一旦改变不可逆转。解决的问题:
① 回调地狱(嵌套回调导致代码混乱);
② 异步操作的同步化表达(链式调用)。常用方法:
①then():处理成功 / 失败;
②catch():捕获错误;
③finally():无论成功失败都执行;
④Promise.all():等待所有 Promise 成功;
⑤Promise.race():取第一个状态改变的 Promise。 -
箭头函数和普通函数的区别是什么?
-
this指向:箭头函数无自身this,继承外层this;普通函数this随调用方式变化。 -
构造函数:箭头函数不能用
new调用(无prototype);普通函数可以。 -
arguments:箭头函数无arguments对象(可用剩余参数...args);普通函数有。 -
写法:箭头函数更简洁(单参数可省括号,单语句可省大括号和
return)。 -
适用场景:箭头函数适合回调(如
setTimeout、数组方法),普通函数适合需要动态this的场景(如对象方法)。
- 什么是深拷贝和浅拷贝?如何实现深拷贝?
-
浅拷贝:仅复制对象的表层属性,若属性为引用类型,则复制地址(修改新对象会影响原对象)。
实现:
Object.assign()、扩展运算符{...obj}、数组slice()/concat()。 -
深拷贝:复制对象的所有层级,新对象与原对象完全独立(修改互不影响)。
实现:
①JSON.parse(JSON.stringify(obj))(缺点:不支持函数、循环引用);
② 递归拷贝:
function deepClone(obj, map = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;if (map.has(obj)) return map.get(obj); // 解决循环引用const clone = Array.isArray(obj) ? \[] : {};map.set(obj, clone);for (const key in obj) {if (obj.hasOwnProperty(key)) {clone\[key] = deepClone(obj\[key], map);}}return clone;}
- 数组有哪些常用方法?请分类说明(如增删、遍历、转换等)。
-
增删:
push()(末尾增)、pop()(末尾删)、unshift()(开头增)、shift()(开头删)、splice(index, deleteCount, ...add)(增删改)。 -
遍历:
forEach()、map()(返回新数组)、filter()(过滤)、find()(找第一个符合条件元素)、some()/every()(判断)、reduce()(累加)。 -
转换:
join()(转字符串)、split()(字符串转数组,数组无此方法)、toString()。 -
其他:
slice()(截取)、sort()(排序)、reverse()(反转)、concat()(合并)。
-
什么是异步编程?JavaScript 中实现异步的方式有哪些?
异步编程指代码不按顺序执行,耗时操作(如网络请求)不阻塞后续代码,完成后通过回调等方式通知执行。
实现方式:
① 回调函数(setTimeout、事件监听);
② Promise(链式调用);
③ Generator 函数(yield暂停 / 恢复);
④ async/await(Promise 语法糖,更接近同步);
⑤ 发布 - 订阅模式(事件总线)。 -
async/await的工作原理是什么?它和 Promise 有什么关系?async/await是 ES2017 新增的异步语法,基于 Promise 实现,使异步代码更像同步代码。原理:
async函数返回 Promise 对象,await后接 Promise,会暂停函数执行,等待 Promise 状态改变后继续。关系:
①await只能在async函数中使用;
②await会自动处理 Promise 的成功结果,失败需用try/catch捕获;
③ 本质是 Generator 函数和 Promise 的封装,简化异步流程。 -
什么是模块化?ES6 模块和 CommonJS 模块的区别是什么?
模块化指将代码拆分为独立文件,通过导出 / 导入实现复用和隔离。
区别:
-
加载时机:ES6 模块是编译时加载(静态分析,支持 tree-shaking);CommonJS 是运行时加载(动态)。
-
输出:ES6 模块输出值的引用(只读);CommonJS 输出值的拷贝。
-
语法:ES6 用
import/export;CommonJS 用require()/module.exports。 -
环境:ES6 模块浏览器和 Node.js(需配置)支持;CommonJS 主要用于 Node.js。
Map和Set有什么特点?它们和普通对象、数组有何区别?
-
Map:键值对集合,键可以是任意类型(对象、基本类型),键唯一,按插入顺序遍历。与对象区别:对象键只能是字符串 / Symbol,Map 键类型更灵活;Map 有
size属性,对象需手动计算长度。 -
Set:值的集合,值唯一,按插入顺序遍历,无索引。与数组区别:数组允许重复值,Set 值唯一;Set 查找值用
has(),数组用indexOf/includes。
- 如何判断一个变量是否为数组?有哪些方法?
-
Array.isArray(arr):最可靠,返回布尔值(推荐)。 -
arr instanceof Array:判断是否为 Array 构造函数的实例(可能受 iframe 影响)。 -
Object.prototype.toString.call(arr) === "[object Array]":准确,可判断多种类型。 -
arr.constructor === Array:不可靠(constructor可被修改)。
-
什么是事件循环(Event Loop)?简述其执行过程。
事件循环是 JavaScript 处理异步操作的机制,流程如下:
-
同步代码进入执行栈,立即执行。
-
异步任务(宏任务 / 微任务)完成后,分别放入宏任务队列和微任务队列。
-
执行栈为空时,先执行所有微任务(如
Promise.then)。 -
微任务执行完,执行一个宏任务(如
setTimeout回调),并触发 UI 渲染。 -
重复步骤 3-4,形成循环。
宏任务:
setTimeout、setInterval、I/O、UI 渲染等。微任务:
Promise.then/catch/finally、queueMicrotask、process.nextTick(Node.js)。
-
