欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Prototype源碼淺析 Enumerable部分(二)

 更新時間:2012年01月18日 01:44:01   作者:  
剩下的方法太多,于是分作兩部分。亮點就是$break和$continue,以及grep方法的思想
前面each方法中掉了一個方面沒有說,就是源碼中的$break和$continue。這兩個變量是預(yù)定義的,其作用相當于普通循環(huán)里面的break和continue語句的作用。出于效率的考慮,在某些操作中并不需要完全遍歷一個集合(不局限于一個數(shù)組),所以break和continue還是很必要的。
對于一個循環(huán)來說,對比下面幾種退出循環(huán)的方式:
復(fù)制代碼 代碼如下:

var array_1 = [1,2,3];
var array_2 = ['a','b','c'];
(function(){
for(var i = 0, len = array_1.length; i < len; i++){
for(var j = 0, len_j = array_1.length; i < len_j; j++){
if('c' === array_2[j]){
break;
}
console.log(array_2[j]);
}
}
})();//a,b,a,b,a,b
(function(){
for(var i = 0, len = array_1.length; i < len; i++){
try{
for(var j = 0, len_j = array_1.length; i < len_j; j++){
if('c' === array_2[j]){
throw new Error();
}
console.log(array_2[j]);
}
}catch(e){
console.log('退出一層循環(huán)');
}
}
})();//a,b,'退出一層循環(huán)',a,b,'退出一層循環(huán)',a,b,'退出一層循環(huán)'
(function(){
try{
for(var i = 0, len = array_1.length; i < len; i++){
for(var j = 0, len_j = array_1.length; i < len_j; j++){
if('c' === array_2[j]){
throw new Error();
}
console.log(array_2[j]);
}
}
}catch(e){
console.log('退出一層循環(huán)');
}
})();//a,b,'退出一層循環(huán)'

  當我們把錯誤捕獲放在相應(yīng)的循環(huán)層面時,就可以中斷相應(yīng)的循環(huán)??梢詫崿F(xiàn)break和break label的作用(goto)。這樣的一個應(yīng)用需求就是可以把中斷挪到外部去,恰好符合Enumerable處的需求。

  回到Enumerable上來,由于each(each = function(iterator, context){})方法的本質(zhì)就是一個循環(huán),對于其第一個參數(shù)iterator,并不包含循環(huán),因此直接調(diào)用break語句會報語法錯誤,于是Prototype源碼中采用上面的第二種方法。
復(fù)制代碼 代碼如下:

Enumerable.each = function(iterator, context) {
var index = 0;
try{
this._each(function(value){
iterator.call(context, value, index++);
});
}catch(e){
if(e != $break){
throw e;
}
}
return this;
};

  一旦iterator執(zhí)行中拋出一個$break,那么循環(huán)就中斷。如果不是$break,那么就拋出相應(yīng)錯誤,程序也穩(wěn)定點。這里的$break的定義并沒有特殊要求,可以按照自己的喜好隨便更改,不過意義不大。

Enumerable中的某些方法在一些現(xiàn)代瀏覽器里面已經(jīng)實現(xiàn)了(參見chrome原生方法之數(shù)組),下面是一張對比圖:


在實現(xiàn)這些方法時,可以借用原生方法,從而提高效率。不過源碼中并沒有借用原生的部分,大概是因為Enumerable除了混入Array部分外,還需要混入其他的對象中。

  看上面的圖示明顯可以看得出,each和map 的重要性,map其實本質(zhì)還是each,只不過each是依次處理集合的每一項,map是在each的基礎(chǔ)上,還把處理后的結(jié)果返回來。在Enumerable內(nèi)部,map是collect方法的一個別名,另一個別名是select,其內(nèi)部全部使用的是collect這個名字。

檢測:all | any | include

這三個方法不涉及對原集合的處理,返回值均是boolean類型。

all : 若 Enumerable 中的元素全部等價于 true,則返回 true,否則返回 false

復(fù)制代碼 代碼如下:

function all(iterator, context) {
var result = true;
this.each(function(value, index) {
result = result && !!iterator.call(context, value, index);
});
return result;
}

  對于all方法來說,里面的兩個參數(shù)都不是必須的,所以,內(nèi)部提供了一個函數(shù),以代替沒有實參時的iterator,直接返回原值,名字叫做Prototype.K。Prototype.K的定義在庫的開頭,是一個返回參數(shù)值的函數(shù)Prototype.K = function(x){return x;}。另外,在all方法中,只要有一個項的處理結(jié)果為false,整個過程就可以放棄(break)了,于是用到了本文開頭的中斷循環(huán)的方法。最后的形式就是:
復(fù)制代碼 代碼如下:

Prototype.K = function(){};
Enumerable.all = function(iterator, context) {
iterator = iterator || Prototype.K;
var result = true;
this.each(function(value, index) {
result = result && !!iterator.call(context, value, index);
if (!result) throw $break;
});
return result;
}

  最后返回的result是一個boolean型,偏離一下all,我們改一下result:
復(fù)制代碼 代碼如下:

function collect(iterator, context) {
iterator = iterator || Prototype.K;
var results = [];
this.each(function(value, index) {
results.push(iterator.call(context, value, index));
});
return results;
}

  此時results是一個數(shù)組,我們不中斷處理過程,保存所有的結(jié)果并返回,恩,這就是collect方法,或者叫做map方法。


