套路--先貼圖html
demo : http://www.vvui.net/editor/index.html前端
gitee : https://gitee.com/kevin-huang/Bui-Editor-publicjava
前提jquery
下面的內容忽略ES6。ES6多了一些特性,語法糖,硬核仍是內在的經典,本文只講經典。git
吐槽JavaScript 的非主流特性es6
JavaScript的非主流特色讓許多習慣了JAVA、c#等這些現代面向對象風格語言的碼農們甚是煩惱。這種怪異的特色,加上天馬行空的做用域特性,讓人有種愛不起的感受。不得不吐槽幾點:c#
1. function 是一個雙面人: 當普通調用時候它是一個函數,當new建立對象時候它是一個構造函數。api
2. JavaScript沒有顯式的class定義,更沒有像java、c#那樣的繼承設計。數組
3. JavaScript的做用域很奇怪,沒有塊級做用域的實現,不能像java、c#那樣定義一個class,你要時刻記得你的操做是否會存在變量污染的問題。app
論JavaScript的 OOP實現
吐槽了JavaScript非主流的特性,咱們回到OOP這個經典問題上:封裝、繼承、多態。下面就三個特性的問題論論Javascript如何經過拐彎抹角的方式實現OOP的。
問題一、沒有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這個一等公民來實現類的封裝。
問題二、沒有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_指向的父類。
繼承鏈查詢機制:我稱之爲「就近原則」!
最後:貼個稍稍複雜的繼承鏈圖解
問題三、沒有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組件思路。
一、定義按鈕組件的容器Div
<div id="toolbar"></div>
二、定義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>
三、使用組件
/** * 建立一個Button實例 * **/ var btn = new Button($("#toolbar"),{ onClick:function(){ alert("按鈕被觸發"); } }); /**調用實例api**/ btn.click();
最後:
貼一個我正在開發的流程設計器,你能夠關注的我 gitee https://gitee.com/kevin-huang