没有比这个更详细的手写节流函数了吧

508 阅读11分钟

节流

这篇文章上接上一篇文章。这片节流的就没有写的那么详细了,但是主要功能是没有变化的。

上篇文章写的很详细,主要是在注释方面没有那么详细以及没有将自己手写的节流函数是怎样实现第三方js库underscore库来进行绑定自己的this和事件对象的。想要了解这方面的内容可以看一下上面一篇文章

没有比这个更详细的手写防抖函数了吧

节流的核心特点就是在不断触发事件的时候,每隔一段时间执行一次函数;

第一版、基本功能的实现

功能描述:在页面区域滑动,只要鼠标有滑动,count就会执行+1操作。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <script>
    let count = 1;
    let container = document.querySelector("#container")
    function doSomething(e) {
      container.innerHTML = count++;
    }
    container.onmousemove = doSomething // 我们自己写的普通的效果。注意这里的调用是没有()的。不是doSomething();
  </script>
</body>
</html>

第二版、第三方js库underscore.js的引用

如果看过我的上一篇文章,就会发现我这里还是引用的underscore.js的库。

没错,真想只有一个!那就是这个underscore.js封装了防抖函数_.debounce和节流函数 _.throttle。所以我们就引用了一个js

节流的功能主要是下面三种情况,第二点是防抖函数的核心:

  • 第一次鼠标进入滑动的时候count会执行+1的操作。这个可以取消第一次进入的+1操作,后续会做相关介绍;
  • 在鼠标不断滑动的时候,count会每隔1s来执行+1操作;这个就是节流的核心特点:在不断触发事件的时候,每隔一段时间执行一次函数;
  • 在鼠标推出滑动区域的时候,count还会执行一次+1操作。这个可以取消推出后还会执行+1的操作,后续会做相关介绍。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <!--导入第三方节流js库-->
  <script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
  <script>
    let count = 1;
    let container = document.querySelector("#container")
    function doSomething(e) {
      console.log(this)
      console.log(e)
      container.innerHTML = count++;
    }
    container.onmousemove = _.throttle(doSomething,1000) //这里是调用throttle函数。调用方式和防抖函数类似,都是采用下滑线加.的方式调用的
  </script>
</body>
</html>

第三版、第三方js库underscore.js的引用---首次加1,退出不加1

节流的功能主要是下面三种情况,第二点是防抖函数的核心:

  • 第一次鼠标进入滑动的时候count会执行+1的操作。
  • 在鼠标不断滑动的时候,count会每隔1s来执行+1操作;这个就是节流的核心特点:在不断触发事件的时候,每隔一段时间执行一次函数;
  • 在鼠标推出滑动区域的时候,count还会执行一次+1操作。

这种功能的实现主要是根据underscore.js的第三个参数来实现的。第三个参数是一个对象。值分别是leading和trailing,两个都是布尔值,leading控制首次,trailing控制退出。两个的搭配主要有三种效果

  • leading: true,trailing: false ---控制首次加1,退出不加1。这次我们采用这种配置看效果
  • leading: false, trailing: true ---控制首次不加1,退出加1;
  • Leading: true, trailing: true ---控制首次加1,退出也加1,也就是不穿参数的默认情况
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <!--导入第三方节流js库-->
  <script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
  <script>
    let count = 1;
    let container = document.querySelector("#container")
    function doSomething(e) {
      console.log(this)
      console.log(e)
      container.innerHTML = count++;
    }
    container.onmousemove = _.throttle(doSomething,1000,{  //这里是调用throttle函数。调用方式和防抖函数类似,都是采用下滑线加.的方式调用的
      leading: true,
      trailing: false
    })
  </script>
</body>
</html>

第四版、手写节流函数基本功能的实现(一)---首次加1,退出不加1

本次实现的功能主要是采用时间戳的方式来模拟下面这种情况的

  • 第一次鼠标进入滑动的时候count会执行+1的操作;
  • 在鼠标不断滑动的时候,count会每隔1s来执行+1操作;
  • 在鼠标推出滑动区域的时候,count不会执行一次+1操作,注意,这里是不会执行+1的操作

