JavaScript之鏈式結構序列化

来源:http://www.cnblogs.com/giggle/archive/2016/09/28/5878285.html
-Advertisement-
Play Games

在JavaScript代碼有很多單鏈表形式的代碼,如if_else,switch等,倘若我們想將其如同Promise一樣扁平化處理呢?下麵我們就一起來看看唄~ ...


一、概述

在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) !== 'next'){
            break;
        }
    }
}
thens.push(f1, f2, f3);

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

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

好了,這就是鏈表的模式。

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

//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 === 'function'){
        this.push(f);
        return this;        
    }        
}

測試代碼如下:

var thens = [];
thens.add = function(f){
    if(typeof f === 'function'){
        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();
View Code

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

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

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

function Slink(){
    this.thens = [];
}
Slink.prototype = {
    add: function(f){
            if(typeof f === 'function'){
                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) !== 'next'){
                    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 === 'function'){
                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) !== 'next'){
                    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 === 'function'){
                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) !== 'next'){
                    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 === 'Monkey'){
        console.log('yes, I am Monkey');
    }else{
        return 'next';
    }
}
function f2(name){
    if(name === 'Dorie'){
        console.log('yes, I am Dorie');
    }else{
        return 'next';
    }
}
function f3(){
    console.log('sorry, over for ending!');
}
f1._elseIf(f2)._else(f3)('Dorie');
View Code
三、非同步代碼鏈式扁平化

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

什麼意思?如下:

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

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

測試代碼如下:

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

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

Why?

因為JavaScript是單線程嘛。詳情見(here

那該怎麼解決呢?

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

function f1(name){
    setTimeout(function(){
        if(name === 'Monkey'){
            console.log('yes, I am Monkey');
        }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 === 'function'){
                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) !== 'next'){
                    break;
                }
            }
            return this;            
    }
}

測試代碼如下:

function f1(name){
    setTimeout(function(){
        if(name === 'Monkey'){
            console.log('yes, I am Monkey');
        }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);
View Code

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

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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 學習過Spring框架的人一定都會聽過Spring的IoC(控制反轉) 、DI(依賴註入)這兩個概念,對於初學Spring的人來說,總覺得IoC 、DI這兩個概念是模糊不清的,是很難理解的, IoC是什麼 Ioc—Inversion of Control,即“控制反轉”,不是什麼技術,而是一種設計思 ...
  • 透切理解面向對象三大基本特性是理解面向對象五大基本原則的基礎. 三大特性是:封裝,繼承,多態 所謂封裝: 也就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。封裝是面向對象的特征之一,是對象和類概念的主要特性。 簡單的說,一個類就是一個封裝了 ...
  • 多態:龍生九子,各有不同 同樣都是繼承了同一個父類,但是父類中的方法並不使用任何一個子類,那麼在這就需要子類重新編寫這個方法的主體 1、需要父類同意子類可以重新編寫自己的方法 virtual - 虛方法 2、子類只能重寫父類允許重寫的方法,只能重寫虛方法 override - 重寫覆蓋虛方法 所有的 ...
  • Atitit 延遲綁定架構法attilax總結 配置文件的延遲綁定1 Api屬性與方法的回調延遲綁定1 後期綁定和前期綁定2 延遲調用2 用 Java 語言延遲綁定2 什麼是推遲綁定 C++3 配置文件的延遲綁定 通過把配置文件延遲綁定在啟動bat文件上,可以及時靈活的切換配置文件。 Api屬性與方 ...
  • Atitit java集成內嵌瀏覽器與外嵌瀏覽器attilax總結 HTML5將顛覆原生App世界。這聽起來有點危言聳聽,但若認真分析HTML5的發展史,你會發現,這個世界的發展趨勢確實就是這樣。 熟知歷史才能預知未來,先讓我們來看看HTML5為什麼誕生、這8年是怎麼過來的。 HTML5的誕生 自W ...
  • 摘要: 微服務架構被提出很短的時間內,就被越來越多的開發人員推崇,簡單來說其主要的目的是有效的拆分應用,實現敏捷開發和部署 。本分享即嘗試介紹微服務架構的一些實施細節和要求,探詢微服務架構的由來,並最終提供我們團隊內部的一些實踐總結,希望對大家有幫助。 WHAT - 什麼是微服務 微服務簡介 這次參... ...
  • 摘要: 最近很多阿裡內部的同學和客戶私信來咨詢如何學習 Docker 技術。為此,我們列了一個路線圖供大家學習Docker和阿裡雲容器服務。這個列表包含了一些社區的優秀資料和我們的原創文章。我們會隨著Docker技術的發展持續更新本文,也會在雲棲社區繼續貢獻內容來幫助同學們快速入門或持續提高。 Do... ...
  • 背景 現在的web系統已經越來越多的應用緩存技術,而且緩存技術確實是能實足的增強系統性能的。我在項目中也開始接觸一些緩存的需求。 開始簡單的就用jvm(java托管記憶體)來做緩存,這樣對於單個應用伺服器來說很好。 為了系統的可用性,需要做災備,那麼就要多準備一套系統環境,這時就會有一些共用資源的問題 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...