防抖和节流
防抖(Debouncing):
防抖是指在短时间内连续触发同一事件时,只执行最后一次触发的事件处理函数。
在实际应用中,常常用于处理用户输入的搜索框或者滚动事件。例如,当用户连续输入搜索关键词时,如果没有防抖处理,每次输入都会触发搜索请求,造成不必要的请求发送和资源浪费。而通过防抖技术,可以等待一定的时间间隔,只有在用户停止输入后才触发搜索请求,从而减少请求次数,提高性能。
节流(Throttling):
节流是指在一定时间间隔内只执行一次事件处理函数。
与防抖不同的是,节流不会等待最后一次触发事件,而是在规定的时间间隔内执行事件处理函数,然后忽略剩余的触发事件。
节流常用于处理滚动事件、窗口调整大小事件等频繁触发的事件。例如,在网页中,当用户快速滚动页面时,如果没有节流处理,滚动事件会频繁触发,影响页面性能和流畅度。而通过节流技术,可以限制滚动事件的触发频率,使页面能够更加平滑地滚动。
代码实现:
/** * 防抖函数,在一定时间内只执行一次函数,避免函数因频繁触发而过度消耗性能 * * @param func 要防抖的函数 * @param wait 等待时间,单位为毫秒 * @returns 返回防抖后的函数 */ function debounce(func, wait) { // 定义一个变量timeoutId,用于存储setTimeout的返回值 let timeoutId; // 返回一个函数 return function () { // 获取当前函数的上下文和参数 const context = this; const args = [...arguments]; // 如果timeoutId存在,则清除之前的setTimeout定时器 if (timeoutId) clearTimeout(timeoutId); // 设置一个新的setTimeout定时器,等待wait毫秒后执行func函数,并将上下文和参数传递给func函数 timeoutId = setTimeout(() => { func.apply(context, args); }, wait); }; } // 节流函数 /** * 节流函数,限制函数的执行频率 * * @param func 要进行节流的函数 * @param wait 两次执行之间的时间间隔,单位毫秒 * @returns 返回一个新的函数,该函数在wait毫秒内只执行一次func函数 */ function throttle(func, wait) { // 定义一个变量lastTime,用于存储上一次触发的时间 let lastTime = 0; // 返回一个函数 return function () { // 获取当前函数的上下文和参数 const context = this; const args = [...arguments]; // 获取当前时间戳 const now = Date.now(); // 如果距离上次触发的时间间隔大于wait毫秒,则执行func函数,并更新lastTime为当前时间戳 if (now - lastTime >= wait) { func.apply(context, args); lastTime = now; } }; } /** * 节流函数,限制函数的执行频率 * * @param func 要进行节流的函数 * @param interval 两次执行之间的时间间隔,单位毫秒 * @returns 返回一个新的函数,该函数在 interval 毫秒内只执行一次 func 函数 */ function throttle(func, interval) { // 定义一个定时器ID let timeoutId; // 返回一个新的函数 return function (...args) { // 获取当前上下文 const context = this; // 如果定时器ID不存在 if (!timeoutId) { // 设置定时器 timeoutId = setTimeout(() => { // 在定时器回调函数中执行原始函数,并传入参数 func.apply(context, args); // 将定时器ID置为null timeoutId = null; }, interval); } }; }
原生JS实现懒加载
方案一
使用getBoundingClientRect()方法用于获取元素的大小及其相对于视口的位置信息
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Lazy Loading Example</title> <style> .lazy-img { width: 100%; height: 200px; background: #ccc; margin: 10px 0; } </style> </head> <body> <div class="lazy-img-container"> <img class="lazy-img" data-src="image1.jpg" alt="Image 1"> <img class="lazy-img" data-src="image2.jpg" alt="Image 2"> <img class="lazy-img" data-src="image3.jpg" alt="Image 3"> <!-- 更多图片 --> </div> <script> document.addEventListener("DOMContentLoaded", function() { var lazyImages = document.querySelectorAll('.lazy-img'); var lazyLoad = function() { lazyImages.forEach(function(img) { if (img.getBoundingClientRect().top < window.innerHeight && !img.src) { img.src = img.dataset.src; } }); }; // 第一次加载页面时执行一次懒加载 lazyLoad(); // 滚动事件触发时检查是否需要加载图片 window.addEventListener('scroll', lazyLoad); }); </script> </body> </html>
方案二
通过计算得到一些数据
(1) window.innerHeight 是浏览器可视区的高度
(2) document.body.scrollTop || document.documentElement.scrollTop是浏览器滚动的过的距离
(3) imgs.offsetTop 是元素顶部距离文档顶(3)部的高度(包括滚动条的距离)
(4) 图片加载条件:img.offsetTop - document.body.scrollTop< window.innerHeight ;图示:
判断打印结果(参数传递修改,创建实例)
解释:
在这段代码中,首先创建了一个名为
p1
的对象,该对象包含了name
和age
属性。然后定义了一个名为test
的函数,该函数接受一个参数person
。在函数内部,首先修改了传入的person
对象的age
属性为26
,然后又重新赋值了person
对象为一个新的对象{ name: 'hzj', age: 18 }
。最后返回了这个新对象。在函数外部,调用
test
函数时将p1
对象传入,并将返回值赋给了变量p2
。因为 JavaScript 中的对象是按引用传递的,所以当在函数内部修改person
对象时,实际上是在修改传入的对象的引用,因此p1
对象的age
属性也会被修改为26
。但是在重新赋值person
对象后,p1
对象不会受到影响,因为此时person
变量指向了一个新的对象。因此最终输出p1
对象时,其age
属性为26
;而输出p2
对象时,其age
属性为18
。
const p1 = {
name: 'fyg',
age: 19
};
function test(person) {
person.age = 26;
person = {
name: 'hzj',
age: 18
};
return person;
}
const p2 = test(p1);
console.log(p1); // { name: 'fyg', age: 26 }
console.log(p2); // { name: 'hzj', age: 18 }