刷刷题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 ‌:通过 effecttrack/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;低频场景(如邮件通知)可用长轮询。

相关推荐
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
uhakadotcom10 小时前
视频直播与视频点播:基础知识与应用场景
后端·面试·架构
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
快速开始使用 n8n
后端·面试·github
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github