刷刷题50(常见的js数据通信与渲染问题)

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 的差异:

  1. ‌依赖追踪‌:

    • ‌Proxy 实现 ‌:需要手动管理依赖(如示例中的 callbacks),无法自动追踪数据与视图的关联。
    • ‌Vue 4.0 ‌:通过 effect 和 track/trigger 实现自动依赖收集,精确更新相关组件。
  2. ‌性能优化‌:

    • ‌Proxy 实现‌:每次数据变化都会触发所有回调,可能造成不必要的渲染。
    • ‌Vue 4.0‌:使用虚拟 DOM 和异步批处理更新,减少实际 DOM 操作次数。
  3. ‌数据类型支持‌:

    • ‌Proxy 实现 ‌:对数组的 push/pop 等方法需要额外处理。
    • ‌Vue 4.0‌:重写了数组方法,确保能够触发响应式更新。
  4. ‌嵌套对象‌:

    • ‌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); // 重新观察
}

性能优化策略:

  1. ‌虚拟列表 ‌:只渲染可视区域内的元素(如使用 react-window 库)。
  2. ‌请求防抖‌:确保滚动结束时才触发加载。
  3. ‌内存管理‌:移除视口外的 DOM 元素。
  4. ‌缓存策略‌:缓存已加载的数据,避免重复请求。

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;低频场景(如邮件通知)可用长轮询。

相关推荐
Rain的Java大神之路几秒前
别再上来就堆 Redis!点赞系统从打挂数据库到 18w 写峰值,我把这套方案讲透了
java·后端·面试
试图让你心动4 分钟前
【无标题】
前端·javascript·vue.js
Hilaku6 分钟前
为什么每个前端都应该拥有一个自己的独立博客?
前端·javascript·程序员
燕落南枝10 分钟前
CSS 后代选择器与子代选择器:从入门到实战
前端·css
Amos_Web13 分钟前
Rspack 源码解析(二十八):HtmlRspackPlugin 如何把模板变成带资源标签的最终产物
前端
马可家的菠萝16 分钟前
个人知识库怎么建?从一个正在学的技术主题开始
前端·笔记·知识管理·个人知识库
几何心凉16 分钟前
端到端具身交互智能正式上线:让 AI 通过屏幕和机器人,真正走进现实世界
前端·人工智能·实时互动·机器人·交互
木圭的AI时代指南22 分钟前
实测 MiniMax M3.1 Flash:同一道 Three.js 大题,两个 agent 差出 4.6 倍
开发语言·javascript·ecmascript
西红柿炖牛腩35428 分钟前
3D 模型压缩脚本又报错又配环境?换个不用维护的方案
javascript·3d·性能优化
萧瑟余晖1 小时前
Spring Cloud Gateway详解
spring cloud·面试