1.undefined 类型 、null 类型
undefined 该类型只有一个值,即 undefined
- 一个声明的变量未赋值的状态时,值为 undefined
null 它表示一个空对象指针,故而使用 typeof 检测 null 值会返回, Object
- 如果定义一个变量将来准备用于保存对象,将该变量初始化为 null
2.代码优化
switch 用法
// case 条件判断
switch (true) {
case a === b:
break;
}
3.防抖、节流
当我们在 scroll 事件中执行事件处理函数时,每次 scroll 事件触发都会执行事件处理函数,这样无限制的调用执行很多次重复的操作,会加重浏览器的负担。此时我们可以采用 debunce(防抖)和 throttle(节流)的方式来减少事件调用频率,同时也不影响实际效果。
防抖函数
当频繁触发事件时,一定时间内没有再次触发事件,此时才会执行事件处理函数。
如果设定的时间内,又一次触发了事件,就清除之前的处理函数,重新延时。
let timeoutFun = null; window.addEventListener("scroll", () => { if (timeoutFun !== null) { clearTimeout(timeoutFun); } timeoutFun = setTimeout(() => { console.log("执行事件处理"); }, 1000); });
函数节流
使得一定时间内只出触发一次函数。
原理:通过判断是否到达一定时间来触发函数。
- 在节流函数内部使用开始时间 startTime 、当前时间 curTime 与 delay 来计算剩余时间 remaining。
- 当 remaining <= 0 时表示该执行事件处理函数了(保证了第一次触发事件就能立即执行事件处理函数和每隔 delay 时间执行一次事件处理函数)。
- 如果还没到时间的话就设定在 remaining 时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。
- 当然在 remaining 这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个 remaining 来判断当前状态。
let timer = null; let startTime = Date.now(); window.addEventListener("scroll", () => { let curTime = Date.now(); let remaining = 1000 - (curTime - startTime); clearTimeout(timer); if (remaining <= 0) { console.log("第一触发事件 执行函数"); startTime = Date.now(); } else { timer = setTimeout(() => { console.log("settimeout 执行函数"); }, remaining); } });
区别
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在 delay 时间后触发函数,但是在 delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
4.计算元素在滚动元素中的位置
方法一
Element.getBoundingClientRect()
获取元素的大小及其相对于视口的位置元素
currentNode
距离父容器parentNode
顶部高度 toptop = currentNode.getBoundingClientRect().top - parentNode.getBoundingClientRect().top;
方法二
currentNode.offsetTop
获取元素相对于其offsetParent
元素的顶部内边距的距离
5.分时函数
- 利用浏览器剩余时间进行渲染,不阻塞浏览器渲染
- 浏览器每秒渲染 60 帧,1 帧需要 16.67ms,优化代码核心保证每帧渲染时间不超过 16.67ms
- idle.timeRemaining 浏览器渲染每帧剩余时间
// 利用浏览器剩余时间进行渲染,不阻塞浏览器渲染
const performChunk = (data, task) => {
let i = 0;
const run = () => {
if (i >= data.length) return;
requestIdleCallback((idle) => {
// idle.timeRemaining 浏览器渲染每帧剩余时间
while (idle.timeRemaining() > 0 && i < data.length) {
task(data[i], i);
i++;
}
run();
});
};
run();
};
// 常规方式,会出现卡顿
const eachFun = (data, task) => {
data.forEach((o, i) => {
task(o, i);
});
};
const addEle = () => {
const parentEle = document.getElementById("addBox");
const task = (v, i) => {
const newEle = document.createElement("div");
newEle.innerHTML = `${i}`;
parentEle.appendChild(newEle);
};
const data = new Array(100000).fill(0);
// eachFun(data, task)
performChunk(data, task);
};
return (
<div onClick={addEle} id="addBox" className="pt-20 h-6">
插入元素
</div>
);
6.requestAnimationFrame
- 函数会在浏览器下一次重绘之前执行。
- requestAnimationFrame 是一次性的, 若想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>文字跑马灯</title>
<style>
html,
body {
margin: 0;
padding: 0;
width: 100%;
padding-top: 30px;
}
.textBox {
display: flex;
align-items: center;
position: relative;
width: 100%;
margin: 0;
height: 30px;
border-radius: 40px;
background: #ee9ca7; /* fallback for old browsers */
background: -webkit-linear-gradient(
to right,
#ffdde1,
#ee9ca7
); /* Chrome 10-25, Safari 5.1-6 */
background: linear-gradient(
to right,
#ffdde1,
#ee9ca7
); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
font-size: 12px;
color: #fff;
}
</style>
</head>
<body>
<div class="textBox">
<div class="text">文字跑马灯</div>
</div>
<script>
const text = document.querySelector(".text");
const textBox = document.querySelector(".textBox");
const width = text.offsetWidth;
const length = textBox.offsetWidth;
let left = 0;
const move = 1;
function write() {
if (left <= length) {
text.style.transform = `translateX(${left + "px"})`;
left += move;
} else {
left = -width;
text.style.transform = `translateX(${left + "px"})`;
}
window.requestAnimationFrame(write);
}
write();
</script>
</body>
</html>
7.Promise
- Promise.all
- 它接收一个数组,数组的每一项都是一个 promise 对象。
- 当数组中所有的 promise 的状态都达到 resolved 的时候,all 方法的状态就会变成 resolved
- 如果有一个状态变成了 rejected,那么 all 方法的状态就会变成 rejected。
- Promise.race
- 参数同 all 一样
- 不同的是,当最先执行完的事件执行完之后,就直接返回该 promise 对象的值。
- 如果第一个 promise 对象状态变成 resolved,那自身的状态变成了 resolved
- 反之第一个 promise 变成 rejected,那自身状态就会变成 rejected。
- 当要做一件事,超过多长时间就不做了,可以用这个方法来解决:
Promise.race([promise1, timeOutPromise(5000)]).then((res) => {});
8. js 脚本延迟加载的方式
- defer 属性:给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
- async 属性:给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js 脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
- 动态创建 DOM 方式:动态创建 DOM 标签的方式,可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
- 使用 setTimeout 延迟方法:设置一个定时器来延迟加载 js 脚本文件
- 让 JS 最后加载:将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。