如何開發原生的 JavaScript 插件(知識點+寫法)


一、前言

通過 "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 })();

代碼分析:

  1. 最后面的小括號 () 表示執行該函數
  2. (匿名函數) 小括號(分組表達式)包起來匿名函數的聲明,作用相當是將函數聲明轉為表達式,這樣才能執行,僅此而已

    如果采取以下寫法

    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");

四、插件的幾種寫法

這里我就不墨跡了,直接上代碼,關鍵處會給注釋

  1. 面向對象思想 類方式

    //自定義類    
    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
  2. 閉包方式

    閉包方式就是我們剛剛一直在介紹

    var plugin =(function(){
        function _firstFunc(str){
            alert(str);
        };
        return{
            firstFunc: _firstFunc,
        };
    })();

     

  3. 第二種方式上的一些變化

    (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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM