• [读书笔记]高阶函数


    1. 什么是高阶函数

    • 函数可以作为参数被传递;
    • 函数可以作为返回值输出。

    2. 高阶函数的示例

    2.1 函数作为参数传递

    (1) 回调函数(callback)

    回调函数类似于C#中的委托,在异步操作中应用较为广泛。如jQuery中的ajax:

     1 var getOrderInfo = function (callback) {
     2     $.ajax({
     3         type: 'POST',
     4         url: '/URL',
     5         data: "name=John&location=Boston",
     6         dataType: "json",
     7     }).then(function (data) {
     8         if (typeof callback === 'function') {
     9             // render 
    10             callback(data);
    11         }
    12     }, function () {
    13         console.error('error');
    14     });
    15 };

    (2) Array.prototype.sort

    将排序规则作为回调函数传给Array.prototype.sort方法来得到想要的排序结果:

     1 var s1 = [
     2     { name: '李雷', age: 12 },
     3     { name: '陈梅梅', age: 11 },
     4     { name: '露西', age: 11 },
     5     { name: '大卫', age: 13 }
     6 ],
     7 s2 = [].concat(s1),
     8 ageAsc = function (a, b) {
     9     return a.age - b.age;
    10 },
    11 ageDesc = function (a, b) {
    12     return b.age - a.age;
    13 };
    14 // 年龄从小到大
    15 s1.sort(ageAsc);
    16 console.dir(s1);
    17 // 年龄大到小
    18 s2.sort(ageDesc);
    19 console.info(s2);

    2.2 函数作为返回值输出

    (1) 判断数据类型

     1 var isType = function( type ){
     2     return function( obj ){
     3         return Object.prototype.toString.call( obj ) === '[object '+ type +']';
     4     }
     5 },
     6 isString = isType( 'String' ),
     7 isArray = isType( 'Array' ),
     8 isNumber = isType('Number');
     9 console.log(isString); // 输出isString
    10 /*
    11 function( obj ){
    12     return Object.prototype.toString.call( obj ) === '[object '+ type +']';
    13 }
    14 */

    (2) 获取单例

     1 var getSingle = function (fn) {
     2     var ret;
     3     return function () {
     4         return ret || (ret = fn.apply(this, arguments));
     5     };
     6 },
     7 getScript = getSingle(function () {
     8     return document.createElement('script');
     9 });
    10 
    11 var script1 = getScript(), // 第一次调用时将执行 (ret = fn.apply(this, arguments)
    12     script2 = getScript(); // 第二次调用时直接返回 ret
    13 console.log('script1 === script2 is ', script1 === script2); // 输出:true

    3. 高阶函数的应用

    3.1 高阶函数实现AOP

    提起AOP(面向切面编程),可能会想起Spring MVC 中的AOP。实现如下:

     1 Function.prototype.before = function (beforefn) {
     2     var that = this; // 保存原函数的引用
     3     console.log(that); // that指向print2()
     4     // 返回包含了原函数和新函数的"代理"函数
     5     // return#1
     6     return function () {
     7         beforefn.apply(this, arguments); // 执行新函数,修正this
     8         return that.apply(this, arguments); // 执行原函数
     9     };
    10 };
    11 
    12 Function.prototype.after = function (afterfn) {
    13     var that = this; // 保存原函数的引用
    14     console.log(that); // that 指向return#1
    15     // return#2
    16     return function () {
    17         var ret = that.apply(this, arguments); // 执行原函数,并保存原函数的执行结果
    18         afterfn.apply(this, arguments); // 执行新函数
    19         return ret; // 在新函数执行完成之后返回原函数的执行结果
    20     };
    21 };
    22 
    23 var print1 = function () {
    24     console.log(1);
    25 },
    26 print2 = function () {
    27     console.log(2);
    28 },
    29 print3 = function () {
    30     console.log(3);
    31 };
    32 
    33 print2 = print2.before(print1).after(print3);
    34 print2();

    3.2 柯里化(currying)

    currying 又称部分求值。一个currying 的函数首先会接受一些参数,接受了这些参数之后,
    该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保
    存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

     1 var currying = function (fn) {
     2     var args = [];
     3     return function () {
     4         if (arguments.length === 0) {
     5             console.log(this);
     6             return fn.apply(this, args);
     7         } else {
     8             console.log(arguments,args);
     9             [].push.apply(args, arguments);
    10             // arguments.callee, Returns the Function object being executed
    11             return arguments.callee; 
    12         }
    13     }
    14 },
    15 cost = (function () {
    16     var money = 0;
    17     return function () {
    18         for (var i = 0, l = arguments.length; i < l; i++) {
    19             money += arguments[i];
    20         }
    21         return money;
    22     }
    23 })();
    24 
    25 var cost = currying(cost); // 转化成currying 函数
    26 cost(100); // 未真正求值
    27 cost(200); // 未真正求值
    28 cost(300); // 未真正求值
    29 console.log(cost()); // 求值并输出:600

    3.3 反柯里化(uncurrying)

     1         Function.prototype.uncurrying = function () {
     2             var self = this;
     3             return function () {
     4                 return Function.prototype.call.apply(self, arguments);
     5             }
     6         };
     7 
     8         var push = Array.prototype.push.uncurrying();
     9 
    10         var obj = {
    11             name: 'wills',
    12             age: 26
    13         };
    14         push(obj, 'JavaScript programer');
    15 
    16         console.log(obj); // Object {0: "JavaScript programer", name: "wills", age: 27, length: 1}

    3.4 函数节流

    函数节流目的即降低函数被频繁调用的频率。代码实现如下:

     1 // 函数节流
     2 var throttle = function (fn, interval) {
     3     var __self = fn, // 保存需要被延迟执行的函数引用
     4     timer, // 定时器
     5     firstTime = true; // 是否是第一次调用
     6 
     7     return function () {
     8         var args = arguments,
     9             that = this;
    10 
    11         // 如果是第一次调用,不需延迟执行
    12         if (firstTime) {
    13             __self.apply(that, args);
    14             return firstTime = false;
    15         }
    16 
    17         if (timer) { // 如果定时器还在,说明前一次延迟执行还没有
    18             return false;
    19         }
    20 
    21         timer = setTimeout(function () { // 延迟一段时间执行
    22             clearTimeout(timer);
    23             timer = null;
    24             __self.apply(that, args);
    25         }, interval || 500);
    26     };
    27 };
    28 
    29 var resizeThrottle = throttle(function () {
    30     console.log(1);
    31 }, 500),
    32 resize = function () {
    33     console.log(2);
    34 };
    35 window.onresize = function () {
    36     resizeThrottle();
    37     resize(2);
    38 };

    3.5 分时函数

     分时函数与函数节流相似,使用场景一般为主动调用。

     1 var ary = [];
     2 for (var i = 1; i <= 1000; i++) {
     3     ary.push(i); // 假设ary 装载了1000 个好友的数据
     4 };
     5 var renderFriendList = function (data) {
     6     for (var i = 0, l = data.length; i < l; i++) {
     7         var div = document.createElement('div');
     8         div.innerHTML = i;
     9         document.body.appendChild(div);
    10     }
    11 };
    12 renderFriendList(ary);
    未使用分时函数优化前

    使用分时函数之后:

    var timeChunk = function (ary, fn, count) {
        /// <summary>
        /// 分时函数
        /// </summary>
        /// <param name="ary" type="Array">数据(对象)数组</param>
        /// <param name="fn" type="Function">函数</param>
        /// <param name="count" type="Number">每次执行的数组长度</param>
        var obj,
            t,
            len = ary.length,
            start = function () {
                for (var i = 0; i < Math.min(count || 1, ary.length) ; i++) {
                    var obj = ary.shift();
                    fn(obj);
                }
            };
    
        return function () {
            t = setInterval(function () {
                if (ary.length === 0) { // 如果全部节点都已经被创建好
                    return clearInterval(t);
                }
                start();
            }, 200); // 分批执行的时间间隔,也可以用参数的形式传入
        };
    };
    
    var ary = [];
    for (var i = 1; i <= 1000; i++) {
        ary.push(i);
    };
    var renderFriendList = timeChunk(ary, function (n) {
        var div = document.createElement('div');
        div.innerHTML = n;
        document.body.appendChild(div);
    }, 8);
    
    renderFriendList();

    3.6 惰性加载函数

     1         var addEvent = function (elem, type, handler) {
     2             console.log('addEvent');
     3             if (window.addEventListener) {
     4                 console.log('addEventListener');
     5                 addEvent = function (elem, type, handler) {
     6                     elem.addEventListener(type, handler, false);
     7                 }
     8             } else if (window.attachEvent) {
     9                 console.log('attachEvent');
    10                 addEvent = function (elem, type, handler) {
    11                     elem.attachEvent('on' + type, handler);
    12                 }
    13             }
    14             addEvent(elem, type, handler);
    15         };
    16 
    17         var div = document.getElementById('div1');
    18         addEvent(div, 'click', function () {
    19             alert(1);
    20         });
    21         addEvent(div, 'click', function () {
    22             alert(2);
    23         });
  • 相关阅读:
    BZOJ4346 : [POI2016]Nadajniki
    BZOJ4345 : [POI2016]Korale
    BZOJ4134 : ljw和lzr的hack比赛
    BZOJ4342 : CF348 Pilgrims
    BZOJ2310 : ParkII
    BZOJ3322 : [Scoi2013]摩托车交易
    BZOJ1444 : [Jsoi2009]有趣的游戏
    Xcode8中处理打印日志的配置
    iOS开发之记录用户登录状态
    热修复
  • 原文地址:https://www.cnblogs.com/January/p/5430764.html
Copyright © 2020-2023  润新知