首頁  >  文章  >  web前端  >  JavaScript 鍊式結構序列化詳解

JavaScript 鍊式結構序列化詳解

黄舟
黄舟原創
2017-03-04 15:21:221084瀏覽

一、概述​​

在JavaScript中,鍊式模式程式碼,太多太多,如下:

if_else:

if(...){
    //TODO
}else if(...){
    //TODO
}else{
    //TODO
}

switch:

switch(name){
    case ...:{
        //TODO
        break;
    }
    case ...:{
        //TODO
        break;
    }
    default:{
        //TODO    
    }
}

問題:諸如上述這些鍊式程式碼,倘若,我們想將其扁平化鍊式處理呢?如下:

//fn1,f2,f3为处理函数
_if(fn1)._elseIf(fn2)._else(fn3);

下面我們就來一起嘗試實作下唄。

二、鍊式程式碼扁平化

假如,現在我們有以下鍊式程式碼:

if(name === 'Monkey'){
    console.log('yes, I am Monkey');
}else if(name === 'Dorie'){
    console.log('yes, I am Dorie');
}else{
    console.log('sorry, over for ending!');
}

好了,現在我們一步一步將其」扁平化」。

其實看看上面的程式碼,不難發現,if…else這種格式,其實就是資料結構中的單鍊錶,那麼,初步利用JavaScript實作單鍊錶,如下:

var thens = [];
thens.resolve = function(name){
    for(var i = 0, len = this.length; i < len;i++){
        if(this[i](name) !== &#39;next&#39;){
            break;
        }
    }
}
thens.push(f1, f2, f3);

其中f1,f2,f3為判斷函數,並且我們假設,如果諸如f1、f2、f3返回'next'時,就繼續往下查找,否則,停止往下查找。如下:

function f1(name){
    if(name === &#39;Monkey&#39;){
        console.log(&#39;yes, I am Monkey&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f2(name){
    if(name === &#39;Dorie&#39;){
        console.log(&#39;yes, I am Dorie&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f3(){
    console.log(&#39;sorry, over for ending!&#39;);
}

好了,這就是鍊錶的模式。

但是,我們的最終目的是想實現如下這樣的呢?

//fn1,f2,f3为处理函数
_if(fn1)._elseIf(fn2)._else(fn3);

你可能會說,將上述程式碼改成如下這樣,不就好了嗎? ! !

thens.push(f1).push(f2).push(f3).resolve();

But,JavaScript的push方法回傳的是陣列的新長度,而不是陣列物件哦。

So,那我們只能新寫一個add方法,效果和push一樣,但是傳回陣列物件。如下:

thens.add = function(f){
    if(typeof f === &#39;function&#39;){
        this.push(f);
        return this;        
    }        
}

測試程式碼如下:

var thens = [];
thens.add = function(f){
    if(typeof f === &#39;function&#39;){
        this.push(f);
        return this;        
    }        
}
thens.resolve = function(name){
    for(var i = 0, len = this.length; i < len;i++){
        if(this[i](name) !== 'next'){
            break;
        }
    }    
}
thens.add(f1).add(f2).add(f3).resolve();

但是,這樣有個缺點,我們是將add、resolve方法綁定在全域變數thens中的,總不能每次創建一個數組時,都複製粘貼一遍方法吧,所以重構代碼如下:

function Slink(){
    this.thens = [];
    this.thens.add = function(f){
        if(typeof f === &#39;function&#39;){
            this.push(f);
            return this;        
        }        
    }
    this.thens.resolve = function(name){
        for(var i = 0, len = this.length; i < len;i++){
            if(this[i](name) !== &#39;next&#39;){
                break;
            }
        }    
    }
}

#顯然,add,resolve這種公共方法,在每次實例化時,都創建一遍是不科學的,so,利用prototype在原有的基礎上繼續變形,如下:

function Slink(){
    this.thens = [];
}
Slink.prototype = {
    add: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f);
                return this;        
            }        
    },
    resolve: function(name){
            for(var i = 0, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }    
    }
}

#測試程式碼如下:

var thens = new Slink();
thens.add(f1).add(f2).add(f3);
thens.resolve();

不錯,但是這樣,我們每次都得手動new一個Slink,有點麻煩,所以,我們將new Slink這個過程,封裝到函數中,如同jQuery一樣,如下:

function $go(f){
    return new Slink(f);
}
function Slink(f){
    this.thens = [];
    this.thens.push(f);
}
Slink.prototype = {
    add: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f);
                return this;        
            }        
    },
    resolve: function(name){
            for(var i = 0, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }    
    }
}

