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 顶部高度 top

    top =
      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 脚本尽可能的在最后来加载执行。
powered by Gitbook该文件修订时间: 2024-07-01 17:50:08

results matching ""

    No results matching ""