經過 "WWW" 原則咱們來了解 JavaScript 插件這個東西javascript
第一個 W "What" -- 是什麼?什麼是插件,我就不照搬書本上的抽象概念了,我我的簡單理解就是,能方便實現某個功能的擴展工具.(下面我會經過簡單的例子來幫助讀者理解)css
第二個 W "Why" -- 爲何? 爲何要有插件這種東西,首先結合第一個 W 來理解就是,使用插件的目的是方便咱們實現某一個功能. 也就是說在編程過程當中咱們只須要找輪子,或者改輪子而不須要從新造輪子.節省開發時間,而且各司其職會更加專業(作得更好)。其次就是方便維護,由於每一個功能模塊能夠分得更清楚,所謂的鬆耦合。html
第三個 W "How" -- 如何作?咱們如何開發 JavaScript 插件?這是咱們這片文章要談論的重點.java
在討論如何作以前咱們不妨先經過反向思惟來看看插件的特色。咱們從如何使用 Javascript 插件開始。jquery
假設咱們如今要使用插件 js-plugin.jsgit
第一步:引入插件,注意依賴項,例若有些插件是基於 jquery 編寫的,先引入 jquerygithub
第二步:經過插件提供的 API 實現咱們所要的業務web
以經典的 jquery 使用方法爲例編程
<script src="//cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script> <script> $(function(){ $("#Id").html('hello world!'); }) </script>
順便說一句,能使用CDN的儘可能使用CDN,這將使你的資源加載得更快.並節省你主機的帶寬開銷 傳送門: BootCDNjson
上述兩點其實也就是說咱們的插件要作到,引入相關文件就能夠方便地進行使用。換句話說插件必須知足下面的特色:
首先,我以爲插件最重要的一點 -- 複用性。就是說你這個插件在這個項目中是能用的,搬到另外一個項目中它也是能用的(廢話),而且原則上依賴項越少越好
其次,我以爲這是插件的宗旨 -- 易用性。開發一個插件,若是使用繁瑣,倒不如從新造輪子,那就失去了插件的意義。
除此以外,固然還有高效性,考慮執行的效率還有內存的優化。
插件開發不得不提的是 Modlule 模式,Module -- 模塊,模塊化開發,是在編程中十分通用的模式。說白了就是把業務需求分模塊。每個模塊負責一個功能的實現。有點像其餘面向對象編程語言中的類。例如 JsonHelper 專門負責 json 解析,FilesUpload,專門用來作文件上傳的,等等這些。
插件就是用這樣一種模塊化思想來進行開發的,下面咱們經過代碼來簡單解釋下 Module 模式。
var HelloWorld = function(objId){ var _get_dom = function(Id){ return document.getElementById(Id); } var _aim_obj = _get_dom(objId); var _say_hello = function(str){ _aim_obj.innerHTML = str; } return{ sayHello:_say_hello } }
由上述代碼可見,咱們將某些功能,如 「sayHello」 給歸到 HelloWorld (模塊)中了。固然咱們能夠繼續在下面添加其餘功能,但都歸於模塊 HelloWorld 來管理。這就是 Module 的體現。
使用方法(注意這裏使用了 new )
var Hei = new HelloWorld('hello'); Hei.sayHello('Hello Word'); var Hei2 = new HelloWorld('hi'); Hei2.sayHello('hi');
更直觀點,咱們來看下完整的代碼
<!DOCTYPE html> <html> <head> <title>Module</title> </head> <body> <div Id="hello"></div> <div Id="hi"></div> <script type="text/javascript"> var HelloWorld = function(objId){ var _get_dom = function(Id){ return document.getElementById(Id); } var _aim_obj = _get_dom(objId); var _say_hello = function(str){ _aim_obj.innerHTML = str; } return{ sayHello:_say_hello } } var Hei = new HelloWorld('hello'); Hei.sayHello('Hello World'); var Hei = new HelloWorld('hi'); Hei.sayHello('hi'); </script> </body> </html>
運行結果以下
咱們這裏須要注意的是,每使用 new 建立出來的新對象都將開闢新的內存空間(新的一份copy),只要引用沒有釋放,那麼該對象的佔用的內存空間將不會被回收。那麼如何避免過多浪費內存呢?一句話「釋放引用」,只須要釋放對該對象的全部
引用,垃圾回收機制就會將該對象佔用的內存空間回收。
var Hei = new HelloWorld('hello'); Hei.sayHello('Hello World'); Hei = null;//解除引用
這樣還要「手動」內存管理,麻煩。如何讓該模塊在內存中只保留一份(copy)呢?請看下面一段代碼
var HelloWorld = (function(){ var _getDom = function(Id){ return document.getElementById(Id) } var _sayHello = function(Id,str){ _getDom(Id).innerHTML = str; } return { getDom:_getDom, sayHello:_sayHello } }())
使用方法
HelloWorld.sayHello('hello','hello text');
是的,正如你所見到的,不須要 new 了。使用時再也不須要建立新對象,也就是說咱們只保持了該對象在內存中的一份引用,也就是HelloWorld 對它的引用。當 HelloWorld 對其引用解除時其所佔用的內存將獲得釋放。上述代碼實質上是一個匿名閉包。若是對閉包不是很理解的朋友能夠看看我寫的上一篇文章《淺析 JavaScript 中的閉包(Closures)》
瞭解了上面的種種以後咱們要開始直切主題了。
首先咱們建立一個 js 文件 取名爲 first-js-plugin.js(啥名字都行),鍵入如下代碼
; var plugin =(function(){ function _firstFunc(str){ console.log(str); }; return{ firstFunc: _firstFunc, }; })();
再建立一個 HTML頁面 取名爲 pluginTest.html (啥名字都行)
完整代碼以下
<!DOCTYPE html> <html> <head> <title></title> <script type="text/javascript" src="./first-js-plugin.js"></script> <script type="text/javascript"> plugin.firstFunc("Hello ! I am firstFunc"); </script> </head> <body> </body> </html>
運行結果以下圖顯示
經過這個簡單的插件,咱們來分析一下里面的代碼.
在分析代碼以前咱們先來了解另外一個東西,自調用匿名函數(防止插件用戶定義函數與插件衝突)
(function(){ //code })();
可能有些童鞋會以爲有點陌生,那看下下面的代碼
var func = function(){ //code } func();
其實這兩段代碼是等價的,固然有點差異,第一個是匿名函數.做用都是定義一個函數並當即執行.
(function(){ //code })();
代碼分析:
(匿名函數) 小括號(分組表達式)包起來匿名函數的聲明,做用至關是將函數聲明轉爲表達式,這樣才能執行,僅此而已
若是採起如下寫法
function(){ //code }();
編譯器報錯,問題是函數聲明沒法執行,表達式才能執行
搞清楚這些以後咱們回頭給下面的代碼加上分析,以下
;//JavaScript 弱語法的特色,若是前面恰好有個函數沒有以";"結尾,那麼可能會有語法錯誤 /* plugin.api_funcs 給對象設置屬性,屬性值爲 自調用匿名函數 這裏涉及到js做用域鏈以及閉包的知識點 */ var plugin =(function(){ function _firstFunc(str){ alert(str); }; //返回API return{ firstFunc: _firstFunc }; })();
咱們將代碼抽取一下(只爲幫助理解,已經理解的朋友請忽略)
//01.定義變量 var plugin = 某對象; //02.建立對象並返回 (function(){//code;return ...})();//匿名執行函數 return 某對象 //而後看核心的返回 return{firstFunc: _firstFunc}; //說白了就是,經過某個key將一個函數存儲起來.使用時經過key訪問到這個函數 var plugin = {key:function(){//code}} //因此最終的體現以下 var plugin = {firstFunc: 「具體的函數引用」}
因此咱們最後才能經過,插件名.屬性
來使用插件,正如:
plugin.firstFunc("Hello ! I am firstFunc");
4、插件的幾種寫法
這裏我就不墨跡了,直接上代碼,關鍵處會給註釋
面向對象思想 類方式
//自定義類 function plugin(){} //提供默認參數 plugin.prototype.str = "default param"; //提供方法(若是不傳參,則使用默認參數) plugin.prototype.firstFunc = function(str = this.str){ alert(str); } //建立"對象" var p = new plugin(); //調用方法 p.firstFunc("Hello ! I am firstFunc");//Hello ! I am firstFunc p.firstFunc();//default param
閉包方式
閉包方式就是咱們剛剛一直在介紹
var plugin =(function(){ function _firstFunc(str){ alert(str); }; return{ firstFunc: _firstFunc, }; })();
第二種方式上的一些變化
(function(){ //定義一些默認參數 var _options={ default_word:"default hello" } //定義一些api var _plugin_api = { firstFunc:function(str = _options.default_word){ alert(str); return this;//返回當前方法 }, secondFunc:function(){ alert("secondFunc"); return this;//返回當前方法 } } //這裏肯定了插件的名稱 this.CJPlugin = _plugin_api; })(); CJPlugin.firstFunc("hello");//hello CJPlugin.firstFunc();//default hello CJPlugin.secondFunc();//secondFunc
JavaScript 插件的相關知識今天暫時聊到這了.下篇文章筆者將經過實例來介紹如何開發一款屬於本身的省市區三級聯動插件.若是有朋友正在學習插件開發.那麼下篇文章可能咱們有更多能夠探討的話題。
限於筆者技術,文章觀點不免有不當之處,但願發現問題的朋友幫忙指正,筆者將會及時更新。也請轉載的朋友註明文章出處並附上原文連接,以便讀者能及時獲取到文章更新後的內容,以避免誤導讀者。筆者力求避免寫些晦澀難懂的文章(雖然也有人說這樣顯得高逼格,專業),儘可能使用簡單的用詞和例子來幫助理解。若是表達上有好的建議的話也但願朋友們在評論處指出。
本文爲做者原創,轉載請註明出處! Cboyce