零基礎(chǔ)打造自己的 js 類庫(1)

寫作不易,轉(zhuǎn)載請注明出處,謝謝。

文章類別:Javascript基礎(chǔ)(面向初學(xué)者)

前言

在之前的章節(jié)中,我們已經(jīng)不依賴jQuery,單純地用JavaScript封裝了很多方法,這個時候,你一定會想,這些經(jīng)常使用的方法能不能單獨整理成一個js文件呢?

當(dāng)然可以,封裝本來就是干這個用的。放在一個單獨js文件里固然不錯,其實我們也可以單獨整一個js類庫,一方面可以鍛煉一下自己封裝方法的能力,另一方面,也可以將自己學(xué)到的東西做一個整理。

出于這個目的,本文將介紹如何封裝一個簡單的js類庫。

1. 總體設(shè)計

所謂的js庫,其實也就是一個js文件,我思前想后,決定取個名字叫“miniQuery”,是不是山寨的味道十足呢?哈,請不要在意這些小細節(jié)。

大概的設(shè)計如下:

  1. 擴展方法的兼容(主要寫一些兼容的擴展方法,比如 forEach 方法等)

  2. 工具包定義 (就是之前封裝的utils.js,我們的miniQuery需要依賴這個工具包,為了方便,就干脆寫在一個文件里面了。)

  3. miniQuery定義

2. 擴展方法的兼容

// ------------------------ 基本擴展, 字符串,數(shù)組等---------------------------------//
function extend_base (){
    
    if(!String.prototype.format ){
        String.prototype.format = function() {
            var e = arguments;
            return this.replace(/{(\d+)}/g,function(t, n) {
                return typeof e[n] != "undefined" ? e[n] : t
            })
        };
    }
    
    if (!Array.prototype.forEach && typeof Array.prototype.forEach !== "function") {
        Array.prototype.forEach = function(callback, context) {
           // 遍歷數(shù)組,在每一項上調(diào)用回調(diào)函數(shù),這里使用原生方法驗證數(shù)組。
           if (Object.prototype.toString.call(this) === "[object Array]") {
               var i,len;
               //遍歷該數(shù)組所有的元素
               for (i = 0, len = this.length; i < len; i++) {
                   if (typeof callback === "function"  && Object.prototype.hasOwnProperty.call(this, i)) {
                       if (callback.call(context, this[i], i, this) === false) {
                           break; // or return;
                       }
                   }
               }
           }
        };
    }
    
    if(!String.prototype.format ){
        Array.isArray = function(obj){
            return obj.constructor.toString().indexOf('Array') != -1;
        }
    }


    //待補充 ...
    
}

我們定義一個extend_base方法,里面主要對js內(nèi)置對象的api做了一些兼容性補充,目前還不完善,只有寥寥幾個方法。當(dāng)然,如果你不考慮IE678的話,那么基本上不需要這一部分了。

定義完成后立即調(diào)用。

extend_base(); 

2. 工具包整合

// ------------------------ 工具包---------------------------------//
var utils = {
            center : function(dom){
                dom.style.position = 'absolute';
                dom.style.top = '50%';
                dom.style.left = '50%';
                dom.style['margin-top'] = - dom.offsetHeight / 2 + 'px';
                dom.style['margin-left'] = - dom.offsetWidth / 2 + 'px';
            },
        
            /** dom相關(guān) * */
            isDom : ( typeof HTMLElement === 'object' ) ?
                function(obj){
                    return obj instanceof HTMLElement;
                } :
                function(obj){
                    return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
                } ,
             
            /** 數(shù)組相關(guān) * */
            isArray : function(obj){
                return obj.constructor.toString().indexOf('Array') != -1;
            }
            
        }
  • center :控制dom元素相對于父盒子居中
  • isDom :判斷是否為dom元素
  • isArray :判斷是否為數(shù)組

3. miniQuery總體設(shè)計

