前端面试宝典总结4之手写代码JavaScript(场景篇)
本文章 对各大学习技术论坛知识点,进行总结、归纳自用学习,共勉🙏
上一篇👉: 前端面试宝典总结4-手搓代码JavaScript(基础篇)
1.深拷贝: 当你需要完全复制一个对象,包括它的嵌套对象时,避免引用造成的数据篡改。
function deepClone(obj, hash = new WeakMap()) {
if (obj == null) return obj; // 处理 null 和 undefined
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (hash.has(obj)) return hash.get(obj); // 处理循环引用
let Constructor = obj.constructor;
let newObj = new Constructor();
hash.set(obj, newObj);
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 只遍历实例上的属性
newObj[key] = deepClone(obj[key], hash);
}
}
return newObj;
}
2.用Promise实现图片的异步加载
let imageAsync = (url) => {
return new Promise((resolve, reject) => {
let img = new Image();
img.src = url;
img.onload = () => {
console.log(`图片请求成功,此处进行通用操作`);
resolve(img);
}
img.onerror = (err) => {
console.log(`失败,此处进行失败的通用操作`);
reject(err);
}
});
};
imageAsync("url").then((img) => {
console.log("加载成功");
}).catch((error) => {
console.log("加载失败");
});
3.实现发布-订阅模式
class PubSub {
constructor() {
this.events = {};
}
subscribe(event, handler) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(handler);
}
publish(event, data) {
if (this.events[event]) {
this.events[event].forEach(handler => handler(data));
}
}
unsubscribe(event, handler) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(h => h !== handler);
}
}
}
// 使用示例
const pubsub = new PubSub();
pubsub.subscribe('news', data => console.log('Breaking News:', data));
pubsub.publish('news', 'New Article Published!');
4. 实现一个简单的事件监听与触发系统
class EventBus {
constructor() {
this.events = {};
}
on(eventName, listener) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(listener);
}
emit(eventName, data) {
const listeners = this.events[eventName];
if (listeners) {
listeners.forEach(listener => listener(data));
}
}
off(eventName, listener) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(l => l !== listener);
}
}
}
// 使用示例
const bus = new EventBus();
bus.on('login', data => console.log('User logged in:', data));
bus.emit('login', { username: 'John Doe' });
5.实现一个简单的节流(Throttle)函数
function throttle(func, delay) {
let lastExecution = 0;
return function(...args) {
const now = Date.now();
if (now - lastExecution >= delay) {
lastExecution = now;
return func.apply(this, args);
}
}
}
// 使用示例
const logMessage = throttle(console.log, 1000);
logMessage('Hello');
setTimeout(() => logMessage('World'), 500);
setTimeout(() => logMessage('Again'), 1500);
6.实现一个简单的深合并函数(Deep Merge)
function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
function deepMerge(target, source) {
Object.keys(source).forEach(key => {
const targetValue = target[key];
const sourceValue = source[key];
if (isObject(targetValue) {
target[key] = deepMerge(Object.assign({}, targetValue), sourceValue);
} else {
target[key] = sourceValue;
}
});
return target;
}
// 使用示例
const obj1 = { a: 1, b: { c: 2 } };
const obj2 = { b: { d: 3 }, e: 4 };
const merged = deepMerge(obj1, obj2);
console.log(merged); // 输出: { a: 1, b: { c: 2, d: 3 }, e: 4 }
7.实现一个简单的计时器(倒计时器)
用户需要一个简单的倒计时器,从特定秒数开始倒数,每秒更新一次直到0。
function countdown(seconds, onUpdate, onFinish) {
let remaining = seconds;
const intervalId = setInterval(() => {
if (remaining <= 0) {
clearInterval(intervalId);
if (typeof onFinish === 'function') onFinish();
return;
}
onUpdate(remaining--);
}, 1000);
}
// 使用示例
countdown(10, time => console.log(`Time Remaining: ${time}s`), () => console.log('Countdown Finished!'));
8.实现一个简单的轮询请求,直到满足条件
场景描述:在某些场景中,比如等待远程资源变为可用,需要不断地发起请求直到某个条件满足或达到最大尝试次数。
function pollUntil(conditionFn, pollFn, interval = 1000, maxAttempts = -1, attempt = 0) {
return new Promise((resolve, reject) => {
const checkCondition = () => {
if (conditionFn()) {
resolve();
} else if (maxAttempts !== -1 && attempt >= maxAttempts) {
reject(new Error('Polling stopped after maximum attempts reached'));
} else {
attempt++;
setTimeout(checkCondition, interval);
}
};
checkCondition();
});
}
// 使用示例
// 假设有一个检查资源是否准备好的函数 checkResourceReady
const checkResource = () => /* 返回 true/false 表示资源是否准备好 */;
pollUntil(checkResource, () => console.log('Checking...'), 2000, 10)
.then(() => console.log('Resource is ready!'))
.catch(err => console.error('Polling ended with error:', err));
9. 实现一个简单的缓存机制(LRU Cache)
对于昂贵的计算或频繁查询的API调用,实现一个简单的缓存策略,使得最近使用的数据能在短时间内被快速获取。
class LRUCache {
constructor(capacity = 5) {
this.capacity = capacity;
this.cache = new Map();
this.keysOrder = [];
}
get(key) {
if (this.cache.has(key)) {
// 更新访问顺序
this.keysOrder.splice(this.keysOrder.indexOf(key), 1);
this.keysOrder.push(key);
return this.cache.get(key);
}
return null;
}
put(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
this.keysOrder.splice(this.keysOrder.indexOf(key), 1);
}
if (this.cache.size >= this.capacity) {
const leastRecentlyUsedKey = this.keysOrder.shift();
this.cache.delete(leastRecentlyUsedKey);
}
this.cache.set(key, value);
this.keysOrder.push(key);
}
}
// 使用示例
const cache = new LRUCache(2);
cache.put('A', 'Apple');
cache.put('B', 'Banana');
cache.get('A'); // 返回 'Apple'
cache.put('C', 'Cherry'); // 自动移除最久未使用的项,这里是 'A'
cache.get('A'); // 返回 null,因为'A'已经被移除
10.实现每隔一秒打印 1,2,3,4
// 使用闭包实现
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, i * 1000);
})(i);
}
// 使用 let 块级作用域
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
let i = 1;
setInterval(() => {
console.log(i++);
if (i > 4) {
i = 1; // 重置计数器
}
}, 1000);