zoukankan      html  css  js  c++  java
  • js函数相关高级用法

    一、惰性载入函数(lazy function)

    使用场景:当一个函数中的判断分支只用执行一次(第一次调用时执行),后续不会再变化,则可以使用惰性函数来提高性能。

    var addEvent = function(elem, type, handler) {
        if (window.addEventListener) {
             elem.addEventListener(type, handler, false);
        }
        else if (window.attachEvent) {
            elem.attachEvent('on' + type, handler);
        }
    };

    上面的函数是一个事件监听函数,每次调用时都会判断使用标准的事件监听函数还是IE事件监听函数,其实只用判断一次就可以知道该使用哪种监听函数,改为惰性函数的代码如下:

    var addEvent=function(elem, type, handler){
        if (window.addEventListener) {
            addEvent=function(elem, type, handler){
                elem.addEventListener(type, handler, false);
            }
        }
        else if (window.attachEvent) {
            addEvent=function(elem, type, handler){
                elem.attachEvent(type, handler, false);
            }
        }
        //当addEvent函数第一次被外部调用时,会被覆盖为另外的函数,但覆盖后的函数体的代码不会执行,因此需要执行一次
        addEvent(elem,type,handler);
    }

    另一种方法是声明后立即给出合适的函数

    var addEvent2=(function(elem, type, handler){
        if (window.addEventListener) {
            return function(elem, type, handler){
                elem.addEventListener(type, handler, false);
            }
        }
        else if (window.attachEvent) {
            return function(elem, type, handler){
                elem.attachEvent(type, handler, false);
            }
        }
    })(document.body,'click',function(){
        console.log('单击了');
    })
    console.log(addEvent2)

    二、一次性函数(once function)

     即只会执行一次的函数,核心思想是执行某个函数一次后将函数覆盖为null。

           //一次执行函数的工厂
           function once(fn,context){
               var result;
               return ()=>{
                   if(typeof(fn)==='function'){
                       result=fn.apply(context||this,arguments);
                       fn=null;
                   }
                   return result;
               }
           }
           var canOnlyFireOnce=once(()=>{
               console.log('测量屏幕宽度');
               return {deviceWidth:'375px',deviceHeight:'480px'}
           })
           var deviceInfo=canOnlyFireOnce(); //输出“测量屏幕宽度”
           var deviceInfo2=canOnlyFireOnce(); //不会输出任何东西
           console.log(deviceInfo2);

    三、记忆函数

    核心思想:让一个函数记住处理过的参数,并可以根据相应的参数把结果缓存起来,避免重复计算。

    实现方式:

    1.使用闭包

          function memoryFunction(fn){
                var cacheArgs,cacheReturn;
                return function(a){
                    if(a===cacheArgs){
                        return cacheReturn;
                    }
                    cacheArgs=a;
                    cacheReturn=fn.call(null,a);
                    return cacheReturn;
                }
          }
          var demo=memoryFunction((a)=>a*1000);
          var b=demo(1), c=demo(1);
          console.log(b,c)
    

    上面代码中,第二次调用demo函数时,直接返回值,不会再次计算

    2.函数也是一个对象,因此可以动态的给函数增加静态的属来记录缓存键和结果

           function isPrime(value) { 
                if (!isPrime.answers) { 
                    isPrime.answers = {}; 
                }
                //创建缓存 
                if (isPrime.answers[value] !== undefined) {  
                    return isPrime.answers[value]; 
                }
                //检查缓存键
                var prime = value !== 0 && value !== 1; // 1 is not a prime   
                for (var i = 2; i < value; i++) {  
                    if (value % i === 0) {    
                        prime = false;    
                        break;  
                    }
                } 
                //缓存计算的结果
                return isPrime.answers[value] = prime;
            }
    

      

    四、纯函数(pure function)

     所谓纯函数就是一个函数返回的结果只依赖于其参数,并且在执行过程中没有副作用,这样的函数就叫纯函数。

    条件1:返回结果直依赖参数

    const a = 1
    const foo = (b) => a + b
    foo(2) // => 3
    

     上面的代码中,foo函数的返回值依赖了外部的a变理,因此不是纯函数

    const a = 1
    const foo = (x, b) => x + b
    foo(1, 2) // => 3
    

     上面的代码中,foo函数的返回值只跟其参数有关且没有副作用,因此是纯函数

    条件2:执行过程没有副作用

    什么是副作用?所谓副作用就是指函数执行过程中对外产生了可观察的变化。看例子

    const a = 1
    const counter = { x: 1 }
    const foo = (obj, b) => {
      obj.x = 2
      return obj.x + b
    }
    foo(counter, 2) // => 4
    counter.x // => 2
    

    上面代码中foo 的结果只跟传入参数有关,但在内部加了一句 obj.x = 2,计算前 counter.x 是 1,但是计算以后 counter.x 是 2。foo 函数的执行对外部的 counter 产生了影响,修改了外部传进来的对象,也就是产生了副作用,因此不是纯函数。

    const a = 1
    const counter = { x: 1 }
    const foo = (obj, b) => {
      return obj.x + b
    }
    foo(counter, 2) // => 3
    counter.x // => 1
    

      上面代码中foo的结果只跟传入的参数有关,且计算的过程里面并不会对传入的对象进行修改,计算前后的 counter 不会发生任何变化,计算前是 1,计算后也是 1,它现在是纯的。

      下面的代码也是纯函数,因为对象是在函数内定义的,对外部不可见。

    const foo = (b) => {
      const obj = { x: 1 }
      obj.x = 2
      return obj.x + b
    }
    

    五、高阶函数(high order functions)

     所谓高阶函数是这样一种函数:即可以把函数作为参数传递,或者返回的值是一个函数。

    比如Array.Map,Array.Sort,还有我们上面写的一次性执行函数演示代码都是高阶函数。

    六、组合函数(compose function)

     作用:将需要嵌套执行的函数平铺。嵌套执行指的是,一个函数的返回值将作为另一个函数的参数。

    function C(){
        console.log('C')
        return function(){
            console.log('C函数回调执行')
        }
    }
    function B(CM){
        console.log('B')
        return function(){
            console.log('B函数回调执行')
            CM()
        }
    }
    function A(BM){
        console.log('A')
        return function(){
            console.log('A函数回调执行')
            BM()
        }
    }
    A(B(C()))()

    改为compose方式后调用如下;

    compose(A,B,C)()

    compose函数的实现:

    function compose(...funcs) {
      //没有传入函数参数,就返回一个默认函数(直接返回参数)
      if (funcs.length === 0) {
        return arg => arg
      }
    
      if (funcs.length === 1) {
      // 单元素数组时调用reduce,会直接返回该元素,不会执行callback;所以这里手动执行
        return funcs[0]
      }
      // 依次拼凑执行函数
      return funcs.reduce((prev, current) => (...args) => prev(current(...args)))
    }

    举例分析:compose(f4,f3,f2,f1)()

    • reduce回调函数第一次执行时,返回值为 函数 (...args) => f4(f3(...args)),作为下一次执行的prev参数
    • 回调函数第二次执行时,返回值为 函数(...args) => f4(f3(f2(...args))),作为下一次执行的prev参数
    • 回调函数第三次执行时,返回值为 函数(...args) => f4(f3(f2(f1(...args))))

    七、函数柯里化(function currying)

    把一个接收多个参数的函数分解成逐层调用的函数,每一层接收一部分参数,余下的参数由下一层再进行分解。

    假如有如下函数:

           function add(a,b,c,d,e){
               return a+b+c+d+e;
           }
           console.log(add(1,2,3,4,5)); //15
    

     现在要求每一步调用最多只能传2个参数,改写为柯里化版本如下:

           function curryAdd(a,b){
               return (c,d)=>{
                   return (e)=>{
                       return a+b+c+d+e;
                   }
               }
           }
           console.log(curryAdd(1,2)(3,4)(5)); //15
    

       看似把简单问题进行了复杂化,那么柯里化函数有什么作用呢?最大的用处就是可以固定不可变参数和可变参数,消除重复参数

    function ajax(url,type,data){
     // ...
    }
    ajax('www.my.com','GET')
    ajax('www.my.com','POST')
    //采化柯里化固定重复参数 let newAjax=curryAjax('www.my.com')
    //只需要传入可变参数即可 newAjax('GET') newAjax('POST')

      

    八、防抖函数(debouncing)

     核心思想:对同一个函数进行连续调用时,只有最后次调用生效,

    实现方式:使用setTimeout方法,每次调用时,清除上一次的timer,并将本次的timer记录下来就可以保证只有最后一次调用会生效

     

    function debounce(method,time){
        var timer = null ;
        return function(){
            var context = this;
            //在函数执行的时候先清除timer定时器;
            if(timer)clearTimeout(timer);
            timer = setTimeout(function(){
                method.call(context);
            },time);
        }
    }
    

      

    九、节流函数(throttling)

    核心思想:对同一个函数进行连续调用时,只有当上一次函数执行后过了你规定的时间间隔,才能进行下一次该函数的调用

    实现方式:使用setTimeout方法,给定两个时间,后面的时间减去前面的时间,到达我们给定的时间就去触发一次这个事件

     

     function throttle(method,time){
                var timer = null;
                var startTime = new Date();
                return function(){
                    var context = this;
                    var endTime = new Date();
                    var resTime = endTime - startTime;
                    //判断大于等于我们给的时间采取执行函数;
                    if(resTime >= time){
                        method.call(context);
                        //执行完函数之后重置初始时间,等于最后一次触发的时间
                        startTime = endTime;
                    }
                }
    }
    

      

  • 相关阅读:
    SSIS Error:Package Validation Error. SSIS Error Code DTS_E_OLEDBERROR. .Error code: 0x80040E37. An OLE DB record is available. Hresult: 0x80040E37
    MDS
    oracle11g rac静默安装+racADG部署搭建
    oracle 19c 单机ADG部署
    对索引组织表以及簇表的一些理解
    对表的连接的总结
    全局临时表
    对分区表的一些总结
    对临时表空间的一些常用查询
    linux 常用命令总结
  • 原文地址:https://www.cnblogs.com/94pm/p/10093017.html
Copyright © 2011-2022 走看看