Javascript/ES6+/Typescript重点内容篇——手撕
前端核心知识点梳理与面试题详解
1. Promise
核心知识点
- Promise 是异步编程的解决方案,用于处理异步操作
- 三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
- 状态一旦改变就不会再变,从 pending 到 fulfilled 或 rejected
- 常用方法:then()、catch()、finally()、all()、race()、allSettled() 等
面试题:实现 Promise.all
Promise.myAll = function(promises) {return new Promise((resolve, reject) => {// 判断传入的是否是可迭代对象if (!Array.isArray(promises)) {return reject(new TypeError('The input must be an array'));}const results = [];let completedCount = 0;const total = promises.length;if (total === 0) {return resolve(results);}promises.forEach((promise, index) => {// 确保每个元素都是 Promise 对象Promise.resolve(promise).then((value) => {results[index] = value;completedCount++;// 所有 Promise 都成功时才 resolveif (completedCount === total) {resolve(results);}},(reason) => {// 有一个 Promise 失败就立即 rejectreject(reason);});});});
};
面试题:实现 Promise 串行执行
// 实现一个函数,让多个Promise按顺序执行
function promiseSerial(tasks) {// 初始返回一个已 resolved 的 Promisereturn tasks.reduce((prev, current) => {return prev.then((results) => {// 等待当前任务执行完成return current().then((result) => {// 将结果添加到数组中return [...results, result];});});}, Promise.resolve([]));
}// 使用示例
const createTask = (time, value) => {return () => new Promise(resolve => {setTimeout(() => {console.log(value);resolve(value);}, time);});
};const tasks = [createTask(1000, '任务1'),createTask(500, '任务2'),createTask(800, '任务3')
];promiseSerial(tasks).then(results => {console.log('所有任务完成:', results);
});
2. 原型链
核心知识点
- 每个对象都有
__proto__
属性,指向其构造函数的 prototype - 构造函数有 prototype 属性,是其实例的原型
- 原型链是由
__proto__
连接而成的链式结构 - 当访问对象的属性或方法时,会先在自身查找,找不到则沿原型链向上查找
Object.prototype
是原型链的终点,其__proto__
为 null
面试题:原型链相关输出题
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(); // 2 - 访问Foo的静态方法
getName(); // 4 - 函数声明提升后被变量声明覆盖
Foo().getName(); // 1 - Foo()执行后修改了全局getName
getName(); // 1 - 已经被Foo()修改
new Foo.getName(); // 2 - 优先级问题,相当于new (Foo.getName)()
new Foo().getName(); // 3 - 实例化后访问原型上的方法
new new Foo().getName();// 3 - 先实例化Foo,再调用其原型上的getName并实例化
面试题:实现 instanceof
3. 生成器(Generator)
核心知识点
- 生成器函数使用
function*
声明,内部使用yield
关键字 - 调用生成器函数返回迭代器对象,而非直接执行函数体
- 通过迭代器的
next()
方法控制函数执行,每次遇到yield
暂停 next()
方法返回包含value
和done
属性的对象- 可用于实现异步操作的同步化表达、自定义迭代器等
面试题:使用 Generator 实现异步操作
// 模拟异步操作
function fetchData(url) {return new Promise((resolve) => {setTimeout(() => {resolve(`数据: ${url}`);}, 1000);});
}// 使用Generator处理异步
function* getData() {console.log('开始请求数据1');const data1 = yield fetchData('url1');console.log('获取到数据1:', data1);console.log('开始请求数据2');const data2 = yield fetchData('url2');console.log('获取到数据2:', data2);return '所有数据获取完成';
}// 执行生成器
function runGenerator(gen) {const iterator = gen();function handleResult(result) {if (result.done) {console.log('最终结果:', result.value);return;}// 处理Promiseresult.value.then(data => {handleResult(iterator.next(data));});}handleResult(iterator.next());
}// 运行
runGenerator(getData);
4. 闭包
核心知识点
- 闭包是函数及其捆绑的周边环境状态的引用的组合
- 形成条件:函数嵌套、内部函数引用外部函数的变量、内部函数被外部引用
- 作用:实现私有变量、模块化、柯里化、保存状态等
- 注意:不当使用可能导致内存泄漏
面试题:实现防抖函数
面试题:实现节流函数
function throttle(fn, interval) {let lastTime = 0;let timer = null;return function(...args) {const context = this;const now = Date.now();const remainingTime = interval - (now - lastTime);// 如果时间间隔已到,直接执行if (remainingTime <= 0) {if (timer) {clearTimeout(timer);timer = null;}fn.apply(context, args);lastTime = now;} else if (!timer) {// 否则设置定时器,确保最后一次一定会执行timer = setTimeout(() => {fn.apply(context, args);lastTime = Date.now();timer = null;}, remainingTime);}};
}// 使用示例
const handleScroll = () => {console.log('滚动事件触发');
};// 节流处理,每100ms最多执行一次
const throttledScroll = throttle(handleScroll, 100);// 监听滚动事件
window.addEventListener('scroll', throttledScroll);
5. 异步与事件循环
核心知识点
- JavaScript 是单线程语言,通过事件循环实现异步
- 事件循环:调用栈 -> 微任务队列 -> 宏任务队列 -> UI渲染
- 微任务优先级高于宏任务,常见微任务:Promise.then/catch/finally、process.nextTick、MutationObserver
- 常见宏任务:setTimeout、setInterval、DOM事件、I/O操作、setImmediate
面试题:事件循环输出题
console.log('1');setTimeout(function() {console.log('2');new Promise(function(resolve) {console.log('3');resolve();}).then(function() {console.log('4');});
}, 0);new Promise(function(resolve) {console.log('5');resolve();
}).then(function() {console.log('6');
});setTimeout(function() {console.log('7');new Promise(function(resolve) {console.log('8');resolve();}).then(function() {console.log('9');});
}, 0);console.log('10');// 输出顺序:1 5 10 6 2 3 4 7 8 9
6. Map
核心知识点
- Map 是 ES6 新增的键值对集合
- 与对象相比,Map 的键可以是任意类型,而对象的键只能是字符串或 Symbol
- 常用方法:set()、get()、has()、delete()、clear()、size 属性
- 可以通过 for…of 直接迭代,迭代顺序是插入顺序
- 适合用于频繁添加/删除键值对的场景
面试题:实现 Map 的基本功能
7. 数组
核心知识点
- 数组是有序的元素集合,具有动态长度
- 常用方法:push()、pop()、shift()、unshift()、slice()、splice() 等
- 高阶函数:map()、filter()、reduce()、forEach()、find()、some()、every() 等
- 数组去重、扁平化、排序是常见操作
面试题:数组扁平化
// 方法1:使用递归
function flatten(arr, depth = Infinity) {if (depth <= 0) return arr.slice();return arr.reduce((acc, item) => {if (Array.isArray(item)) {// 递归扁平化,并减少深度return acc.concat(flatten(item, depth - 1));} else {return acc.concat(item);}}, []);
}// 方法2:使用ES6的flat方法
// const flattened = arr.flat(depth);// 测试
const nestedArray = [1, [2, [3, [4]], 5]];
console.log(flatten(nestedArray)); // [1, 2, 3, 4, 5]
console.log(flatten(nestedArray, 1)); // [1, 2, [3, [4]], 5]
面试题:数组去重
以上梳理了前端核心知识点及常见面试题,涵盖了Promise、原型链、生成器、闭包、异步、事件循环、Map和数组等内容。这些知识点不仅是面试高频考点,也是日常开发中经常用到的核心概念,掌握这些内容对于前端工程师至关重要。