測試程式碼如下:

#
$go(f1).add(f2).add(f3).resolve();

好了,大功告成,接下來就是語法糖滴問題咯,整理程式碼如下:

function _if(f){
    return new Slink(f);
}
function Slink(f){
    this.thens = [];
    this.thens.push(f);
}
Slink.prototype = {
    _elseIf: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f);
                return this;        
            }        
    },
    _else: function(f){
            return this._elseIf(f);
    },
    resolve: function(name){
            for(var i = 0, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }
            return this;            
    }
}

#測試程式碼如下:

_if(f1)._elseIf(f2)._else(f3).resolve();

當然,除開利用數組這種方式,也可以利用閉包,實現鍊式扁平化效果,如下:

var func = Function.prototype;
func._else = func._elseIf = function(fn){
    var _this = this;
    return function(){
        var res = _this.apply(this,arguments);
        if(res==="next"){  //值为Boolean
            return fn.apply(this,arguments);
        }
        return res;
    }
}

測試程式碼如下:

function f1(name){
    if(name === &#39;Monkey&#39;){
        console.log(&#39;yes, I am Monkey&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f2(name){
    if(name === &#39;Dorie&#39;){
        console.log(&#39;yes, I am Dorie&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f3(){
    console.log(&#39;sorry, over for ending!&#39;);
}
f1._elseIf(f2)._else(f3)('Dorie');

三、非同步程式碼鍊式扁平化

在上面我們討論的都是同步過程,倘若,鍊式呼叫函數中有非同步情況呢?

什麼意思?如下:

function f1(name){
    setTimeout(function(){
        if(name === &#39;Monkey&#39;){
            console.log(&#39;yes, I am Monkey&#39;);
        }else{
            return &#39;next&#39;;
        }
    }, 2000);
}
function f2(name){
    if(name === &#39;Dorie&#39;){
        console.log(&#39;yes, I am Dorie&#39;);
    }else{
        return &#39;next&#39;;
    }
}
function f3(){
    console.log(&#39;sorry, over for ending!&#39;);
}

我們將f1利用setTimeout變成了非同步,按照上述程式碼的邏輯,應該是等f1完全執行完畢(包括setTimeout執行)後,判斷是否執行f2,但真的如此嗎?

測試程式碼如下:

_if(f1)._elseIf(f2)._else(f3).resolve();

執行程式碼的結果就是,什麼也不輸出。

Why?

因為JavaScript是單執行緒嘛。詳情請見(here)

那該怎麼解決呢?

由於有非同步程式碼,且必須在非同步程式碼後處理後續的鏈,那麼我們就等待非同步程式碼執行完畢後,才執行後續的鏈嘛,如下:

function f1(name){
    setTimeout(function(){
        if(name === &#39;Monkey&#39;){
            console.log(&#39;yes, I am Monkey&#39;);
        }else{
            //处理后续链
            this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
        }
    }.bind(this), 2000);
}

好了,由於在函數中,我們使用了this,其代表Slink對象,且改變了resolve方法,固,需細微調整Slink建構函數和原型鏈,如下:

function Slink(f){
    this.thens = [];
    this.thens.push(f.bind(this));
}
Slink.prototype = {
    _elseIf: function(f){
            if(typeof f === &#39;function&#39;){
                this.thens.push(f.bind(this));
                return this;        
            }        
    },
    _else: function(f){
            return this._elseIf(f.bind(this));
    },
    resolve: function(name, flag){
            for(var i = flag, len = this.thens.length; i < len; i++){
                if(this.thens[i](name) !== &#39;next&#39;){
                    break;
                }
            }
            return this;            
    }
}

測試程式碼如下:

function f1(name){
    setTimeout(function(){
        if(name === &#39;Monkey&#39;){
            console.log(&#39;yes, I am Monkey&#39;);
        }else{
            //处理后续链
            this.resolve(name, 1);//1代表下一个需处理函数在数组中的位置
        }
    }.bind(this), 2000);
}
function f2(name){
    if(name === 'Dorie'){
        console.log('yes, I am Dorie');
    }else{
        return 'next';
    }
}
function f3(){
    console.log('sorry, over for ending!');
}
_if(f1)._elseIf(f2)._else(f3).resolve('',0);

哈哈,如果你了解Promise,是不是感覺這麼相似。

是的,宗旨都一樣,達到非同步程式碼扁平化目的,不過這裡的程式碼比Promise要簡約得多啦。關於Promise詳情請見(here)。

 以上就是JavaScript 鍊式結構序列化詳解的內容,更多相關內容請關注PHP中文網(www.php.cn)!


#

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn