防抖
防抖思路:在规定时间内未触发第二次,则执行
function debounce(fn, delay) {
let timer = null;
//利用闭包保存定时器
return function () {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
// fn();
}, delay);
};
}
function fn () {
console.log('防抖---')
}
addEventListener('scroll', debounce2(fn, 1000))
节流
触发高频事件,且 N 秒内只执行一次。
function throttle(fn, delay) {
let lastTime = 0;
return function () {
let nowTime = new Date();
if (nowTime - lastTime > delay) {
fn.apply(this, arguments);
lastTime = nowTime;
}
};
}
function fn() {
console.log("防抖---",new Date());
}
addEventListener("scroll", throttle(fn, 3000));
// 基础版1:时间戳(第一次触发会执行,但不排除不执行的可能,请思考一下哦)
function throttle(fn, delay) {
var prev = Date.now()
return function(...args) {
var dist = Date.now() - prev
if (dist >= delay) {
fn.apply(this, args)
prev = Date.now()
}
}
}
// 基础版2:定时器(最后一次也会执行)
function throttle(fn, delay) {
var timer = null
return function(...args) {
var that = this
if(!timer) {
timer = setTimeout(function() {
fn.apply(this, args)
timer = null
}, delay)
}
}
}
// 进阶版:开始执行、结束执行
function throttle(fn, delay) {
var timer = null
var prev = Date.now()
return function(...args) {
var that = this
var remaining = delay - (Date.now() - prev) // 剩余时间
if (remaining <= 0) { // 第 1 次触发
fn.apply(that, args)
prev = Date.now()
} else { // 第 1 次之后触发
timer && clearTimeout(timer)
timer = setTimeout(function() {
fn.apply(that, args)
}, remaining)
}
}
}
function fn () {
console.log('节流')
}
addEventListener('scroll', throttle(fn, 1000))
懒加载
- 在页面放置img标签
- 给img图片加上alt, width, height 和 data-src
- 通过js判断页面是否滚动到某张图片需要显示的位置,这时将src赋值为data-src
- 判断图片距离顶部的距离大于可视区域和滚动区域之和时懒加载。
<ul>
<li><img src="./imgs/default.png" data="./imgs/1.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/2.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/3.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/4.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/5.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/6.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/7.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/8.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/9.png" alt=""></li>
<li><img src="./imgs/default.png" data="./imgs/10.png" alt=""></li>
</ul>
let imgs = document.querySelectorAll('img')
// 可视区高度
let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
function lazyLoad () {
// 滚动卷去的高度
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
for (let i = 0; i < imgs.length; i ++) {
// 图片在可视区冒出的高度
let x = clientHeight + scrollTop - imgs[i].offsetTop
// 图片在可视区内
if (x > 0 && x < clientHeight+imgs[i].height) {
imgs[i].src = imgs[i].getAttribute('data')
}
}
}
// addEventListener('scroll', lazyLoad) or setInterval(lazyLoad, 1000)
与普通的图片懒加载不同,如下这个多做了 2 个精心处理:
- 图片全部加载完成后移除事件监听;
- 加载完的图片,从 imgList 移除;
let imgList = [...document.querySelectorAll('img')]
let length = imgList.length
// 修正错误,需要加上自执行
const imgLazyLoad = (function() {
let count = 0
return function() {
let deleteIndexList = []
imgList.forEach((img, index) => {
let rect = img.getBoundingClientRect()
if (rect.top < window.innerHeight) {
img.src = img.dataset.src
deleteIndexList.push(index)
count++
if (count === length) {
document.removeEventListener('scroll', imgLazyLoad)
}
}
})
imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
}
})()
// 这里最好加上防抖处理
document.addEventListener('scroll', imgLazyLoad)
实现url参数转json
var getQueryJson = function (url) {
let arr = [];
let res = {};
let jsonstr = url.split("?")[1].split("#")[0]; //选取?和#中间的json串
arr = jsonstr.split("&"); //获取浏览器地址栏中的参数
for (let i = 0; i < arr.length; i++) {
if (arr[i].indexOf("=") != -1) {
let str = arr[i].split("=");
res[str[0]] = str[1];
} else {
res[arr[i]] = "";
}
}
return res;
};
var getQueryJson2 = function (url) {
let param = {}; // 存储最终JSON结果对象
url.replace(/([^?&=]+)=([^?&=]*)/g, function (s, v, k) {
param[v] = decodeURIComponent(k); //解析字符为中文
return k + "=" + v;
});
return param;
};
console.log(
getQueryJson(
"http://127.0.0.1:8080/html/urltojson.html?id=1&name=good&price=#hash"
)
);
异步任务调度器
//调度器类
class Scheduler {
constructor(maxnum) {
this.taskList = []; //承载还未执行的异步任务
this.count = 0; //计数
this.maxNum = maxnum; //允许同时运行的异步函数的最大个数
}
async add(fn) {
if (this.count >= this.maxNum) {
await new Promise((resolve) => {
this.taskList.push(resolve);
});
}
this.count++;
const result = await fn();
this.count--;
if (this.taskList.length > 0) {
this.taskList.shift()();
}
return result;
}
}
const schedule = new Scheduler(2); //最多同一时间让它执行2个异步函数
//定义定时器
const timeOut=(time)=>{
return new Promise(resolve=>{
setTimeout(resolve,time)
})
}
//定义异步任务执行器
const addTask=(time,order)=>{
schedule.add(()=>{return timeOut(time).then(()=>{
console.log('异步任务',order)
})})
}
//输出顺序:2 3 1 4
addTask(1000,'1')
addTask(500,'2')
addTask(300,'3')
addTask(400,'4')
// 一开始,1、2两个任务进入队列
// 500ms时,2完成,输出2,任务3进队
// 800ms时,3完成,输出3,任务4进队
// 1000ms时,1完成,输出1
// 1200ms时,4完成,输出4
后两个函数写在一起
//定时器在内部的异步任务执行器(写在一起)
const addTaskByTime = (time, order) => {
schedule.add(() => {
return new Promise((resolve) => {
setTimeout(resolve, time);
}).then(() => {
console.log("异步任务", order);
});
});
};
//输出顺序:2 3 1 4
addTaskByTime(1000, "1");
addTaskByTime(500, "2");
addTaskByTime(300, "3");
addTaskByTime(400, "4");