什么是高阶函数

至少满足以下一个条件的函数

  1. 函数作为参数进行传递
  2. 函数作为返回值输出

函数作为参数传递例子:

1、回调函数

作用:可以抽离出一部分容易变化的业务逻辑,把这部分业务逻辑放在函数参数中,这样一来可以分离业务代码中变化与不变的部分

1、ajax异步请求应用

例子:

 var getUserInfo = function (userId, callback) {
            let url = "https://geoapi.qweather.com/v2/city/lookup?key=eb04b1545b06437486b293fd1f23edd8&location=" + userId
            $.ajax({
                url: url, complete: function (data) {
                    if (callback instanceof Function) {
                        callback();
                        console.log("成功获取数据" + data)
                    }
                }
            })
        }
        getUserInfo("beijing", function () {
            console.log("请查收北京天气数据")
        })

2、Array.prototype.sort

      let arrs = [1, 5, 7, 2, 8];
        console.log(arrs)
        arrs.sort(function (a, b) {
            return a - b;
        })
        console.log(arrs)
        arrs.sort(function (a, b) {
            return b - a;
        })
        console.log(arrs)

函数作为返回值输出例子:

1、数据类型判断

    var Type = {};
        for (var i = 0, typeArrs; typeArrs = ["Array", "Object", "Number", "String"][i++];) {
            (function (type) {
                Type["is" + type] = function (obj) {
                    return Object.prototype.toString.call(obj) === '[object ' + type + ']'
                }
            })(typeArrs)
        }
        console.log(Type.isArray([12, 13]));
        console.log(Type.isNumber([12, 13]));

2、单例

  var getSingle = function (fn) {
            debugger
            var ret;
            return function () {
                ret || (ret = fn.apply(this, arguments))
                console.log(ret);
            }
        }
        var getScript = getSingle(function(){
            return document.createElement('scripe')
        })
        var script1 = getScript()
        var script2 = getScript()
        alert(script1===script2)

高阶函数实现AOP

AOP概念:
(面向切面编程)主要作用是把跟核心逻辑无关的业务剥离出来,跟逻辑无关的功能通常包含:日志统计、安全控制、异常处理等。
好处:1、保持业务逻辑模块的纯净和高内聚合,2、可以很方便的复用日志统计等模块
实现:

    Function.prototype.before = function(beforefn){
            var _self = this;//保存原函数的引用
            return function(){//返回包含了新函数和原函数的代理函数
                beforefn.apply(this,arguments);//执行新函数,修正this
                return _self.apply(this,arguments)//执行原函数
            }
        }
        Function.prototype.after = function(afterfn){
            var _self = this;
            return function(){
                var ret = _self.apply(this,arguments);
                afterfn.apply(this,arguments);
                return ret
            }
        }
        var func = function(){
            debugger
            console.log(2)
        }
        // before函数返回一个函数,取决于调用它的函数
        func =  func.before(function(){
            console.log(1); 
        }).after(function(){
            console.log(3)
        })

        func();

高级函数的其他应用

1、柯里化

概念:根据传进来的参数决定什么时候执行,当参数满足规则在执行。
案例:计算每个月消费情况,每天开销不做计算,月底在计算整个月的消费总额。

  var currying = function(fn){
            var costs = [];
            return function(){
                if(arguments.length===0){
                    // 计算数组的合
                    fn.apply(this,costs);
                }else{
                    [].push.apply(costs,arguments);
                    return arguments.callee
                    // console.log(costs);
                }

            }
        }
        var  cost = (function(){
            var money = 0
            return function(){
                let args = arguments
                for(let i = 0;i<args.length;i++){
                   money += args[i]
                }
                return money;
            }
        })();

        var costfunc = currying(cost); //转化成柯里化函数
        costfunc(100);//不计算求值
        costfunc(500);
        costfunc();//计算求值

2、uncurrying

概念:当我们调用一个对象的某个方法时,其实不关心这个对象是否被设计拥有该方法,可以通过call或者apply的方式去借用不属于它的方法
uncurrying方法,泛化this的提取过程

 Function.prototype.uncurrying=function(){
            var self = this;
            return function(){
                let obj = Array.prototype.shift.call(arguments);
                return self.apply(obj,arguments)
            }
        }
        

使用uncurrying例子1


      var push = Array.prototype.push.uncurrying();
        (function(){
            push(arguments,4);
            console.log(arguments);
        })(1,2,3);

我们还可以一次性把Array.prototype方法搬到Array对象上,同样这些方法操作的不仅仅是 array

        for (let i = 0,fn,arrs=["push","shift","slice"]; fn=arrs[i++];) {
           Array[fn] = Array.prototype[fn].uncurrying();
        }
        var obj ={
            "name":"zhangsan",
            "age":18
        }
        Array.push(obj,2);
        console.log(obj);
        // 截取第一个元素
        Array.shift(obj);
        console.log(obj);

3、函数节流

在少数情况下,函数的触发不是由用户直接控制的,函数可能非常频繁调用,造成性能问题。
函数被频繁调用场景:

  1. window.onresize事件
  2. mousemove事件
  3. 上传进度

函数节流原理:函数被触发频率太高,我们可以用setTimeout忽略掉一些请求
例子:

   var throttle = function(fn,interval){
            var self = fn,
            timer,
            firstTime = true;
            return function(){
                var args = arguments,
                _me = this;
                if(firstTime){
                    self.apply(_me,args);
                   return firstTime = false;
                }
                if(timer){
                    return false;
                }
                timer = setTimeout(function(){
                    clearTimeout(timer);
                    timer = null;
                    self.apply(_me,args);
                },interval||500)
            }
        }
        window.onresize = throttle(function(){
            console.log(1)
        },500)

5、惰性加载

方案一:缺点每次调用这个函数都会执行if的分支,增大开销

        var addevent = function(elem,type,handler){
            debugger
            if(window.addEventListener){
                return elem.addEventListener(type,handler,false)
            }else if(window.attchEvent){
                return elem.attchEvent('on'+type,handler)
            }

        }

第二种方案,把嗅探浏览器工作提前到代码加载的时候,可以避免重复的执行过程
缺点:也许我们从头到尾都没使用过addeven函数,那么浏览器的嗅探就完全多余,而且会稍稍延长页面的ready的时间

        var addevent = (function(){
            if(window.addEventListener){
                return function(elem,type,handler){
                    elem.addEventListener(type,handler,false)
                }
            }else if(window.attchEvent){
                return function(elem,type,handler){
                    elem.attchEvent('on'+type,handler) 
                }    
            }
            })()

第三种方案:惰性载入函数方案,此时addevent依然被声明为一个普通函数,在函数里依然有分支,但在第一次进入分支后,函数内部重写这个函数重写后函数就是我们期望的addevent函数,在下一次进入addevent函数的时候,addevent不存在条件分支语句

          var addEvent = function(elem,type,handler){
                if(window.addEventListener){
                    addEvent = function(elem,type,handler){
                        elem.addEventListener(type,handler,false)
                    }
                    console.log("条件判断")
                }else if(window.attchEvent){
                    addEvent = function(elem,type,handler){
                    elem.attchEvent('on'+type,handler) 
                }  
                addEvent(elem,type,handler)

            }
        }

运行

   var div = document.getElementById("d1");
        // addevent(div,'click',function(){
        //     console.log(1);
        // })
        addEvent(div,'click',function(){
            console.log(1);
        })
        addEvent(div,'click',function(){
            console.log(2);
        });

一声蔷薇udVkP
25 声望3 粉丝

未来可期