angular 防抖和节流踩坑

如果在angualr项目中实现防抖节流,可以使用封装好的模块,比如rxjs。

但今天说的是使用原始的防抖节流方法遇到的问题:

以节流为例,先贴一段常见的原始节流代码:

    function throttle(fn) {
      let canRun = true; // 通过闭包保存一个标记
      return function () {
        if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return
        canRun = false; // 立即设置为false
        setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
          fn.apply(this, arguments);
          // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉
          canRun = true;
        }, 500);
      };
    }
    function sayHi(e) {
      console.log(e.target.innerWidth, e.target.innerHeight);
    }
    window.addEventListener('resize', throttle(sayHi));


通常都是以闭包的形式实现节流代码,然后再看触发方式window.addEventListener,网上大多数介绍防抖和节流的例子,都是用window.addEventListener来监听,等待触发的。

然而!应用到angular项目中时,代码是这样的:


 
search(){
    // 节流处理,间隔时间处理一次
    this.throttle(func, 1000);
}


坑一:我们期望input输入时,进行节流处理,然而事与愿违,发现throttle()并没有触发,也许聪明的你已经发现,原始代码使用节流方法,是通过回调函数,而我们现在是直接调用,this.throttle(func, wait)的结果只是一个匿名函数,并没有执行!所以应该这样调用:


 
search(){
    // this.throttle(func, wait)返回匿名函数,要进行调用
    this.throttle(func, 1000)();
}


坑二:现在函数可以执行了,我们期望无论input怎么触发,每1000ms执行一次,但很快我们发现并没有节流成功!传入的func函数,还是每次触发都会执行!这是为什么?其实中了闭包的坑!先看个闭包的例子:

function outerFn(){
  var i = 0; 
  function innerFn(){
      i++;
      console.log(i);
  }
  return innerFn;
}
var inner = outerFn();  //每次外部函数执行的时候,都会开辟一块内存空间,外部函数的地址不同,都会重新创建一个新的地址
inner();
inner();
inner();
var inner2 = outerFn();
inner2();
inner2();
inner2();   //1 2 3 1 2 3


闭包相关例子可以看着篇:https://blog.csdn.net/weixin_43586120/article/details/89456183

上面的例子可以看出,每次执行outerFn()时,都会开辟一块空间,这就导致变量i的值不能保持连续!结合本例,看一下上面的节流为什么不生效?就是每次input触发,都会执行this.throttle(func, 1000)();而this.throttle(func, 1000)每次执行都新开辟一块空间,这样就导致内部变量canRun 这个标志位每次都会初始化,根本起不到标志位的作用!那么怎么解决呢?重点是this.throttle(func, 1000)只执行一次,把它返回的匿名函数拿到,以后触发执行这个匿名函数就行了!

 

constructor() {
    //构造函数只执行一次,但弊端还是会引入了一个全局变量,不过这不是本文讨论的重点
    this.fun = this.throttle(this.func, 1000);
}
search(){
    // 每次执行返回匿名函数
    this.fun();
}


这样每次执行this.fun()时,每次取标志位变量,就是从同一个地址去获取,当然就生效了!其实这个问题是闭包引起的,如果不使用闭包,不考虑全局变量污染,那么实现节流就不会遇到这个问题。

最后,回到最初的原始代码示例,window.addEventListener('resize', throttle(sayHi))这样监听回调为什么没有这个问题?因为throttle(sayHi)只执行了一次,就是在执行window.addEventListener('resize', throttle(sayHi))语句时执行了一次,把返回的函数作为回调函数,每次resize触发,就会执行已经返回的回调函数!

你可能感兴趣的:(angular,angular.js,javascript,前端)