標籤:turn -- images 工具 img 基礎 內容 可見 抽取
一、前言
通過 "WWW" 原則我們來瞭解 JavaScript 外掛程式這個東西
第一個 W "What" -- 是什麼?什麼是外掛程式,我就不照搬書本上的抽象概念了,我個人簡單理解就是,能方便實現某個功能的擴充工具.(下面我會通過簡單的例子來協助讀者理解)
第二個 W "Why" -- 為什麼? 為什麼要有外掛程式這種東西,首先結合第一個 W 來理解就是,使用外掛程式的目的是方便我們實現某一個功能. 也就是說在編程過程中我們只需要找輪子,或者改輪子而不需要重新造輪子.節省開發時間,並且各司其職會更加專業(做得更好)。其次就是方便維護,因為每個功能模組可以分得更清楚,所謂的松耦合。
第三個 W "How" -- 如何做?我們如何開發 JavaScript 外掛程式?這是我們這片文章要談論的重點.
二、準備知識
在討論如何做之前我們不妨先通過反向思維來看看外掛程式的特點。我們從如何使用 Javascript 外掛程式開始。
假設我們現在要使用外掛程式 js-plugin.js
第一步:引入外掛程式,注意依賴項,例如有些外掛程式是基於 jquery 編寫的,先引入 jquery
第二步:通過外掛程式提供的 API 實現我們所要的業務
以經典的 jquery 使用方法為例
<script src="//cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script><script> $(function(){ $("#Id").html(‘hello world!‘); })</script>
順便說一句,能使用CDN的盡量使用CDN,這將使你的資源載入得更快.並節省你主機的頻寬開銷 傳送門: BootCDN
上述兩點其實也就是說我們的外掛程式要做到,引入相關檔案就可以方便地進行使用。換句話說外掛程式必須滿足下面的特點:
首先,我覺得外掛程式最重要的一點 -- 複用性。就是說你這個外掛程式在這個項目中是能用的,搬到另一個項目中它也是能用的(廢話),並且原則上依賴項越少越好
其次,我覺得這是外掛程式的宗旨 -- 易用性。開發一個外掛程式,如果使用繁瑣,倒不如重新造輪子,那就失去了外掛程式的意義。
除此之外,當然還有高效性,考慮執行的效率還有記憶體的最佳化。
三、Module 模式
外掛程式開發不得不提的是 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");
四、外掛程式的幾種寫法
這裡我就不墨跡了,直接上代碼,關鍵處會給注釋
物件導向思想 類方式
//自訂類 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 firstFuncp.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");//helloCJPlugin.firstFunc();//default helloCJPlugin.secondFunc();//secondFunc
結語
JavaScript 外掛程式的相關知識今天暫時聊到這了.下篇文章筆者將通過執行個體來介紹如何開發一款屬於自己的省市區三級聯動外掛程式.如果有朋友正在學習外掛程式開發.那麼下篇文章可能我們有更多可以探討的話題。
限於筆者技術,文章觀點難免有不當之處,希望發現問題的朋友幫忙指正,筆者將會及時更新。也請轉載的朋友註明文章出處並附上原文連結,以便讀者能及時擷取到文章更新後的內容,以免誤導讀者。筆者力求避免寫些晦澀難懂的文章(雖然也有人說這樣顯得高逼格,專業),盡量使用簡單的用詞和例子來協助理解。如果表達上有好的建議的話也希望朋友們在評論處指出。
如何開發原生的 JavaScript 外掛程式(知識點+寫法)