【開源我寫的富文字】打造全網最勁富文字技術選型之經典OOP仍是魅力硬核。

huang發表於2019-05-30

套路--先貼圖

demo :  http://www.vvui.net/editor/index.html

gitee : https://gitee.com/kevin-huang/Bui-Editor-public

前提

下面的內容忽略ES6。ES6多了一些特性,語法糖,硬核還是內在的經典,本文只講經典。

 

吐槽JavaScript  的非主流特性

  JavaScript的非主流特點讓許多習慣了JAVA、c#等這些現代物件導向風格語言的碼農們甚是煩惱。這種怪異的特點,加上天馬行空的作用域特性,讓人有種愛不起的感覺。不得不吐槽幾點:

  1. function 是一個雙面人: 當普通呼叫時候它是一個函式,當new建立物件時候它是一個建構函式。

  2. JavaScript沒有顯式的class定義,更沒有像java、c#那樣的繼承設計。

  3. JavaScript的作用域很奇怪,沒有塊級作用域的實現,不能像java、c#那樣定義一個class,你要時刻記得你的操作是否會存在變數汙染的問題。

 

論JavaScript的 OOP實現

  吐槽了JavaScript非主流的特性,我們回到OOP這個經典問題上:封裝、繼承、多型。下面就三個特性的問題論論Javascript如何通過拐彎抹角的方式實現OOP的。

  

  問題1、沒有class,JavaScript是怎麼做封裝的?

    我們都知道java或者c#裡面,我們通常利用class來做封裝,將一組屬性、函式封裝在一類裡面,類的成員可以定義為私有、公有。

    而然,JavaScript壓根沒有class這個設計,那怎麼做封裝呢?怎麼定義類成員?對於這個問題,function這個一等公民的身份特性就派上用場了。

    1)function是JavaScript裡的一等公民,它的權利比較大,在它裡面定義的變數(var 宣告),僅限fuction內使用。嘿嘿,利用這個特性,funciton可以模擬class,滿足區域性作用域的要求。

    2)function不僅解決了區域性作用域的問題,它還可以在new的時候,搖身一變轉為建構函式。不要問我為什麼,它天生如此。

    3)function解決了class裡面的建構函式,區域性作用域問題,那成員變數怎麼定義解決呢?

    4)關於成員變數,一是可以定義在function的prototype屬性物件上,二可以定義在function執行時的this上下文物件上。

    5)關於私有,公有。嚴格來說,JavaScript的function封裝,沒法實現私有、公有。無論是定義在prototype的成員,或者this上下文上的成員,外部均可以訪問。

    6)通常定義在prototype上的成員稱為公有(即所有實列都共享這些成員),定義在this物件上的成員稱為私有,這個私有表示每一個例項都有一個副本,不是共享的。   

    總結:

      JavaScript利用functon這個一等公民來實現類的封裝。

 

  問題2、沒有extend,JavaScript是怎麼做繼承的?

    * 問題1中提到的“function”是JavaScript世界裡面的一等公民。權利很大。這不!Javascript的繼承還得靠這個一等公民。那這個一等公民有啥特性可以模擬java的extend繼承呢?

    * 大凡想拿JavaScript裝裝B的人,經常會拿“原型繼承鏈”這個高深詞嚇唬嚇唬小白。看上去這玩兒好像就是JavaScript繼承的機制。通俗地說:“繼承啊,還得看function這個一等公民”。

    * 繼承看“function”,準確來說是看function的prototype(原型)。這個prototype是function隱含的一個屬性,這個屬性是一個物件,你可以理解為 function.prototype = {} 。

    * function裡的prototype可以實現繼承?那要看看這個prototype裡面都有什麼了。prototype裡面有兩個很重要的屬性:constructor 、_proto_。其中_proto_是繼承的關鍵,它會指向父類的prototype。

    * 在貼出更復雜的關係圖前,先看看function、prototype、_proto_三者簡單的關係圖:

      關係解析:funciton裡面有一個prototype,prototype指向一個物件,該物件裡有兩個屬性:constructor,_prop_。constructor回指function,_prop_指向父類的prototype。

         

                * 閱讀至此,你應該明白了,繼承的關鍵是 function裡的prototype及prototype裡面的_prop_,_prop_是通向父親大人的路徑。

     * 懂得了關鍵還不行,要理解一下當你訪問物件的一個成員時候,JavaScript是怎麼找到這個成員給你的(暫時稱之為 繼承鏈查詢機制)。

      比如你訪問了 myIns.name(myIns是new funciton產生的例項),js會這樣查詢你要訪問的成員。

      (1)js會先找this裡面是否有name屬性,有則直接返回。

      (2)this裡面沒有name,則找myIns._proto_(這個_proto_實際上指向了function.prototype)裡面是否有該屬性,有則直接返回。

      (3)function.prototype裡面也沒有找到name屬性,則找 function.prototype._proto_指向的父類裡面是否存在這個name。

    總結:

      JavaScript的繼承是利用function的prototype(原型)、prototype._proto_(原型裡面的父類指引) 實現的。

      繼承還依賴JavaScript成員訪問的鏈式查詢機制:先查自己有沒有,沒有則查原型prototype裡的,prototype裡也沒有則進一步查_proto_指向的父類。 

      繼承鏈查詢機制:我稱之為“就近原則”!  

    最後:貼個稍稍複雜的繼承鏈圖解

                                  

 

  問題3、沒有override,JavaScript是怎麼做多型重寫?

  多型,嚴格來說,JavaScript沒有明確的多型重寫實現,但是可以藉助JavaScript動態語言特性,繼承鏈成員查詢機制,達到模擬多型重寫的實現。

  1)重寫實現:利用JavaScript的繼承鏈查詢機制(就近原則),在this、prototype上定義同名的成員,可以達到類似於java裡的重寫機制。

  2)過載實現:過載即同一個函式名,多種引數組合。對於這個問題可以利用function內部arguments,在函式內部通過對argumnets數量,型別的判斷達到過載目的。

  3)JavaScript具有靈活的動態成員功能,即你可以在執行時刪除,新增一個成員。利用這個特性,可以實現java類似於java的反射動態成員的功能。 

 