html文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>

</head>
<body>
  <div id="container"></div>
  <script src="throttle.js"></script>
  <script>
    let count = 0;
    function doSomething() {
      container.innerHTML = count++;
    }
    let container = document.querySelector("#container");
    container.onmousemove = thorttle(doSomething,1000);
  </script>
</body>
</html>

js文件

function throttle (func,wait) {
  let context,args; // 声明context用来后续指定this,args指定arguments
  let oldTimestamp = 0; // 之前的时间戳,默认是0
  return function(e) { // 同样是返回一个函数
    context = this; // 指定this。此时this是当前的容器,通过DOM操作获取的div。这两个的详细操作可以看我的上一篇文章
    args = arguments; // 指定e。此时e是当前的事件对象
    // 获取当前操作的时间戳
    var nowTimestamp = new Date().valueOf() // 表示当前开始滑动的时间戳,.valueOf()是用来获取日期的时间戳的
    if(nowTimestamp -oldTimestamp>wait) { // 判断一下nowTimestamp和oldTimestamp之间差值是不是大于1000ms,大于1000ms,就会执行下面count+1
      //立即执行。这里的立即执行指的是开始第一次进入的时候执行一次count+1的操作
      func.apply(context,args) // 通过apply绑定this和事件对象。
      oldTimestamp = nowTimestamp; // 将原来的nowTimestamp赋值给oldTimestamp。以为只有这样才会执行操作。你想一下,nowTimestamp在不断的随着时间的推移而增加,oldTimestamp还一直停留在0的时间。这肯定是不对的
    }

  }
}

第五版、第三方js库underscore.js的引用---首次不加1,退出加1

这种功能的实现主要是根据underscore.js的第三个参数来实现的。第三个参数是一个对象。值分别是leading和trailing,两个都是布尔值,leading控制首次,trailing控制退出。两个的搭配主要有三种效果

  • leading: true,trailing: false ---控制首次加1,退出不加1;
  • leading: false, trailing: true ---控制首次不加1,退出加1。这次我们采用这种配置看效果
  • Leading: true, trailing: true ---控制首次加1,退出也加1,也就是不穿参数的默认情况
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <!--导入第三方节流js库-->
  <script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
  <script>
    let count = 1;
    let container = document.querySelector("#container")
    function doSomething(e) {
      console.log(this)
      console.log(e)
      container.innerHTML = count++;
    }
    container.onmousemove = _.throttle(doSomething,1000,{  //这里是调用throttle函数。调用方式和防抖函数类似,都是采用下滑线加.的方式调用的
      leading: false,
      trailing: true
    })
  </script>
</body>
</html>

第六版、手写节流函数基本功能的实现(二)---首次不加1,退出加1

本次实现的功能主要是采用定时器的方式来模拟下面这种情况的

  • 第一次鼠标进入滑动的时候count会执行+1的操作;在鼠标第一次进入滑动区域的时候,count不会执行一次+1操作,注意,这里是不会执行+1的操作
  • 在鼠标不断滑动的时候,count会每隔1s来执行+1操作;
  • 在鼠标推出滑动区域的时候,count执行一次+1操作。

html文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>

</head>
<body>
  <div id="container"></div>
  <script src="throttle.js"></script>
  <script>
    let count = 0;
    function doSomething() {
      container.innerHTML = count++;
    }
    let container = document.querySelector("#container");
    container.onmousemove = thorttle(doSomething,1000);
  </script>
</body>
</html>

js文件

function throttle (func,wait) {
  let context,args,timeout; // 声明context用来后续指定this,args指定arguments
  return function(e) { // 同样是返回一个函数
    context = this; // 指定this。此时this是当前的容器,通过DOM操作获取的div。这两个的详细操作可以看我的上一篇文章
    args = arguments; // 指定e。此时e是当前的事件对象
    if(!timeout) { //开始我们声明了timeout,肯定是在null,取反为true;就会进入下面的函数
      timeout = setTimeout(()=>{
        timeout = null; // 每次都需要将timeout置空一下
        func.apply(context,args) // 通过apply绑定this和事件对象。
      },wait)
    }

  }
}

