前端开发的小伙伴们,是不是一想到面试就紧张得手心冒汗?别担心!今天专门挑出 3 道 JavaScript 面试中高频出现的 “拦路虎”,用最通俗的语言和生活例子帮你拆解,让你吃透知识点,面试时胸有成竹,轻松避开陷阱!
第一题:变量声明那些坑 ——var、let、const 到底怎么选?
面试官常问:“说说 var、let、const 的区别,在实际开发中怎么选择?”这三个 “兄弟” 各有脾气:var是 “老派选手”,会变量提升且没有块级作用域;let是 “新生代”,有块级作用域还不会重复声明;const则是 “坚定守护者”,声明后必须赋值且不能重新赋值。
// var的坑:变量提升+函数作用域,容易引发意外覆盖
var count = 0;
if (true) {
var count = 1; // 这里的var属于函数作用域,会覆盖外层的count
}
console.log(count); // 输出1,因为内外层是同一个count
// let的优势:块级作用域,避免变量污染
let age = 20;
if (true) {
let age = 30; // 这里的let属于块级作用域,和外层的age是两个变量
console.log(age); // 输出30
}
console.log(age); // 输出20,内外层互不影响
// const的用法:声明常量,必须初始化且不可重新赋值
const PI = 3.14; // 正确,声明时赋值
// PI = 3.15; // 报错,不能重新赋值
const obj = { name: '前端君' };
// obj = { name: '小码农' }; // 报错,不能重新赋值obj本身,但可以修改属性
obj.name = '新名字'; // 允许,修改对象属性
第二题:事件循环机制 ——JS 如何处理异步任务?
“解释一下 JavaScript 的事件循环(Event Loop),它是怎么处理同步和异步任务的?”别被专业术语吓到!事件循环就像一个 “任务调度员”,先处理同步任务(主线程排队执行),再处理异步任务(分为微任务和宏任务,按优先级排队)。记住口诀:先微任务,后宏任务,循环往复。
// 同步任务和异步任务示例
console.log('1. 同步任务开始');
setTimeout(() => { // 宏任务(定时器)
console.log('4. 定时器回调');
}, 0);
Promise.resolve() // 微任务(Promise.then)
.then(() => {
console.log('3. Promise回调');
});
console.log('2. 同步任务结束');
// 执行顺序:
// 1. 同步任务开始 → 2. 同步任务结束 → 3. Promise回调(微任务) → 4. 定时器回调(宏任务)
在这段代码中,同步任务先执行,然后按顺序处理微任务(Promise 的 then 回调),最后处理宏任务(定时器回调)。理解事件循环能帮你写出更高效的代码,避免阻塞主线程。
第三题:数组去重哪家强 —— 常见方法对比与最佳实践
“实现数组去重有哪些方法?说说各自的优缺点和适用场景。”数组去重是面试常客,方法很多,各有千秋:最简单的双重循环(效率低),ES6 的 Set(快速但无法保留顺序),进阶的 filter+indexOf(保留顺序且兼容),还有利用对象属性唯一性的高阶方法。
// 方法1:Set去重(简单高效,但无法保留原始顺序)
function uniqueBySet(arr) {
return [...new Set(arr)]; // 将数组转为Set去重,再转回数组
}
console.log(uniqueBySet([1, 2, 2, 3])); // 输出[1, 2, 3]
// 方法2:filter+indexOf(保留顺序,兼容旧浏览器)
function uniqueByFilter(arr) {
return arr.filter((item, index) => {
return arr.indexOf(item) === index; // 只保留第一次出现的元素
});
}
console.log(uniqueByFilter([1, 2, 2, 3])); // 输出[1, 2, 3]
// 方法3:对象键值对去重(处理复杂数据类型,如对象)
function uniqueByObject(arr) {
const obj = {};
return arr.filter(item => {
// 这里假设对象以id作为唯一标识,实际需根据场景调整
return !obj[item.id] && (obj[item.id] = true);
});
}
面试应答模板 —— 让面试官眼前一亮
问题 1:var、let、const 怎么选?
“这三个变量声明方式区别挺大的。var 是 ES5 的老语法,会变量提升,而且只有函数作用域,不小心就会污染全局变量,现在基本很少用了。let 和 const 是 ES6 的新特性,都有块级作用域,比如在 if 语句里声明的变量,外面访问不到。let 声明的变量可以重新赋值,适合值会变的情况;const 声明的是常量,声明时必须赋值,而且不能重新赋值,适合固定不变的值,比如配置项。现在开发中推荐优先用 let 和 const,避免 var 带来的坑。”
问题 2:事件循环是啥?
“事件循环是 JavaScript 处理异步任务的机制。JavaScript 是单线程的,同一时间只能做一件事。同步任务直接在主线程执行,异步任务比如定时器、Promise、Ajax 这些,不会马上执行,会先放到任务队列里。任务队列分微任务和宏任务,微任务优先级更高。每次主线程执行完同步任务,就会先处理所有微任务,再处理一个宏任务,然后重复这个过程。比如 Promise 的 then 回调是微任务,setTimeout 是宏任务,所以微任务会比宏任务先执行。”
问题 3:数组去重怎么做?
“数组去重有好几种方法。最简单的是用 ES6 的 Set,把数组转成 Set 自动去重,再转回数组,代码少效率高,但缺点是不能保留原始顺序,而且对对象这种复杂数据类型没用。如果需要保留顺序,可以用 filter 结合 indexOf,检查每个元素第一次出现的位置是不是当前索引,是的话就保留。如果是处理对象数组,可能需要用对象的键值对,根据唯一标识来去重,比如用 id 作为键,确保每个 id 只出现一次。具体用哪种方法,看项目需求和兼容性要求。”
数组去重,Set vs filter+indexOf,谁更胜一筹?
有人觉得 Set 去重代码简洁,一行搞定,现代浏览器支持也好,是首选方案;也有人认为 filter+indexOf 兼容性更强,而且能明确控制逻辑,尤其是在需要保留顺序的场景下更可靠。你平时用哪种方法去重?觉得 Set 的 “无序” 算不算硬伤?来评论区说说你的实战经验,看看哪种方法更得人心!
看完这 3 道题的解析,是不是感觉思路更清晰了?其实面试题的核心就是考察基础是否扎实,把这些高频考点吃透,再结合实际开发场景思考,就能轻松应对面试官的提问。觉得有帮助的话,别忘了点赞关注,后续还会分享更多前端面试干货,帮你顺利通关!