2.1 - 第一阶段面试题总结
JavaScript 核心技术点面试题汇总
目录
数据类型、类型判断、类型转换
考查重点分析
面试官主要考查:
- 基础扎实度:对8种数据类型的理解深度
- 类型判断:各种判断方法的优缺点和适用场景
- 隐式转换:类型转换规则的掌握,特别是边界case
- 实际应用:在实际开发中如何避免类型相关的bug
面试题
基础题
-
JavaScript有哪些数据类型?基本类型和引用类型有什么区别?
-
typeof和instanceof的区别是什么?它们各有什么局限性? -
如何准确判断一个变量是数组类型?请列举至少3种方法。
-
说说
null和undefined的区别,以及typeof null为什么返回"object"?
进阶题
-
实现一个通用的类型判断函数
getType(),能准确判断所有JavaScript数据类型。 -
以下代码的输出结果是什么?为什么?
console.log([] + []);
console.log([] + {});
console.log({} + []);
console.log({} + {});
console.log(true + false);
console.log(1 + "2" + 3);
console.log(1 + +"2" + 3);
console.log(1 + -"1" + 2);
console.log(+"1" + "1" + "2");
console.log("A" - "B" + 2);
-
什么是装箱和拆箱?请举例说明。
-
解释以下代码的执行结果:
console.log(0.1 + 0.2 === 0.3);
console.log(0.1 + 0.2);
// 如何解决浮点数精度问题?
高频核心题
-
请实现一个深拷贝函数
deepClone(),要求:- 支持对象、数组、Date、RegExp、Map、Set等类型
- 解决循环引用问题
- 考虑Symbol作为key的情况
-
以下比较运算的结果是什么?请解释原因:
console.log([] == ![]);
console.log([] == false);
console.log({} == false);
console.log(null == undefined);
console.log(null === undefined);
console.log(NaN == NaN);
console.log(Object.is(NaN, NaN));
console.log(Object.is(+0, -0));
-
如何判断两个对象是否相等?请实现一个
isEqual()函数。 -
说说
==和===的区别,以及完整的类型转换规则。
作用域、闭包、this指向
考查重点分析
面试官主要考查:
- 作用域理解:词法作用域、作用域链的概念
- 闭包原理:闭包的形成条件、使用场景和内存管理
- this绑定:各种this指向的判断和改变this的方法
- 实战能力:在复杂场景下准确判断作用域和this
面试题
基础题
-
什么是作用域?JavaScript中有哪几种作用域?
-
什么是作用域链?解释变量查找的过程。
-
var、let、const的区别是什么?什么是暂时性死区? -
什么是闭包?闭包有什么作用和应用场景?
进阶题
- 分析以下代码的输出结果:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// 如何修改代码让其输出 0, 1, 2, 3, 4?请给出至少3种方案。
- 请解释以下代码的执行结果:
var name = 'global';
var obj = {
name: 'obj',
getName: function() {
return this.name;
}
};
console.log(obj.getName());
var fn = obj.getName;
console.log(fn());
console.log((obj.getName)());
console.log((obj.getName = obj.getName)());
-
闭包会造成内存泄漏吗?如何避免?
-
说说
call、apply、bind的区别和使用场景。
高频核心题
-
手写实现
call、apply、bind方法。 -
箭头函数和普通函数的区别是什么?箭头函数的this指向规则是什么?
-
分析以下代码的输出:
var length = 10;
function fn() {
console.log(this.length);
}
var obj = {
length: 5,
method: function(fn) {
fn();
arguments[0]();
}
};
obj.method(fn, 1, 2, 3);
- 实现一个函数柯里化
curry()函数,要求支持以下用法:
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
curriedAdd(1)(2)(3); // 6
curriedAdd(1, 2)(3); // 6
curriedAdd(1)(2, 3); // 6
-
什么是立即执行函数(IIFE)?它有什么作用?
-
分析以下代码的作用域和输出:
var a = 1;
function foo() {
console.log(a);
var a = 2;
console.log(a);
function a() {}
console.log(a);
}
foo();
原型链、继承
考查重点分析
面试官主要考查:
- 原型理解:prototype、proto、constructor的关系
- 原型链机制:属性查找、方法继承的过程
- 继承方式:各种继承方式的实现和优缺点
- ES6 Class:Class语法糖与传统继承的关系
面试题
基础题
-
什么是原型?什么是原型链?
-
prototype和__proto__的区别是什么? -
constructor属性的作用是什么? -
如何判断一个属性是对象自身的还是原型上的?
进阶题
- 解释以下代码的输出结果:
function Person() {}
Person.prototype.name = 'person';
var p1 = new Person();
var p2 = new Person();
p1.name = 'p1';
console.log(p1.name);
console.log(p2.name);
delete p1.name;
console.log(p1.name);
- 请画出以下代码的原型链关系图:
function Parent() {}
function Child() {}
Child.prototype = new Parent();
var child = new Child();
-
instanceof的实现原理是什么?请手写实现一个myInstanceof()。 -
new操作符具体做了什么?请手写实现一个myNew()。
高频核心题
-
JavaScript有哪些继承方式?请分别实现并说明各自的优缺点:
- 原型链继承
- 构造函数继承
- 组合继承
- 原型式继承
- 寄生式继承
- 寄生组合式继承
-
ES6 的 Class 继承与 ES5 的继承有什么区别?
-
如何创建一个没有原型的对象?
-
实现一个寄生组合式继承,这是最优的继承方式。
-
分析以下代码的输出:
function Foo() {
getName = function() { console.log(1); };
return this;
}
Foo.getName = function() { console.log(2); };
Foo.prototype.getName = function() { console.log(3); };
var getName = function() { console.log(4); };
function getName() { console.log(5); }
Foo.getName();
getName();
Foo().getName();
getName();
new Foo.getName();
new Foo().getName();
new new Foo().getName();
- 什么是原型污染?如何防止原型污染?
Event Loop、宏任务与微任务
考查重点分析
面试官主要考查:
- 执行机制:JavaScript单线程、事件循环的理解
- 任务分类:宏任务和微任务的区别和执行顺序
- 实际应用:能够准确分析异步代码的执行顺序
- 浏览器与Node:不同环境下Event Loop的差异
面试题
基础题
-
什么是Event Loop(事件循环)?
-
什么是宏任务(macro task)和微任务(micro task)?请分别列举。
-
JavaScript为什么是单线程的?
-
解释同步任务和异步任务的执行顺序。
进阶题
-
说说宏任务和微任务的执行顺序,以及一个完整的Event Loop流程。
-
分析以下代码的输出顺序:
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
Promise.resolve().then(function() {
console.log('promise1');
}).then(function() {
console.log('promise2');
});
console.log('script end');
- 分析以下代码的输出顺序:
console.log('1');
setTimeout(() => {
console.log('2');
Promise.resolve().then(() => {
console.log('3');
});
}, 0);
new Promise((resolve) => {
console.log('4');
resolve();
}).then(() => {
console.log('5');
}).then(() => {
console.log('6');
});
setTimeout(() => {
console.log('7');
Promise.resolve().then(() => {
console.log('8');
});
}, 0);
console.log('9');
process.nextTick()和setImmediate()的区别是什么?(Node环境)
高频核心题
- 分析以下复杂代码的输出顺序:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');
-
浏览器的Event Loop和Node.js的Event Loop有什么区别?
-
分析以下代码的执行顺序并解释原因:
Promise.resolve().then(() => {
console.log('Promise1');
setTimeout(() => {
console.log('setTimeout2');
}, 0);
});
setTimeout(() => {
console.log('setTimeout1');
Promise.resolve().then(() => {
console.log('Promise2');
});
}, 0);
requestAnimationFrame在Event Loop中的位置是什么?
Promise、async/await、Generator
考查重点分析
面试官主要考查:
- Promise原理:状态管理、链式调用、错误处理
- 手写能力:能否手写符合Promise/A+规范的实现
- async/await:语法糖的本质和错误处理
- Generator:迭代器协议和异步流程控制
- 实战经验:异步编程的最佳实践
面试题
基础题
-
什么是Promise?Promise有哪几种状态?
-
Promise如何解决回调地狱问题?
-
Promise的
.then()方法可以接收几个参数?分别是什么? -
.then()和.catch()的区别是什么? -
什么是Promise链?如何实现Promise的链式调用?
进阶题
- 分析以下代码的输出:
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve();
console.log(2);
});
promise.then(() => {
console.log(3);
});
console.log(4);
-
Promise.all()、Promise.race()、Promise.allSettled()、Promise.any() 的区别和使用场景?
-
手写实现
Promise.all()。 -
手写实现
Promise.race()。 -
如何取消一个Promise?
-
分析以下代码的执行结果:
Promise.resolve(1)
.then(res => {
console.log(res);
return 2;
})
.catch(err => {
return 3;
})
.then(res => {
console.log(res);
});
Promise.reject(1)
.then(res => {
console.log(res);
return 2;
})
.catch(err => {
console.log(err);
return 3;
})
.then(res => {
console.log(res);
});
高频核心题
-
手写实现一个符合Promise/A+规范的Promise,要求:
- 实现基本的状态管理(pending、fulfilled、rejected)
- 实现 then 方法的链式调用
- 处理异步操作
- 实现值的穿透
- 处理循环引用
-
分析以下代码的输出:
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
}).then(function() {
console.log('promise3');
});
-
async/await 的原理是什么?它是如何实现的?
-
async/await 如何进行错误处理?请给出多种方案。
-
分析以下代码的执行结果:
async function test() {
try {
await Promise.reject('error');
} catch(e) {
console.log(e);
}
return Promise.resolve('success');
}
test().then(res => {
console.log(res);
});
-
Generator函数是什么?它有什么特点和应用场景?
-
Generator函数与async/await的关系是什么?如何用Generator实现async/await?
-
实现一个自动执行Generator函数的函数
run()(co库的简化版)。 -
实现一个Promise的串行执行和并行限制:
- 串行执行:依次执行多个异步任务
- 并行限制:最多同时执行N个Promise
-
实现一个带有重试功能的Promise请求函数
retryRequest()。 -
实现一个Promise的超时控制函数
timeoutPromise()。 -
如何处理多个并发请求,并保证它们按照发起顺序返回结果?
综合应用题
考查重点分析
面试官主要考查:
- 综合能力:多个知识点的综合运用
- 问题分析:对复杂问题的分析和解决能力
- 代码质量:代码的健壮性、可维护性
- 工程思维:在实际项目中的应用经验
面试题
-
实现一个EventEmitter类(发布订阅模式),要求:
- 支持on(订阅)、emit(发布)、off(取消订阅)
- 支持once(只触发一次)
- 考虑内存泄漏问题
-
实现一个LazyMan,支持链式调用:
LazyMan('Hank').sleep(3).eat('dinner');
// 输出:
// Hi! This is Hank!
// (等待3秒)
// Wake up after 3
// Eat dinner~
- 实现一个任务调度器Scheduler:
class Scheduler {
constructor(maxCount) {
// 最多同时运行maxCount个任务
}
add(promiseCreator) {
// 添加任务
}
}
const scheduler = new Scheduler(2);
const addTask = (time, order) => {
scheduler.add(() => timeout(time).then(() => console.log(order)));
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 输出:2 3 1 4
-
实现一个带缓存的异步请求函数,相同的请求在pending期间不会重复发送。
-
分析并优化以下代码的性能问题:
async function processData(dataList) {
const results = [];
for (let data of dataList) {
const result = await fetchData(data);
results.push(result);
}
return results;
}
学习建议
- 系统学习:不要孤立地学习每个知识点,理解它们之间的联系
- 动手实践:所有手写题都要自己实现一遍,不要只看答案
- 理解原理:知其然更要知其所以然,深入理解底层原理
- 代码调试:多用debugger和console.log,观察代码执行流程
- 总结归纳:建立自己的知识体系,定期复习和总结
- 实战应用:在实际项目中应用这些知识,加深理解
答题思路
1. 基础概念题
- 先给出准确定义
- 举例说明
- 说明应用场景
- 补充注意事项
2. 代码输出题
- 逐行分析代码执行过程
- 画出执行流程图或调用栈
- 说明关键知识点
- 给出准确答案
3. 手写实现题
- 先分析需求和边界条件
- 写出清晰的代码结构
- 处理异常情况
- 优化代码性能
- 补充测试用例
4. 对比分析题
- 列出相同点
- 列出不同点
- 说明适用场景
- 给出选择建议
面试准备检查清单
- 理解所有基础概念
- 能够准确分析代码输出题
- 熟练手写核心API实现
- 掌握常见应用场景和解决方案
- 准备1-2个深入研究的技术点
- 整理自己的项目中相关的实战经验
- 模拟面试练习,提高表达能力
附录:推荐资源
书籍
- 《JavaScript高级程序设计》
- 《你不知道的JavaScript》
- 《JavaScript语言精粹》
- 《深入理解ES6》
在线资源
- MDN Web Docs
- JavaScript.info
- ES6标准文档
- Promise/A+规范
实践平台
- LeetCode
- 牛客网
- 前端面试题库
最后提醒:面试不仅仅是考查技术能力,更重要的是展现你的学习能力、思考方式和解决问题的思路。保持自信,充分准备,祝你面试成功!🚀