終于到miniQuery了,在寫代碼之前,先簡單說一下自執(zhí)行函數(shù)。

可能你在很多書上,或者下載的源碼里面,經(jīng)常會看到這樣的代碼:

(function(){
    
})();

這樣子你或許覺得很奇怪,沒事,我們一起來分析。

《JavaScript: 零基礎(chǔ)輕松學(xué)閉包(1)》 里面已經(jīng)說過,在js中,你如果把函數(shù)看作一個數(shù)據(jù)類型,和其他語言中的 Integer, Float , String等等一樣,就會理解很多事情了。當(dāng)然,其實在js中,函數(shù)本身就是一個對象,不然的話就不會出現(xiàn)call方法了。因為只有對象才可以調(diào)用方法嘛。不過,大部分情況下,你把函數(shù)理解為數(shù)據(jù)類型就可以了。

匿名函數(shù):

function(){
    
}

這是一個函數(shù),因為沒有函數(shù)名,所以是一個匿名函數(shù)。你定義了它,如果接下來你不想通過函數(shù)調(diào)用的方式來執(zhí)行它,那么是不是可以直接給它打一個括號來執(zhí)行呢?

像這樣:

function(){
    
}();

不過,因為js語法的關(guān)系,這樣子是不能執(zhí)行的,你需要用一對圓括號來包一下:

(  
    function(){
    alert("你好!");
    }()  
) ;
Paste_Image.png

這樣就可以了,下面是另一種寫法:

(  
    function(){
    alert("你好!");
    }  
)();

這樣也可以,這種寫法會更多一點。它的意思就是說,我不關(guān)心你這個函數(shù)叫什么名字,反正你在被定義的時候就要給我執(zhí)行,這就是所謂的自執(zhí)行函數(shù)。

好,問題來了,怎么加參數(shù)呢?

以前我們習(xí)慣于這么寫:

 function say(str){
    alert(str);
 } 

say("你好!");

依葫蘆畫瓢

(  
    function(str){
       alert(str);
    }  
)("你好!");

OK了。

是不是一樣的意思呢?

沒啥區(qū)別,以前怎么做,現(xiàn)在還怎么做,無非就是一個函數(shù)傳參的事情罷了。

我們將圓括號的位置調(diào)整一下

( function(str){
    alert(str);
} )("你好!");

這樣差不多就是最終的版本了,我記得初學(xué)js的時候,看這種代碼很吃力,好像在看外星語言一樣,后來看多了也就習(xí)慣了。

自執(zhí)行函數(shù)就是這么一回事,沒什么大不了的。

有了上面的解釋,以后如果你再遇到這種寫法,就 so easy 啦。

所以,不要再恐懼了,它就是這么回事,沒什么大不了的,我這么后知后覺的人都能寫,你也可以。我花了半年的時間才看明白,我相信你現(xiàn)在只需要幾分鐘。我的意思是,如果你之前不知道這些的話。

那么,什么時候用自執(zhí)行函數(shù)呢?

當(dāng)你覺得某個函數(shù)只需要執(zhí)行一次,而且不需要在其他地方調(diào)用的時候,就用。

你可能會問了,我干嘛要這樣寫啊,反正就執(zhí)行一次,我直接把實現(xiàn)代碼寫在外面不就行了?

原因很簡單,因為那樣的話,你定義的變量就會是全局的,而一般來說我們設(shè)計的原則是盡量不要使用全局變量。

而采用這種方式,我們就形成了一個匿名函數(shù),函數(shù)的定義又會形成閉包,所以比較安全和簡潔。

你可能還會覺得疑惑,我干嘛要這些寫,如果我非要給函數(shù)取一個名字,然后馬上調(diào)用呢?

額,其實我個人認(rèn)為這也是沒有問題的,但是你得費一番心思去給函數(shù)取名字,取 a,b,c,d 這樣的名字肯定是不好的。那么,我私以為,還不如干脆就用匿名函數(shù)算了,省得麻煩。

