5/12闭包+柯里化+防抖节流

185 阅读4分钟

闭包

常见格式:外部函数内部有1个变量和1个内部函数

function fn() {
    let num=10;
    return function(){
        num++;
        console.log(num);
    }  
}

闭包的意义:外部可以访问num,但是无法修改num

闭包案例:多个点赞按钮可以独立计数

因为重复调用上述的fn,会生成不同的num分别独立存在

screenshots.gif


<script>
    window.onload=function(){
        function setNum() {
            let num=0;
            return function(){
                num++;
                this.value=`点赞(${num})`;
            }
        }

        let btn=document.querySelectorAll(".btn");
        for(let i=0;i<btn.length;i++){
            btn[i].onclick=setNum();
        }

    }
</script>
</head>
<body>
    <input type="button" value="点赞(0)" class="btn">
    <input type="button" value="点赞(0)" class="btn">
    <input type="button" value="点赞(0)" class="btn">
    <input type="button" value="点赞(0)" class="btn">
</body>
</html>

柯里化

所谓的柯里化,我认为也是闭包的一个应用,可以实现将多个函数输入值转变为单一的函数输入

window.onload = function () {
        function checkReg(reg) {
          return function (str) {
            return reg.test(str);
          };
        }

        let myCheck=checkReg(/\d{6}/);
        console.log(myCheck("666666"));
        console.log(myCheck("12345"));

        let myCheck2=checkReg(/\w+/);
        console.log(myCheck2(""));
        console.log(myCheck2("abc"));
        console.log(myCheck2("abc-"));//true
      };

比如构建正则检验的函数,对于同一条检验规则,这样写似乎能省点力

同理,网上流传的一个面试题也能这样解决

window.onload = function () {
        function f(a) {
          return function (b) {
            return function (c) {
              return function (d) {
                return a+b+c+d;
              };
            };
          };
        }

        console.log(f(1));
        console.log(f(1)(2));
        console.log(f(1)(2)(3));
        console.log(f(1)(2)(3)(4));
      };

防抖和节流

对于这两个我的理解是

防抖:用户点击后,1s后执行操作,期间如果再次点击,计时重置,以最后一次点击为准,防止多次点击造成多次执行。

节流:无论用户点击速度多快,我1s只会执行1次

<script>
//防抖
        window.onload=function(){
            //f1是我们点击按钮需要执行的程序
            //我们期望f1的this指向是事件源btn
            const f1=function(){
                console.log(this);
                console.log("防抖函数!");
            }

            const debounce=function(fn,delay){
                //闭包的一个经典写法
                //共用一个公共变量,比如timer
                //然后返回一个函数
                let timer=null;

                return function(){
                    //设定定时器之前先清除,没啥好说的
                    clearTimeout(timer);

                    timer=setTimeout(() => {
                        fn.call(this);
                        console.log(this);
                    }, delay);
                }
            }


            //最终目的:给btn绑定事件函数,
            //要求函数在100毫秒之后执行f1
            //如果在执行前多次点击btn
            //则计时会被重置,以最后一次点击为准
            btn.onclick=debounce(f1,1000)


            function f2() {
                let scrollTopValue=document.documentElement.scrollTop || document.body.scrollTop;
                console.log(scrollTopValue);
                console.log(window.scrollY || window.pageYOffset); 
            }
            window.onscroll=debounce(f2,1000);
        }
    </script>
 <script>
 //节流
      window.onload = function () {
        //f1是我们点击按钮需要执行的程序
        //我们期望f1的this指向是事件源btn
        const f1 = function () {
          console.log(this);
          console.log("防抖函数!");
        };

        const throttle = function (fn, delay) {
          //闭包的一个经典写法
          //共用一个公共变量,比如timer
          //然后返回一个函数
          let timer = null;

          return function () {
            if (!timer) {
              timer = setTimeout(() => {
                //让fn的this指向事件源
                fn.call(this);
                console.log(this);
                timer = null;
              }, delay);
            }
          };
        };
        btn.onclick = throttle(f1, 1000);

        function f2() {
          let scrollTopValue =
            document.documentElement.scrollTop || document.body.scrollTop;
          console.log(scrollTopValue);
          console.log(window.scrollY || window.pageYOffset);
        }
        window.onscroll = throttle(f2, 1000);
      };
    </script>

上述的f1实现了点击事件的防抖和节流,而f2实现了窗口滚动

最后,关于闭包应用GPT给出了解释

闭包是JavaScript中非常重要的概念,它指的是可以访问自由变量的函数。由于JavaScript中的函数嵌套和作用域嵌套的特性,可以很方便地实现闭包,进而实现一些特定的功能,比如隐藏变量、模块化等。

以下是JS闭包的一些常见应用场景及代码演示:

1. 隐藏数据

闭包可以用来隐藏数据,只暴露出不需要隐藏的接口,可以有效地保护数据的安全性。例如:

function counter() {
  let count = 0;
  
  return {
    increment: function() {
      count++;
      console.log(count);
    },
    reset: function() {
      count = 0;
      console.log(count);
    }
  }
}
​
const counter1 = counter();
counter1.increment() // 输出1
counter1.increment() // 输出2
counter1.reset() // 输出0

在这个例子中,我们通过闭包实现了一个计数器函数,该函数中定义了一个局部变量count,并返回了一个包含incrementreset方法的对象。这两个方法访问了count变量,但是由于该变量只在函数内被定义,因此不会被外界访问到,从而保证了数据的安全性。

2. 延迟执行

通过闭包延迟执行函数,可以在特定的时间或者条件下再执行某些函数。例如:

function debounce(fn, delay) {
  let timer = null;
  
  return function() {
    const context = this;
    const args = arguments;
    
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  }
}
​
function doSomething() {
  console.log('Do something');
}
​
const debouncedFn = debounce(doSomething, 1000);
debouncedFn(); // 在1s后输出'Do something'

在这个例子中,我们通过闭包实现了一个防抖函数debounce,该函数返回一个新的函数,该函数在一定的时间后执行原来的函数。闭包中定义了一个局部变量timer,用来标记延迟的时间,并且在原函数执行前清除之前的计时器。

3. 缓存结果

闭包可以用来缓存一些计算结果,避免重复计算带来的性能开销。例如:

function memoize(fn) {
  const cache = {};
  
  return function() {
    const argString = JSON.stringify(arguments);
    
    if (cache[argString]) {
      return cache[argString];
    }
    
    const result = fn.apply(this, arguments);
    cache[argString] = result;
    
    return result;
  }
}
​
function fibonacci(n) {
  if (n <= 1) {
    return n;
  }
  
  return fibonacci(n - 1) + fibonacci(n - 2);
}
​
const memoizedFibonacci = memoize(fibonacci);
console.log(memoizedFibonacci(10)); // 输出55,将会缓存计算过的值
console.log(memoizedFibonacci(10)); // 输出55,直接返回缓存中的值

在这个例子中,我们通过闭包实现了一个缓存函数memoize,该函数接受一个函数作为参数,返回一个新的函数。新函数的作用是执行原函数并缓存结果,如果已经缓存了该参数的结果,就直接返回缓存中的值,否则就执行原函数,计算结果并缓存结果。由于缓存了结果,这样可以避免在重复调用时导致的重复计算带来的性能开销。