[讀書筆記]高階函數

1. 什麼是高階函數ajax

  • 函數能夠做爲參數被傳遞;
  • 函數能夠做爲返回值輸出。

2. 高階函數的示例編程

2.1 函數做爲參數傳遞json

(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.sortapp

將排序規則做爲回調函數傳給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 函數做爲返回值輸出ide

(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         });
相關文章
相關標籤/搜索