Skip to content

常面常新-0918

Promise.all()

Promise.allJavaScript中用于处理多个Promise并发执行的常用方法。会在所有的 Promise 都解决后返回一个按顺序的结果数组。如果有一个Promise失败,Promise.all就会立即失败。

  • 代码实现:
js
function myPromiseAll(promises) {
  return new Promise((resolve, reject) => {
    // 检查是否传入了一个 iterable 类型的参数
    if (!Array.isArray(promises)) {
      return reject(new TypeError("Argument must be an array"));
    }

    const results = []; // 用于存储每个 promise 的结果
    let completedCount = 0; // 记录已完成的 promise 数量

    // 如果传入的是一个空数组,直接返回一个已解决的 promise
    if (promises.length === 0) {
      return resolve(results);
    }

    // 遍历每个 promise
    promises.forEach((promise, index) => {
      // 确保每个 item 都是一个 promise
      Promise.resolve(promise)
        .then((value) => {
          results[index] = value; // 保存结果
          completedCount++; // 计数器增加

          // 当所有的 promise 都完成时,resolve 外部的 promise
          if (completedCount === promises.length) {
            resolve(results);
          }
        })
        .catch((err) => {
          // 只要有一个 promise 被 reject,立即 reject
          reject(err);
        });
    });
  });
}

浅拷贝和深拷贝

  • 浅拷贝只复制对象的第一层属性,对于对象中的引用类型(如数组或对象)只复制其引用。也就是说,浅拷贝后的新对象中的嵌套对象与原对象共享同一个内存地址,对嵌套对象的修改会影响原对象。

  • 常见的浅拷贝方法:

    • 使用Object.assign()
    • 使用展开运算符...
  • 深拷贝则会递归地复制对象的所有属性,甚至是嵌套的对象,确保新对象与原对象完全独立。深拷贝的对象与原对象在内存中是完全分离的,修改其中一个不会影响另一个。

  • 常见的深拷贝方法:

    • 使用 JSON.parse(JSON.stringify())
    • 递归遍历对象
    • 使用第三方库如Lodash _.cloneDeep()

深拷贝中出现循环依赖怎么办

常见的解决办法是使用弱引用集合(WeakMap)来跟踪已经拷贝过的对象

  • 代码实现
js
function deepClone(obj, hash = new WeakMap()) {
  // 基本类型直接返回
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // 如果对象已经被拷贝过,直接返回拷贝的结果,防止循环引用
  if (hash.has(obj)) {
    return hash.get(obj);
  }

  // 处理数组或对象
  const clone = Array.isArray(obj) ? [] : {};

  // 将当前对象存入 hash,避免后续循环引用
  hash.set(obj, clone);

  // 遍历对象并递归拷贝
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], hash);
    }
  }

  return clone;
}

v-lazy

懒加载的基本工作原理是:当某个元素进入视口(也就是用户可见区域)时,才开始加载资源。这样可以避免页面在初始加载时加载所有图片或资源,节省带宽并加速页面渲染。

怎么判断图像是否到可视区域

  • 使用IntersectionObserver API
js
// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver(
  (entries, observer) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        // 图像进入可视区域
        console.log("图像进入可视区域");
        // 在这里可以加载图像或执行其他操作
        const img = entry.target;
        img.src = img.dataset.src; // 假设使用 data-src 存储实际图像 URL
        observer.unobserve(img); // 停止观察该图像
      }
    });
  },
  {
    root: null, // 默认为视口
    rootMargin: "0px",
    threshold: 0.1, // 触发回调的阈值,0.1 表示目标元素 10% 可见时触发
  }
);

// 获取所有需要懒加载的图像
const lazyImages = document.querySelectorAll("img.lazy");

// 开始观察每个图像
lazyImages.forEach((img) => {
  observer.observe(img);
});
  • 使用传统的滚动事件和元素的位置 可以使用传统的滚动事件来检测元素是否进入视口。这个方法可能会影响性能,因为每次滚动事件触发时都需要检查元素的位置。

    js
    function isElementInViewport(el) {
      const rect = el.getBoundingClientRect();
      return (
        rect.top >= 0 &&
        rect.left >= 0 &&
        rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
        rect.right <= (window.innerWidth || document.documentElement.clientWidth)
      );
    }
    
    function lazyLoadImages() {
      const images = document.querySelectorAll("img[data-src]");
      images.forEach((img) => {
        if (isElementInViewport(img)) {
          img.src = img.dataset.src;
          img.removeAttribute("data-src");
        }
      });
    }
    
    // 监听滚动事件
    window.addEventListener("scroll", lazyLoadImages);
    window.addEventListener("resize", lazyLoadImages); // 处理窗口尺寸变化
    lazyLoadImages(); // 页面加载时立即检查
  • 使用第三方库,比如lazysizes

js
<!-- 引入 lazysizes -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lazysizes/5.3.2/lazysizes.min.js" async></script>

<!-- 使用 lazysizes 的 data-src 属性 -->
<img data-src="image.jpg" class="lazyload" alt="Lazy Loaded Image">

如有转载或 CV 的请标注本站原文地址