javascript --- Function模式

回調函數

在javascript中,當一個函數A做爲另一個函數B的其中一個參數時,則稱A函數爲回調函數,即A能夠在函數B的運行週期內執行(開始,中間,結束)。javascript

舉例來講,有一個函數用於生成node.php

var complexComutation = function(){
    // 內部處理,並返回一個node
}

又有一個findNodes函數聲明用於查找全部節點,而後經過callback回調進行執行代碼。html

var findNodes = function(callback){
    var nodes = [];
    var node = complexComputation();
    // 若是回調函數可用,則執行她
    if(typeof callback === 'function'){
        callback(node);
    }  
    nodes.push(node);
    return nodes;
}

關於callback的定義,咱們能夠事先定義好來用:java

// 定義callback
var hide = function(node){
    node.style.display = 'node';
}
var hiddenNodes = findNodes(hide);

也能夠在調用的時候使用匿名定義,以下:node

// 使用匿名定義callback
var blockNodes = findNodes(function(node){
     node.style.display = 'block';
})

咱們平時用的最多的,估計就數jQuery的ajax方法的調用了,經過在done/faild上定義callback,以便在ajax調用成功或者失敗的時候作進一步處理,代碼以下(本代碼基於jquery1.8版):jquery

var menId = $('ul.nav').first().attr('id');
var request = $.ajax({
    url: 'script.php',
    type: 'post',
    data: {id: menuId},
    dataType: 'html'
})
// 調用成功時的回調處理
request.done(function(msg){
    $('#log').html(msg);
})
// 調用失敗時的回調處理
request.fail(function(jqXHR, textStatus){
    alert('Request failed:' + textStatus);
});

 

配置對象

若是一個函數(或方法)的參數只有一個,而且參數爲對象字面量,咱們則稱這種模式爲配置對象模式。以下所述:ajax

var her = {
    username: 'gaohan',
    first: 'gao',
    last: 'han'
};
addPerson(her);

則在addPerson函數內部,就能夠隨意使用her對象內的值了,通常用於初始化工做,例如jquery裏的ajaxSetup也就是這種方式來實現的:編程

// 事先設置好初始值
$.ajaxSetup({
    url: '/xmlhttp/',
    global: false,
    type: 'POST'
})
// 而後再調用
$.ajax({ data:myData });

另外,不少jquery的插件也有這種形式的傳參,只不過也能夠不傳,不傳的時候則就使用默認值了。瀏覽器

返回函數

返回函數,則是指一個函數的返回值爲另外一個函數,或者根據特定的條件靈活建立的新函數,示例以下:閉包

var setup = function(){
    console.log(1);
    return function(){
        console.log(2);
    };
};
// 調用setup函數
var her = setup(); // 1
her(); // 2
// 或者直接調用也能夠;
setup()(); // 2

或者咱們能夠利用閉包的特性,在函數中加入一個計數器,用來計算函數被調用的次數:

var setup = function(){
    var count = 0;
    return function(){
        retunr ++count;
    };
};
var next = serup();
next(); // 1
next(); // 2
next(); // 3

 

Currying

Currying是函數式編程的一個特性,將多個參數的處理轉化成單個參數的處理,相似鏈式調用。

下面來舉一個簡單的例子:

function add(x, y){
    var oldx = x, oldy = y;
    if(typeof oldy === 'undefined'){
        return function (newy){
             return oldx + newy;
        }
    } 
    return x + y ;
}

這樣的話,調用方式就有不少種了,好比:

// 測試
typeof add(5); // 'function'
add(3)(4); // 7
// 也能夠這樣調用
var add2000 = add(2000);
add2000(10); // 2010

接下來咱們定義一個,比較經常使用的currying函數:

// 第一個參數爲要應用的function, 第二個參數是須要傳入的最少參數個數
function curry(func, minArgs) {
    if(minArgs == undefined) {
         minArgs = 1;
    }
    function funcWithArgsFrozen(frozenargs){
         return function () {
              // 優化處理,若是調用時沒有參數,返回該函數自己
              var ages = Array.prototype.slice.call(arguments);
              var newArgs = frozenargs.concat(args);
              if(newArgs.length >= minArgs) {
                   return func.apply(this, newArgs);
              }else{
                   return funcWithArgsFrozen(newArgs);
              }
         }
    }
    return funcWithArgsFrozen([]);
}

這樣,咱們就能夠隨意定義咱們的業務行爲了,好比定義加法:

var plus = curry(function(){
    var result = 0;
    for(var i=0; i<arguments.length; ++i){
        result += arguments[i];
    }
    return result;
}, 2)

使用方法多種多樣。

plus(3, 2) // 正常調用
plus(3) // 偏應用
plus(3) (2) // 完整應用(返回5)
plus() (3) () () (2) // 返回5
plus(3, 2, 4, 5) // 能夠接受多個參數
plus(3) (2, 3, 5) // 同理

JavaScript裏的Function有不少特殊的功效,能夠利用閉包以及arguments參數特性實現不少不一樣的技巧,下一篇咱們將繼續介紹利用Function進行初始化的技巧。

當即執行的函數

