常面常新-0918
Promise.all()
Promise.all
是JavaScript
中用于处理多个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);
});
使用传统的滚动事件和元素的位置 可以使用传统的滚动事件来检测元素是否进入视口。这个方法可能会影响性能,因为每次滚动事件触发时都需要检查元素的位置。
jsfunction 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">