any:若 Enumerable 中的元素有一個或多個等價于 true,則返回 true,否則返回 false,其原理和all差不多,all是發(fā)現(xiàn)false就收工,any是發(fā)現(xiàn)true就收工。
復(fù)制代碼 代碼如下:

function any(iterator, context) {
iterator = iterator || Prototype.K;
var result = false;
this.each(function(value, index) {
if (result = !!iterator.call(context, value, index))
throw $break;
});
return result;
}

include:判斷 Enumerable 中是否存在指定的對象,基于 == 操作符進行比較  這個方法有一步優(yōu)化,就是調(diào)用了indexOf方法,對于數(shù)組來說,indexOf返回-1就不可以知道相應(yīng)元素不存在了,如果集合沒有indexOf方法,就只能查找比對了。這里的查找和沒有任何算法,一個個遍歷而已,如果要改寫也容易,不過平時應(yīng)用不多,因此估計也沒有花這個精力去優(yōu)化這個。所以如果結(jié)果為true的時候效率比結(jié)果為false的時候要高一些,看運氣了。
復(fù)制代碼 代碼如下:

function include(object) {
if (Object.isFunction(this.indexOf))//這個判定函數(shù)應(yīng)該很熟悉了
if (this.indexOf(object) != -1) return true;//有indexOf就直接調(diào)用

var found = false;
this.each(function(value) {//這里的效率問題
if (value == object) {
found = true;
throw $break;
}
});
return found;
}


下面是一組過濾數(shù)據(jù)的方法:返回單個元素:max | min | detect返回一個數(shù)組:grep | findAll | reject | partition 其中max和min并不局限于數(shù)字的比較,字符的比較一樣可以。max(iterator, context)依舊可以帶有兩個參數(shù),可以先用iterator處理之后再來比較值,這樣的好處就是不必局限于特定的數(shù)據(jù)類型,比如,對象數(shù)組按照一定規(guī)則取最大值:
復(fù)制代碼 代碼如下:

console.dir([{value : 3},{value : 1},{value : 2}].max(function(item){
return item.value;
}));//3

因此源碼的實現(xiàn)方式可以想象,直接比較的時候,實現(xiàn)方式可以如下:
復(fù)制代碼 代碼如下:

function max() {
var result;
this.each(function(value) {
if (result == null || value >= result) //result==null是第一次比較
result = value;
});
return result;
}

擴展之后,value要進一步變?yōu)関alue = (iterator處理后的返回值):
復(fù)制代碼 代碼如下:

function max(iterator, context) {
iterator = iterator || Prototype.K;
var result;
this.each(function(value, index) {
value = iterator.call(context, value, index);
if (result == null || value >= result)
result = value;
});
return result;
}

min的原理也一樣。detect和any的原理和接近,any是找到一個true就返回true,detect是找到一個true就返回滿足true條件的那個值。源碼就不貼了。grep 這個很眼熟啊,一個unix/linux工具,其作用也很眼熟——就是返回所有和指定的正則表達式匹配的元素。只不過unix/linux只能處理字符串,這里擴展了范圍,但是基本形式還是沒有變。如果集合的每一項都是字符串,那么實現(xiàn)起來回事這樣:
復(fù)制代碼 代碼如下:

Enumerable.grep = function(filter) {
if(typeof filter == 'string'){
filter = new RegExp(filter);
}
var results = [];
this.each(function(value,index){
if(value.match(filter)){
results.push(value);
}
})
return results;
};

但是有一現(xiàn)在要處理的集合可能并都是字符串,為了達到更廣泛的應(yīng)用,首先要考慮的就是調(diào)用形式??瓷厦娴膶崿F(xiàn),注意這么一句:
if(value.match(filter))
其中value是個字符串,match是String的方法,現(xiàn)在要擴展所支持的類型,要么給每一個value都加上match方法,要么轉(zhuǎn)換形式。顯然第一種巨響太大,作者轉(zhuǎn)換了思路:
if (filter.match(value))
這么一來,不論value為何值,只要filter有對應(yīng)的match方法即可,上面對于RegExp對象,是沒有match方法的,于是在源碼中,作者擴展了RegExp對象:
RegExp.prototype.match = RegExp.prototype.test;
注意上面的match和String的match有本質(zhì)區(qū)別。這么一來,如果value是對象,我們的filter只需要提供相應(yīng)的檢測對象的match方法即可。于是就有:
復(fù)制代碼 代碼如下:

function grep(filter, iterator, context) {
iterator = iterator || Prototype.K;
var results = [];

if (Object.isString(filter))
filter = new RegExp(RegExp.escape(filter));

this.each(function(value, index) {
if (filter.match(value))//原生filter是沒有match方法的。
results.push(iterator.call(context, value, index));
});
return results;
}

  對于匹配的結(jié)果,可以處理之后再返回,這就是iterator參數(shù)的作用。不同于max方法,grep是進行主要操作時候再用iterator來處理結(jié)果,max是用iterator處理源數(shù)據(jù)之后再來進行主要操作。因為grep中的filter代替了max中iterator的作用。至于findAll,是grep的加強版,看過grep,findAll就很簡單了。reject就是findAll的雙子版本,作用正好相反。partition就是findAll + reject,組合親子版本。轉(zhuǎn)載請注明來自小西山子【http://www.cnblogs.com/xesam/】

相關(guān)文章

最新評論