// 聲明完函數之後,當即執行該函數
(function (){
     console.log('watch out');
}());
// 這種方式聲明的函數,也能夠當即執行
!function(){
    console.log('watch out!');
}();
// 以下的方式也均可以哦
~function(){ /* do someing*/ }();
-function(){ /* do someing*/ }();
+function(){ /* do someing*/ }();

 

 當即執行的對象初始化

該模式的意思是指在聲明一個對象(並不是函數)的時候、當即執行對象裏的某一個方法進行初始化工做,一般該模式能夠用在執行一次性的代碼上:

({
    // 這裏你能夠定義常量,並設置其它值
    maxwidth:  600,
    maxheight: 400,
    // 固然也能夠定義方法
    gimmMax: function(){
        return this.maxwidth + 'x' + this.maxheight;
    },
    init: function(){
        console.log(this.gimmeMax());
        // 更多代碼......
    }
}).init(); // 這樣就能初始化嘍

 

分支初始化

分支初始化是指在初始化的時候,根據不一樣的條件(場景)初始化不一樣的代碼,也就是所謂的條件語句賦值。以前咱們在處理的時候,經常使用相似下面的代碼:

var utils = {
    addListener: function(el, type, fn){
        if(typeof window.addEventListener === 'function'){
             el.addEventListener(type, fn, false);
        }else if(typeof document.attachEvent !== 'undefined'){
             el.attachEvent('on' + type, fn); 
        }else{
             el['on' + type] = fn;
        }
    },
    removeListener: function(el, type, fn){
        // 神馬之類的......
    }
}

咱們來改進一下,首先咱們要定義兩個接口,一個用來add事件句柄,一個用來remove事件句柄,代碼以下:

var utils = {
    addListener: null,
    removeListener: null
};

實現代碼以下:

if (typeof window.addEventListener === 'function') {
    utils.addListener = function (el, type, fn) {
        el.addEventListener(type, fn, false);
    };
} else if (typeof document.attachEvent !== 'undefined') { // IE
    utils.addListener = function (el, type, fn) {
        el.attachEvent('on' + type, fn);
    };
    utils.removeListener = function (el, type, fn) {
        el.detachEvent('on' + type, fn);
    };
} else { // 其它舊瀏覽器
    utils.addListener = function (el, type, fn) {
        el['on' + type] = fn;
    };
    utils.removeListener = function (el, type, fn) {
        el['on' + type] = null;
    };
}

用起來,是否是就很方便了?代碼也優雅多了。

自聲明函數

通常是在函數內部,重寫重名函數代碼,好比:

var her = function(){
    alert('Boo!');
    her = function(){
        alert('Double Boo!!');
    }
}

這種代碼,很是容易令人迷惑,咱們先來看看例子的執行結果:

// 1.添加新屬性
scareMe.prototype = 'Anna';
// 2. scareMe賦予一個新值
var prank = scareMe;
// 3. 做爲一個方法調用
var spooky = {
    boo: scareMe
}
// 使用新變量名稱進行調用
prank(); // 'Boo!'
prank(); // 'Boo!'
console.log(spooky.boo.property); // 'properly'

經過執行結果,能夠發現,將定於的函數賦值與新變量(或內部方法),代碼並不執行重載的scareMe代碼,而以下例子則正好相反:

// 使用自聲明函數
scareMe(); // Double boo!
scareMe(); // Double boo!
console.log(scareMe.property); // undefined

你們使用這種模式時,必定要很是當心才行,不然實際結果極可能和你指望的結果不同,固然你也能夠利用這個特殊作一些特殊的操做。

內存優化

該模式主要是利用函數的屬性特性來避免大量的重複計算。一般代碼形式以下:

var myFunc = function (param) {
    if (!myFunc.cache[param]) {
        var result = {};
        // ... 複雜操做 ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
};

// cache 存儲
myFunc.cache = {};

 

可是上述代碼有個問題,若是傳入的參數是toString或者其它相似Object擁有的一些公用方法的話,就會出現問題,這時候就須要使用傳說中的hasOwnProperty方法了,代碼以下:

var myFunc = function (param) {
    if (!myFunc.cache.hasOwnProperty(param)) {
        var result = {};
        // ... 複雜操做 ...
        myFunc.cache[param] = result;
    }
    return myFunc.cache[param];
};

// cache 存儲
myFunc.cache = {};

或者若是你傳入的參數是多個的話,能夠將這些參數經過JSON的stringify方法生產一個cachekey值進行存儲,代碼以下:

var myFunc = function () {
    var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)),
        result;
    if (!myFunc.cache[cachekey]) {
        result = {};
        // ... 複雜操做 ...
        myFunc.cache[cachekey] = result;
    }
    return myFunc.cache[cachekey];
};

// cache 存儲
myFunc.cache = {};

或者多個參數的話,也能夠利用arguments.callee特性:

var myFunc = function (param) {
    var f = arguments.callee,
        result;
    if (!f.cache[param]) {
        result = {};
        // ... 複雜操做 ...
        f.cache[param] = result;
    }
    return f.cache[param];
};

// cache 存儲
myFunc.cache = {};
總結
相關文章
相關標籤/搜索