如果這部分知識你以前就不知道,那么我建議你把這篇文章多看幾遍,反正就是那么回事,沒什么大不了的。我當(dāng)初就是走了很多彎路,也沒有人教我,只有靠自己在那瞎摸索和各種百度,當(dāng)然,現(xiàn)在想想很簡單了。

我們的miniQuery的定義就放在這個自執(zhí)行函數(shù)里面,這樣一來,只要有人調(diào)用了這個js文件,就能調(diào)用miniQuery函數(shù)了。

當(dāng)然,你直接放在外面其實也沒事,因為反正就一個方法,而且這個方法本來就是要暴露出去的。

這邊為了說明自執(zhí)行函數(shù),就硬加進來了。

我們把miniQuery的定義丟進去。

比如,像這樣子的:

(function(){
   
   var miniQuery = function(){
    alert('Hello miniQuery!');
   }
   
})();

我們嘗試在外面調(diào)用:

miniQuery();

很遺憾,調(diào)不到。

Paste_Image.png

我們再回顧一下代碼:

(function(){
   
   var miniQuery = function(){
        alert('Hello miniQuery!');
   }
   
})();

miniQuery();

原來,miniQuery是存在于一個閉包中的,它可以訪問到父級作用域的變量,但是反過來就不行,除非函數(shù)自己用 return 的方式將私有數(shù)據(jù)暴露出去。這些在之前的關(guān)于閉包的文章里面已經(jīng)解釋過了,這里不再贅述。

解決方法有很多,比如,最簡單的,我們直接把var去掉,這樣就會發(fā)生一次變量提升,miniQuery被升級為全局變量,掛在window對象上面。

(function(){
   
   miniQuery = function(){
    alert('Hello miniQuery!');
   }
   
})();

miniQuery();
Paste_Image.png

成了,簡單明了,干干凈凈。

雖然我覺得很有道理,但是我看別人的代碼,他們封裝自己的js庫的時候,幾乎沒有這樣做的,因此我們也采用一種大眾的做法。

即,我們把window作為參數(shù)傳進去,然后手動將miniQuery掛上去。

(function(win){
   
   var miniQuery = function(){
    alert('Hello miniQuery!');
   }

   win.miniQuery = miniQuery;
   
})(window);

miniQuery();
Paste_Image.png

是不是也可以呢?

如果你覺得每次寫miniQuery太麻煩,那么我們可以給它換一個名字,比如 $

(function(win){
   
   var miniQuery = function(){
    alert('Hello miniQuery!');
   }
   
   
   win.$ = miniQuery;
   
})(window);

$();

這樣就差不多了。

4. miniQuery 包裹對象

我們先弄來一個測試用的網(wǎng)頁:

.wrap {
    width:80px;
    height:80px;
    background:darkslateblue;
    margin:20px;
    border-radius: 2px;
}
<body>
    <div class='boxes'>
        <div id='box1' class='wrap'></div>
        <div id='box2' class='wrap'></div>
        <div id='box3' class='wrap'></div>
    </div>
</body>
Paste_Image.png

舉一個例子,現(xiàn)在我們要獲取id為box1的盒子,并把它的背景色改為紅色。

用js代碼,我們會這樣做:

var box2 = document.getElementById('box2');
box2.style.backgroundColor = 'red';

思路很清晰,分為簡單的兩步:

第一步:獲取dom對象。
第二部:設(shè)置其背景色為紅色。

同樣的,我們的 miniQuery 也要這么做,首先得獲取對象,然后進行操作。就好像你做飯,首先得有米面吧。所謂巧婦難為,無米之炊。

于是,我們有了下面的代碼:

var miniQuery = function(selector){
   var miniQuery = document.getElementById(selector);
   console.log(miniQuery);
}

selector 代表選擇器,它只是一個參數(shù)名字,參數(shù)列表的名稱是可以自己定義的。你寫 aaa , bbb , ccc 都沒問題,只要你愿意的話。

