JavaScript 框架設計


JavaScript 高級框架設計

在現在,jQuery等框架已經非常完美,以致於常常忽略了JavaScript原生開發,但是這是非常重要的.

所以,我打算寫一個簡單的框架,兩個目的

  • 熟練框架的思想

  • 熟練DOM操作.

所以我打算,模仿jQuery,實現一個簡單的類似jQuery的庫 Hpawn.

關於JavaScript面向對象高級,會在以后介紹.

關於我所有的代碼,都會托管到 github上,https://github.com/apawn

我的開發環境是VSCode.


在Hpawn中,我會分為七個模塊.

  • 選擇器模塊

  • dom操作模塊

  • 事件模塊(click,on) 比較簡單

  • 屬性模塊

  • 樣式模塊

  • 動畫模塊,使用緩動函數而不是css3.

  • 面向對象封裝

選擇器模塊

在這里,我們要實現一個類似於jQuery選擇器的東西,但是要簡單很多.

代碼 01.html

<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style>
        div{
            width: 300px;
            height: 100px;
            border: 1px red solid;
        }
    </style>
</head>
<body>
    <div></div>
    <div></div>
 </body>

現在我們要給上面的div添加綠色背景,按照傳統的辦法,可能會這么寫

01.js

   onload = function(){
        var divs = document.getElementsByTagName("div");
        for(var i = 0,length = divs.length;i<length;i++){
                divs[i].style.backgroundColor="green";
        }
    }

但是,這樣的DOM操作真的很麻煩.

代碼冗余.丑,易錯, 還會影響性能,為什么?

因為代碼要壓縮,document.getElementsByTagName()不會改變,但是如果改為get方法,只會存在一個g.

現在開始解決,首先解決代碼冗余

02.js

先封裝一個get方法

var get = function (tag) {
    return document.getElementsByTagName(tag);
}

代碼可能會變成現在這樣,

var divs = get("div");
for(var i = 0,length = divs.length;i<length; i++){
        divs[i].style.background = "green"; 
}

但是,似乎還是很冗余,繼續封裝.

03.js

var get = function (tag) {
    return document.getElementsByTagName(tag);
}
var each = function (arr,style,value) {
    for(var i = 0,length = arr.length;i<length; i++){
        arr[i].style[style] = value;
    }
}


var divs = get("div");
each(divs,"backgroundColor","green") ;

代碼變成了上面那樣,有木有覺得好了很多呢.
可能並不覺得會簡單,那是因為我只包含了兩個元素.
但是,上面的代碼只能做一件事情,就是設置樣式,可是我還設置動作,設置屬性.

好了,第一步優化完成,下面開始第二部.
04.js

var get = function (tag) {
    return document.getElementsByTagName(tag);
}
var each = function (arr,func) {
    for(var i = 0,length = arr.length;i<length; i++){
        func(arr[i],i);
    }
}


var divs = get("div");
each(divs,function (item,index) {
   item.style.background = "red"; 
});

我傳遞了一個函數進去,我將arr[i] 和i 傳給函數,這是最關鍵的一步.

但是,一個問題出現了.
比如,我先查找數組 arr =[1,2,3,4] 中元素為3的索引,上面的代碼可以嗎?
我找到了,怎么跳出呢?

也許想到這樣的代碼

each(arr,function(v,i){
  if(v===5){
    breadk;
  }
})

但是,break 可以寫在函數中嗎??
// 為了查看單獨的js代碼,我使用了node作為運行環境,所以代碼庫里有一些並沒有對應的html文件.
還是需要再修改each 函數
05.js

 var each = function (arr, func) {
    for (var i = 0, length = arr.length; i < length; i++) {
        if (func(arr[i], i) === false) {
            break;
        }
    }
};

var a = [1, 2, 3, 4];
var index = -1;
each(a, function (v, i) {
    if (v === 3) {
        index = i;
        return false;
    }
});
console.log(index);  // 輸出2

大功告成!

但是,我們每次調用函數的時候都要傳一個v和i 才能使用,如果不小心忘了呢?

似乎有更好的辦法

07.js,繼續回到第一個例子

var get = function (tag) {
    return document.getElementsByTagName(tag);
}
var each = function (arr, func) {
    for (var i = 0, length = arr.length; i < length; i++) {
        if (func.call(arr[i],arr[i], i) === false) {
            break;
        }
    }
};
var divs = get("div");
each(divs,function (item,index) {
   this.style.background = "red"; 
});

現在,我們看看jQuery的each是怎么實現的.

each: function( obj, callback, args ) {
		var value,
			i = 0,
			length = obj.length,
			isArray = isArraylike( obj );

		 else 
			if ( isArray ) {
				for ( ; i < length; i++ ) {
					value = callback.call( obj[ i ], i, obj[ i ] );

					if ( value === false ) {
						break;
					}
				}
			} else {
				for ( i in obj ) {
					value = callback.call( obj[ i ], i, obj[ i ] );
					if ( value === false ) {
						break;
					}
				}
		}

		return obj;
	},

是不是差不多呢....

現在,繼續優化get方法.

如果用get方法獲得多個元素,就會獲得多個數組,為了簡化開發,可以考慮合並到一個數組中,調用多次get方法.

給get添加一個result參數

var get = function (tag, result) {
    result = result || [];
    result.push.apply(result, document.getElementsByTagName(tag));
    return result;
}
var each = function (arr, func) {
    for (var i = 0, length = arr.length; i < length; i++) {
        if (func.call(arr[i], arr[i], i) === false) {
            break;
        }
    }
};
var divs = get("div");
each(divs, function (item, index) {
    this.style.background = "red";
});

但是,這里為什么要用apply呢?

因為document.getElementsByTagName() 返回一個偽數組,但是push方法只能接受真數組,在這里調用apply,因為apply接受的其他參數必須是一個數組,這里把document.getElementsByTagName() 的返回結果進行展開,然后一個一個push進去.

當然也可以這么寫,但是性能肯定不及原生方法性能高.

each( document.getElementsByTagName(tag),function(){
        result.push(this);
})

再次體現了封裝的意義.

下面JavaScript高級框架設計(二) 將會開始介紹基本選擇器的實現


免責聲明!

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



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