第七版、第三方js库underscore.js的引用---首次加1,退出也加1

这种功能的实现主要是根据underscore.js的第三个参数来实现的。第三个参数是一个对象。值分别是leading和trailing,两个都是布尔值,leading控制首次,trailing控制退出。两个的搭配主要有三种效果

  • leading: true,trailing: false ---控制首次加1,退出不加1;
  • leading: false, trailing: true ---控制首次不加1,退出加1;
  • Leading: true, trailing: true ---控制首次加1,退出也加1,也就是不穿参数的默认情况。这次我们采用这种配置看效果
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>
</head>
<body>
  <div id="container"></div>
  <!--导入第三方节流js库-->
  <script src="https://cdn.bootcss.com/underscore.js/1.9.1/underscore.js"></script>
  <script>
    let count = 1;
    let container = document.querySelector("#container")
    function doSomething(e) {
      console.log(this)
      console.log(e)
      container.innerHTML = count++;
    }
    container.onmousemove = _.throttle(doSomething,1000,{  //这里是调用throttle函数。调用方式和防抖函数类似,都是采用下滑线加.的方式调用的
      leading: true,
      trailing: true
    })
  </script>
</body>
</html>

第八版、手写节流函数基本功能的实现(三)---首次加1,退出也加1

本次实现的功能主要是采用时间戳和定时器双剑合壁的方式来模拟下面这种情况的

  • 第一次鼠标进入滑动的时候count会执行+1的操作;
  • 在鼠标不断滑动的时候,count会每隔1s来执行+1操作;
  • 在鼠标推出滑动区域的时候,count执行一次+1操作。

html文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    #container {
      width: 100%;
      height: 200px;
      line-height: 200px;
      text-align: center;
      color: #fff;
      background-color: #444;
      font-size: 30px;
    }
  </style>

</head>
<body>
  <div id="container"></div>
  <script src="throttle.js"></script>
  <script>
    let count = 0;
    function doSomething() {
      container.innerHTML = count++;
    }
    let container = document.querySelector("#container");
    container.onmousemove = thorttle(doSomething,1000);
  </script>
</body>
</html>

js文件

function throttle (func,wait) {
  let context,args,timeout; // 声明context用来后续指定this,args指定arguments
  let oldTimestamp = 0; // 之前的时间戳,默认是0
  return function(e) { // 同样是返回一个函数
    context = this; // 指定this。此时this是当前的容器,通过DOM操作获取的div。这两个的详细操作可以看我的上一篇文章
    args = arguments; // 指定e。此时e是当前的事件对象
    // 获取当前操作的时间戳
    var nowTimestamp = new Date().valueOf() // 表示当前开始滑动的时间戳,.valueOf()是用来获取日期的时间戳的
    if(nowTimestamp -oldTimestamp>wait) { // 判断一下nowTimestamp和oldTimestamp之间差值是不是大于1000ms,大于1000ms,就会执行下面count+1
      if(timeout) {  // 这里是通过timeout的方式在时间戳里面进行关联退出之后+1的操作
        clearTimeout(timeout)
        timeout = null;
      }
      //立即执行。这里的立即执行指的是开始第一次进入的时候执行一次count+1的操作
      func.apply(context,args) // 通过apply绑定this和事件对象。
      oldTimestamp = nowTimestamp; // 将原来的nowTimestamp赋值给oldTimestamp。以为只有这样才会执行操作。你想一下,nowTimestamp在不断的随着时间的推移而增加,oldTimestamp还一直停留在0的时间。这肯定是不对的
    }

    if(!timeout) { //开始我们声明了timeout,肯定是在null,取反为true;就会进入下面的函数
      timeout = setTimeout(()=>{
        oldTimestamp = new Date().valueOf() // 这里也是一个关键点,当使用定时器执行下面的操作的时候,oldTimestamp也需要更新一下。这样当执行时间戳的时候就可以正常更新了
        timeout = null; // 每次都需要将timeout置空一下
        func.apply(context,args) // 通过apply绑定this和事件对象。
      },wait)
    }

  }
}