亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Prototype源碼淺析 Enumerable部分(二)

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

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

  回到Enumerable上來,由于each(each = function(iterator, context){})方法的本質(zhì)就是一個(gè)循環(huán),對(duì)于其第一個(gè)參數(shù)iterator,并不包含循環(huán),因此直接調(diào)用break語句會(huì)報(bào)語法錯(cuò)誤,于是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í)行中拋出一個(gè)$break,那么循環(huán)就中斷。如果不是$break,那么就拋出相應(yīng)錯(cuò)誤,程序也穩(wěn)定點(diǎn)。這里的$break的定義并沒有特殊要求,可以按照自己的喜好隨便更改,不過意義不大。

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


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

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

檢測(cè):all | any | include

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

all : 若 Enumerable 中的元素全部等價(jià)于 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;
}

  對(duì)于all方法來說,里面的兩個(gè)參數(shù)都不是必須的,所以,內(nèi)部提供了一個(gè)函數(shù),以代替沒有實(shí)參時(shí)的iterator,直接返回原值,名字叫做Prototype.K。Prototype.K的定義在庫的開頭,是一個(gè)返回參數(shù)值的函數(shù)Prototype.K = function(x){return x;}。另外,在all方法中,只要有一個(gè)項(xiàng)的處理結(jié)果為false,整個(gè)過程就可以放棄(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是一個(gè)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;
}

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


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

function include(object) {
if (Object.isFunction(this.indexOf))//這個(gè)判定函數(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ù)的方法:返回單個(gè)元素:max | min | detect返回一個(gè)數(shù)組:grep | findAll | reject | partition 其中max和min并不局限于數(shù)字的比較,字符的比較一樣可以。max(iterator, context)依舊可以帶有兩個(gè)參數(shù),可以先用iterator處理之后再來比較值,這樣的好處就是不必局限于特定的數(shù)據(jù)類型,比如,對(duì)象數(shù)組按照一定規(guī)則取最大值:
復(fù)制代碼 代碼如下:

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

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

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

擴(kuò)展之后,value要進(jìn)一步變?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是找到一個(gè)true就返回true,detect是找到一個(gè)true就返回滿足true條件的那個(gè)值。源碼就不貼了。grep 這個(gè)很眼熟啊,一個(gè)unix/linux工具,其作用也很眼熟——就是返回所有和指定的正則表達(dá)式匹配的元素。只不過unix/linux只能處理字符串,這里擴(kuò)展了范圍,但是基本形式還是沒有變。如果集合的每一項(xiàng)都是字符串,那么實(shí)現(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)在要處理的集合可能并都是字符串,為了達(dá)到更廣泛的應(yīng)用,首先要考慮的就是調(diào)用形式。看上面的實(shí)現(xiàn),注意這么一句:
if(value.match(filter))
其中value是個(gè)字符串,match是String的方法,現(xiàn)在要擴(kuò)展所支持的類型,要么給每一個(gè)value都加上match方法,要么轉(zhuǎn)換形式。顯然第一種巨響太大,作者轉(zhuǎn)換了思路:
if (filter.match(value))
這么一來,不論value為何值,只要filter有對(duì)應(yīng)的match方法即可,上面對(duì)于RegExp對(duì)象,是沒有match方法的,于是在源碼中,作者擴(kuò)展了RegExp對(duì)象:
RegExp.prototype.match = RegExp.prototype.test;
注意上面的match和String的match有本質(zhì)區(qū)別。這么一來,如果value是對(duì)象,我們的filter只需要提供相應(yīng)的檢測(cè)對(duì)象的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;
}

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

相關(guān)文章

最新評(píng)論