1. 如何用 JavaScript 的 Proxy 实现数据的双向绑定?写出关键代码并说明其与 Vue 4.0 的响应式系统的差异。
关键代码实现:
ini
// 数据模型
const data = { value: "" };
// 监听数据变化的回调函数集合
const callbacks = new Set();
// 创建 Proxy 代理
const proxy = new Proxy(data, {
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
// 触发所有回调(模拟视图更新)
callbacks.forEach(cb => cb());
return result;
}
});
// 视图绑定(假设有一个 input 元素)
const input = document.querySelector("input");
input.addEventListener("input", (e) => {
proxy.value = e.target.value; // 数据变化触发 Proxy.set
});
// 注册更新视图的回调
callbacks.add(() => {
input.value = proxy.value; // 数据到视图的绑定
});
与 Vue 4.0 的差异:
-
依赖追踪:
- Proxy 实现 :需要手动管理依赖(如示例中的
callbacks
),无法自动追踪数据与视图的关联。 - Vue 4.0 :通过
effect
和track
/trigger
实现自动依赖收集,精确更新相关组件。
- Proxy 实现 :需要手动管理依赖(如示例中的
-
性能优化:
- Proxy 实现:每次数据变化都会触发所有回调,可能造成不必要的渲染。
- Vue 4.0:使用虚拟 DOM 和异步批处理更新,减少实际 DOM 操作次数。
-
数据类型支持:
- Proxy 实现 :对数组的
push
/pop
等方法需要额外处理。 - Vue 4.0:重写了数组方法,确保能够触发响应式更新。
- Proxy 实现 :对数组的
-
嵌套对象:
- Proxy 实现:需要递归代理嵌套对象。
- Vue 4.0:通过惰性代理(按需转换)优化性能。
2. 设计一个 JavaScript 的异步任务调度器,支持任务优先级调度和超时熔断。
关键代码实现:
kotlin
class TaskScheduler {
constructor(concurrency = 2) {
this.queue = [];
this.running = 0;
this.concurrency = concurrency;
}
add(task, priority = 0, timeout = 5000) {
const taskWrapper = {
task: () => Promise.race([
task(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Timeout")), timeout)
)
]),
priority
};
// 按优先级插入队列(数值越大优先级越高)
const index = this.queue.findIndex(t => t.priority < priority);
if (index === -1) this.queue.push(taskWrapper);
else this.queue.splice(index, 0, taskWrapper);
this.run();
}
run() {
while (this.running < this.concurrency && this.queue.length > 0) {
const { task } = this.queue.shift();
this.running++;
task()
.catch(err => console.error("Task failed:", err))
.finally(() => {
this.running--;
this.run();
});
}
}
}
// 使用示例
const scheduler = new TaskScheduler();
scheduler.add(() => fetch("/api/data"), 2, 3000); // 高优先级
scheduler.add(() => console.log("Low priority task"), 0);
核心特性:
- 优先级调度:通过插入排序实现优先级队列。
- 超时熔断 :使用
Promise.race
实现任务超时自动拒绝。 - 并发控制:限制同时运行的任务数量。
3. 如何用 JavaScript 的 Intersection Observer 实现无限滚动列表?写出关键代码并说明其性能优化策略。
关键代码实现:
ini
let page = 1;
const observer = new IntersectionObserver((entries) => {
if (entries.isIntersecting) {
loadMore();
}
}, { threshold: 0.1 });
// 初始占位元素
const sentinel = document.createElement("div");
document.body.appendChild(sentinel);
observer.observe(sentinel);
async function loadMore() {
observer.unobserve(sentinel); // 防止重复触发
const data = await fetch(`/api/items?page=${page++}`).then(res => res.json());
renderItems(data);
sentinel.scrollIntoView({ behavior: "smooth" });
observer.observe(sentinel); // 重新观察
}
性能优化策略:
- 虚拟列表 :只渲染可视区域内的元素(如使用
react-window
库)。 - 请求防抖:确保滚动结束时才触发加载。
- 内存管理:移除视口外的 DOM 元素。
- 缓存策略:缓存已加载的数据,避免重复请求。
4. 在 JavaScript 中,如何通过 Web Workers 实现多线程计算?写出关键代码并说明其与主线程的通信机制。
关键代码实现:
主线程代码:
javascript
const worker = new Worker("worker.js");
// 发送数据到 Worker
worker.postMessage({ type: "CALC", data: 1000000 });
// 接收 Worker 结果
worker.onmessage = (e) => {
console.log("Result:", e.data.result);
};
// 错误处理
worker.onerror = (err) => console.error("Worker error:", err);
worker.js:
javascript
self.onmessage = (e) => {
if (e.data.type === "CALC") {
const result = heavyCalculation(e.data.data);
self.postMessage({ result });
}
};
function heavyCalculation(n) {
// 模拟耗时计算
return Array.from({ length: n }, (_, i) => i).reduce((a, b) => a + b);
}
通信机制:
- 数据传递 :通过
postMessage
传递结构化克隆或 Transferable 对象。 - 无 DOM 访问:Worker 线程无法操作 DOM。
- 异步通信:消息传递是非阻塞的。
5. 如何用 JavaScript 的 WebSocket 实现实时消息推送?写出关键代码并说明其与 HTTP 长轮询的优劣。
关键代码实现:
javascript
const socket = new WebSocket("wss://api.example.com/ws");
socket.onopen = () => {
socket.send(JSON.stringify({ subscribe: "updates" }));
};
socket.onmessage = (e) => {
console.log("New message:", JSON.parse(e.data));
};
socket.onclose = () => {
console.log("Connection closed");
};
// 发送消息
document.querySelector("button").addEventListener("click", () => {
socket.send(JSON.stringify({ message: "Hello" }));
});
对比 HTTP 长轮询:
特性 | WebSocket | HTTP 长轮询 |
---|---|---|
连接类型 | 全双工持久连接 | 半双工,每次请求后关闭 |
延迟 | 低(无需频繁握手) | 较高(每次请求需要重新建立连接) |
服务器推送 | 支持 | 需等待客户端轮询 |
资源消耗 | 较少(维持单一连接) | 较高(频繁连接/断开) |
浏览器兼容性 | IE10+ | 所有浏览器 |
数据传输效率 | 高效(无 HTTP 头开销) | 较低(每次请求携带完整头信息) |
选择建议:需要高频双向通信(如聊天室)用 WebSocket;低频场景(如邮件通知)可用长轮询。