js节流和防抖别看这一篇

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/shuixiou1/article/details/81147511

  前言:我们在做页面事件绑定的时候,经常要进行节流处理,比如鼠标异步点击,去执行一个异步请求时,需要让它在上一次没执行时不能再点击,又或者绑定滚动事件,这种持续触发进行dom判断的时候,就要按一定频率的执行。

一、 伪理论:

     概念: 节流和防抖我认为都可以称之为节流。区别:防抖就是不想持续操作的节流,节流就是想固定频率执行的。
 节流:函数节流是指一定时间内js方法只跑一次。前一次没结束,后一次不会跑。比如滚动条这种持续按频率触发。
    防抖:函数防抖是指一定时间内js方法只跑一次。前一次没结束,后一次触发会按后一次的新间隔时间计算。比如按钮持续点击的限速。

二、原始的节流操作:

<!-- 一、最原始进行节流操作的函数 -->
<script type="text/javascript">
	window.onload = function(){
		
		/**基本防抖案例:先清除,后setTimeout进行执行**/
		document.querySelector("#send").addEventListener("click",function(e){
			clearTimeout(window.mytime_01);
			window.mytime_01 = setTimeout(function(){ 
				console.log("发射...");
			}, 500);
		});
		
		/**基本节流案例:setTimeout执行完时,恢复标志位,下一次才能执行*/
		var mytime_02 = true;
		window.onscroll = function(e) {
			if(!mytime_02){return;} //首次进入能执行
			mytime_02 = false;
			setTimeout(function(){ 
				console.log("滚..."); 
				mytime_02 = true; //上次执行成功,下一次才可执行。
			}, 200);
		}
		
		/**测试:二、underscore.js进行节流操作的函数**/
		document.querySelector("#send2").addEventListener("click", _.debounce(function() {
			console.log("点击2");
		},1000));
		
	};
		
</script>
<body style="height:800px;">
	<div style="width:200px;height:50px;background:#333" id="send">点我</div>
	<div style="width:200px;height:50px;background:#ccc" id="send2">点我2</div>
</body>

 从上面我们可以进行简单的限流,但是存在一个问题,我们这个代码和事件函数严重耦合在一次,每个事件有节流需要,就需要在方法内固定的添加那几句代码,我可以惭愧的告诉你,我做的一个项目,里面节流基本全是这样手工一个一个加上去的。

  那么市面上有没有比较成熟的库中有这二个方法呢? underscore.js 就有现成的这二个方法。上面的测试二,就是调用此js的节流。

  三、underscore.js 的节流方法的源码阅读。

<!-- 二、underscore.js进行节流操作的函数 -->
<script type="text/javascript">

	  var _ = {};
	
	 _.now = function (){return new Date().getTime()};
	
	  /**防抖: func 执行的方法,wait 等待时间 ,immediate true表示立即执行(此处逻辑代码已经删除)**/
	  _.debounce = function(func, wait, immediate){
	    var timeout, args, context, timestamp, result; //这些变量和方法参数是所有执行事件的函数共享!!!
	    var later = function(){
	    	//下面三行代码是核心。
		    var last = _.now() - timestamp; //timestamp当事件触发就会被改变,last预期就不等于wait.
		    if (last < wait && last >= 0) { //timestamp被改变了,照成last不够wait,就继续setTimeout(...,最后次事件剩余的wait)
		        timeout = setTimeout(later, wait - last); //wait-last到期后 --> 等价于 timestamp + wait 的时间。 
		    }else{
		        timeout = null;
		        result = func.apply(context, args);
		        if (!timeout) context = args = null;
		    }
	    };
	    return function(){ /**触发事件返回闭包函数,可以操作func,wait,以及设置的几个全局变量和later方法**/
	      context = this; //事件调用的话,this就是dom对象
	      args = arguments; //事件调用的话,就是事件的e参数
	      timestamp = _.now(); //最后次事件触发的时间戳。
	      if (!timeout) //只有timeout没有初始化的时候,才会设置setTimeout。
	         timeout = setTimeout(later, wait); //启动延迟执行,later内部会递归调用。
	      return result;
	    };
	  };
	  	
	/**节流: func 执行的方法,wait 等待时间  **/
   _.throttle = function(func, wait, options){
	    var context, args, result;
	    var timeout = null;
	    var previous = 0;
	    if (!options)
	        options = {};
	    var later = function() {
	      previous = options.leading === false ? 0 : _.now();
	      timeout = null;
	      result = func.apply(context, args);
	      if (!timeout) context = args = null;
	    };
	    return function() {
	      var now = _.now();
	      if (!previous && options.leading === false) previous = now;
	      var remaining = wait - (now - previous);
	      context = this;
	      args = arguments;
	      if (remaining <= 0 || remaining > wait){
	        if (timeout) {
	          clearTimeout(timeout); 
	          timeout = null;
	        }
	        previous = now;
	        result = func.apply(context, args);
	        if (!timeout) 
	            context = args = null; 
	      } else if (!timeout && options.trailing !== false){
	        timeout = setTimeout(later, remaining);
	      }
	      return result;
	    };
  };
	
</script>

underscore实现防抖核心思路:是利用闭包函数返回作为执行函数,共享timeout参数进行标记判断,并且利用触发事件会共同操作时间戳timestamp 来重新用setTimeout方式递归调整新的启动时间。

展开阅读全文

没有更多推荐了,返回首页