防抖和节流
防抖(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) {// 定义一个定时器IDlet timeoutId;// 返回一个新的函数return function (...args) {// 获取当前上下文const context = this;// 如果定时器ID不存在if (!timeoutId) {// 设置定时器timeoutId = setTimeout(() => {// 在定时器回调函数中执行原始函数,并传入参数func.apply(context, args);// 将定时器ID置为nulltimeoutId = 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 }