我以前經(jīng)??磩e人寫的代碼,參數(shù)里面有callback,現(xiàn)在我知道是回調(diào)函數(shù)的意思??墒俏乙郧安恢溃缓缶陀X得很困惑,作為一個英語比日語還差的js玩家,我感到很那個啥。

其實無所謂,只是一個名字而已,你寫什么都行,只要符合標(biāo)識符的命名規(guī)范就成。

總有人覺得,看到參數(shù)里邊寫了context(上下文),callback(回調(diào)函數(shù))這樣的詞匯,就覺得很困惑。

不要困惑啦,不要再驚恐啦,它就是一個名稱罷了!

。。。

額,扯遠了,繼續(xù)回來。

我們在外面調(diào)用miniQuery ~

window 上面掛的是 $ , 其實就是 miniQuery

$('box1');

運行結(jié)果

Paste_Image.png

嗯,確實取到了呢。

接下里,我們給dom元素變更背景色為紅色。

var miniQuery = function(selector){
   var miniQuery = document.getElementById(selector);
   miniQuery.style.backgroundColor = 'red';
}
Paste_Image.png

效果確實出來了。

可是呢,如果用戶過幾天又來個需求,說我要把box1的寬度變?yōu)橹暗膬杀叮阍趺崔k?

總不可能去修改源碼吧!

這時候,我們就可以考慮能不能通過一個什么辦法,我先用miniQuery把你傳進來的東西包裝成dom元素,保存起來返回給你,同時再給你返回一大堆方法,比如改變高度啊,添加背景色啊等等。那么,操作的就是之前保存的元素了。也就是你一開始希望操作的元素。

這是一個很好的想法,我們經(jīng)過代碼的重寫,最終產(chǎn)生了這樣的一個miniQuery函數(shù):

var miniQuery = function(selector){
    var miniQuery = document.getElementById(selector);
    
    return {
        obj : miniQuery , //將dom元素保存起來,再返回給你
        
        // ------------------------ css 相關(guān) ------------------------//
        backgroundColor : function(color){
            this.obj.style.backgroundColor = color;
        }
    }
}
   
   win.$ = miniQuery;
   
})(window);

我們再調(diào)用一次,看看這回它給我們返回的是什么東東?

var $box = $('box1');
console.log($box);
Paste_Image.png

可見,它給我們返回的是一個json對象,里面有 obj 變量和 backgroundColor 函數(shù)。這樣的好處就是極大的擴展了我們的miniQuery,你給我一個選擇器,我就包起來,然后不僅把它返回給你,而且還給你各種api方法!

于是我們就可以直接調(diào)用 backgroundColor 函數(shù)了。

var $box = $('box1');
$box.backgroundColor('red');
Paste_Image.png

成了。

我們現(xiàn)在返回的,不是一個單純的dom元素,dom元素只是它的一部分??梢哉f,我們返回給用戶的是一個miniQuery對象!

篇幅關(guān)系,先介紹到這里吧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內(nèi)容

  • 1.幾種基本數(shù)據(jù)類型?復(fù)雜數(shù)據(jù)類型?值類型和引用數(shù)據(jù)類型?堆棧數(shù)據(jù)結(jié)構(gòu)? 基本數(shù)據(jù)類型:Undefined、Nul...
    極樂君閱讀 5,560評論 0 106
  • 吃貨的碰撞(2) 不知道是心理作用還是什么原因,后背上的那份灼熱感依然存在,不安的感覺越來越重,邱然從來就不信什么...
    老街小主閱讀 191評論 0 0
  • 盼著,盼著, 西風(fēng)送來了信件。 枯黃色的信紙, 染著秋姑娘的芬芳清香, 一片紅楓葉成了秋姑娘的禮物。 淡淡的黃色,...
    沙華_927閱讀 276評論 0 0