OOP與Jquery雙劍合璧讓經典永駐

   前面說了一大堆JavaScript如何實現OOP。脫離了Dom談JavaScript就猶如紙上談兵。將OOP結合Dom一起實現前端元件的開發才是實戰,這也是Bui、Bui-editor、Bui-flow具體的OOP元件思路。

   特別地,我認為利用JavaScript經典OOP(也可以用es6的oop)配上jquery操作dom,是開發前端UI元件的經典大殺器,清晰的封裝,物件導向的特性,大大提高程式碼的質量,元件的擴充套件度。

   下面以一個簡單的按鈕元件例子展示Bui、Bui-editor、Bui-flow的OOP元件思路。

   1、定義按鈕元件的容器Div

        <div id="toolbar"></div>   

   2、定義JavaScript的Button物件             

<script>
    /**定義button類的建構函式**/
    function Button(jq,opts){
        this.opts = opts;
        this.jqObj = jq;
        this.button = $("<button>按鈕</button>").appendTo(this.jqObj);
        this._bindEvents();
    }
    /**
     * 定義Button的成員
     * ***/
    Button.prototype = {
     constructor:Button,
/**定義一個點選事件**/ click:function(){ this.button.trigger("click"); }, /**繫結button的事件**/ _bindEvents:function(){ var _this = this; this.button.on({ click:function(){ if(_this.opts.onClick){ _this.opts.onClick.call(this); } alert("click"); } }); } }; </script>

 

  3、使用元件    

    /**
     * 建立一個Button例項
     * **/
    var btn = new Button($("#toolbar"),{
        onClick:function(){
            alert("按鈕被觸發");
        }
    });
    /**呼叫例項api**/
    btn.click();

 

 

最後:

貼一個我正在開發的流程設計器,你可以關注的我 gitee  https://gitee.com/kevin-huang

 

相關文章