ExtJS 把 Javascript 原生的功能加以擴充, 其中迴圈功能就屬於上圖中的核心元件部份. 迴圈是寫程式常用的技巧, 通常用在拜訪陣列元素或物件屬性. ExtJS 在許多類別或物件上都提供了 each 或 forEach 方法, 於 API 中搜尋約有 24 個 :
http://docs.sencha.com/extjs/4.2.2/#!/api/Ext.tab.Panel
此處我們測試其中四個最常用的 :
- Ext.Array.each() 與 Ext.Array.forEach()
- Ext.Object.each()
- Ext.iterate()
each(iterable, fn, [scope], [reverse])
而回呼函式呼叫結構如下 :
fn(item, index, arr)
它會傳入三個參數, 其中 item 是目前所拜訪之陣列元素, index 是其索引, 而 arr 則為陣列本身, 這三個參數都是備選的, 通常應用時是傳入前兩個參數 (不傳入參數我們是要幹啥?).
我們設立一個陣列 color, 然後用 Ext.each() 來遍歷, 如下列範例 1 所示 :
測試範例 1 : http://mybidrobot.allalla.com/extjstest/each_1.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.Array.each(color,
function(item, index) {
output += "color[" + index + "]=" + item + "<br>";
}
);
Ext.Msg.alert("訊息",output);
在上例中, 回呼函式沒有接收第三參數, 即拜訪之陣列本身, 其實我們也可以用第三參數來取得陣列元素, 如下列範例 2 所示 :
測試範例 2 : http://mybidrobot.allalla.com/extjstest/each_2.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.each(color,
function(item, index, arr) {
output += "color[" + index + "]=" + arr[index] + "<br>";
}
);
Ext.Msg.alert("訊息",output);
範例 2 中我們只是將範例 1 的 item 改成 arr[index], 效果一樣.
下面來測試 each() 的第四參數 reverse, 如下列範例 3 所示 :
測試範例 3 : http://mybidrobot.allalla.com/extjstest/each_3.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.Array.each(color,
function(item, index) {
output += "color[" + index + "]=" + item + "<br>";
},
this, true
);
Ext.Msg.alert("訊息",output);
可見當 reverse 設為 true 時, 將會逆序從陣列尾端開始拜訪. 注意, scope 不能略去不填, 否則 true 會被當成 scope, 導致被認為沒有傳入 reverse 而仍然以預設之順序拜訪陣列.
事實上, 使用 Javascript 原生的 for 或 for in 語法也不會很麻煩呀, 如下範例 4-1 與 4-2 所示 :
測試範例 4-1 : http://mybidrobot.allalla.com/extjstest/each_4_1.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
for (var i=0; i
output += "color[" + i + "]=" + color[i] + "<br>";
};
Ext.Msg.alert("訊息",output);
測試範例 4-2 : http://mybidrobot.allalla.com/extjstest/each_4_2.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
for (i in color) {
output += "color[" + i + "]=" + color[i] + "<br>";
};
Ext.Msg.alert("訊息",output);
在 ECMAScript 5 還定義了一個 Javascript 陣列的原生 forEach() 方法, 用法跟 Ext.Array.forEach 幾乎一樣 (誰抄誰?), 如下列範例 4-3 所示 :
測試範例 4-3 : http://mybidrobot.allalla.com/extjstest/each_4_3.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
color.forEach(function(item,index){
output += "color[" + index + "]=" + color[index] + "<br>";
});
Ext.Msg.alert("訊息",output);
不過 Ext.each() 還是有比較特殊的地方啦, 例如第二個參數回呼函式 fn 是有傳回值的 (布林值, 預設傳回 true), 如果傳回 false 會讓迴圈停止, 例如下列範例 5 所示 :
測試範例 5 : http://mybidrobot.allalla.com/extjstest/each_5.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.each(color,
function(item, index) {
if (item=="green") {return false;}
output += "color[" + index + "]=" + item + "<br>";
}
);
Ext.Msg.alert("訊息",output);
可見當拜訪至索引 4 green 時, 便傳回 false 而中斷迴圈執行了.
接著要測試 Ext.Array 提供的另一個拜訪陣列的方法 forEach(). 其呼叫格式如下 (回呼函式 fn 與 each 相同, 只是傳回值不會影響迴圈執行) :
forEach(array, fn, [scope])
此方法用途與上面的 each() 相同, 但功能與效能上有些差異; 據 API 描述, forEach() 是委派給 Javascript 原生的 Array.prorotype.forEach() 來執行, 因此效能比 each() 要好. 其次, 功能上的差異是, forEach() 不能設定逆序拜訪, 也不能透過回呼函式傳回 false 來中斷迴圈.
下面範例 6 是改自範例 1, 只是將 each 改為 forEach 而已 :
測試範例 6 : http://mybidrobot.allalla.com/extjstest/each_6.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.Array.forEach(color,
function(item, index) {
output += "color[" + index + "]=" + item + "<br>";
}
);
Ext.Msg.alert("訊息",output);
結果是一樣的, 但是要注意, 這裡必須寫 Ext.Array.forEach 全名 (因為它沒有 Ext.forEach 這樣的別名), 否則會出現找不到物件之錯誤.
下面要測試物件的拜訪方法 : Ext.Object.each(), 其呼叫結構如下 :
each(object, fn, [scope])
其第一參數為要拜訪之物件, 第二參數 fn 為回呼函式, 其呼叫結構如下 :
fn(key, value, object)
這三個參數都非必要, 但通常都要傳入前兩個 (不傳進來那能幹啥?). 如下列範例 7 所示 :
測試範例 7 : http://mybidrobot.allalla.com/extjstest/each_7.htm [看原始碼]
var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
var output="";
Ext.Object.each(country,
function(key, value) {
output += "country." + key + "=" + value + "<br>";
}
);
Ext.Msg.alert("訊息",output);
跟 Ext.Array.each() 一樣, 物件的 each 也可以在回呼函式中傳回 false 來中斷物件之拜訪, 如下列範例 8 所示 :
測試範例 8 : http://mybidrobot.allalla.com/extjstest/each_8.htm [看原始碼]
var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
var output="";
Ext.Object.each(country,
function(key, value, object) {
if (key=="code") {return false;}
output += "country." + key + "=" + value + "<br>";
}
);
Ext.Msg.alert("訊息",output);
可見拜訪至國碼屬性 code 時就傳回 false 而停止迴圈了.
其實, 陣列是物件的一種, 只是其 key 為數值罷了. 因此我們也可以將陣列傳入 Ext.Object.each() 中遍歷, 如下列範例 9 所示 :
測試範例 9 : http://mybidrobot.allalla.com/extjstest/each_9.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.Object.each(color,
function(key, value, object) {
if (key=="code") {return false;}
output += "color." + key + "=" + value + "<br>";
}
);
Ext.Msg.alert("訊息",output);
可見陣列當物件看時, 其 key 就是其索引.
如果物件有方法呢? 用 each() 遍歷時, 其值就是代表方法定義的函式內容, 如下列範例 10 所示 :
測試範例 10 : http://mybidrobot.allalla.com/extjstest/each_10.htm [看原始碼]
function user(account,password) {
this.account=account;
this.password=password;
this.changePassword=changePassword;
function changePassword(newPassword) {
this.password=newPassword;
}
}
var user1=new user("tony","123");
var output="";
Ext.Object.each(user1,
function(key, value, object) {
output += "user1." + key + "=" + value + "<br>";
}
);
Ext.Msg.alert("訊息",output);
在上例中, 我們定義了具有一個方法的物件 user, 當遍歷物件時, 方法的內容會被當作 value 顯示出來.
上面範例 9 我們用 Ext.Object.each() 來拜訪陣列, 如果反過來呢? 也就是用 Ext.Array.each() 來拜訪物件呢? 這是行不通的, 整個物件會被當作只有一個元素的陣列, 如下範例 11 所示 :
測試範例 11 : http://mybidrobot.allalla.com/extjstest/each_11.htm [看原始碼]
var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
var output="";
Ext.Array.each(country,
function(item, index) {
output += "country[" + index + "]=" + item + "<br>";
}
);
Ext.Msg.alert("訊息",output);
所以, 陣列可以當物件來拜訪, 但是物件卻不能當物件來拜訪.
其實 ExtJS4 的根名稱空間 Ext 有一個 Ext.iterate() 方法是可以傳入陣列或物件的. 其參數格式與 Ext.Object.each() 相同, 從源碼可知, 其實它會先判斷傳入的是陣列還是物件, 若是陣列就呼叫 Ext.Array.each(); 否則就呼叫 Ext.Object.each(). 下列範例 11-1 是用 iterate 來拜訪陣列 :
測試範例 11-1 : http://mybidrobot.allalla.com/extjstest/each_11_1.htm [看原始碼]
var color=["red","white","blue","yellow","green","pink","black"];
var output="";
Ext.iterate(color,
function(key, value) {
output += "color.'" + key + "=" + value + "<br>";
}
);
Ext.Msg.alert("訊息",output);
範例 11-2 是用 iterate 來拜訪物件 :
測試範例 11-2 : http://mybidrobot.allalla.com/extjstest/each_11_2.htm [看原始碼]
var country={name:"伊朗",capital:"德黑蘭",code:"+98"};
var output="";
Ext.iterate(country,
function(key, value) {
output += "country." + key + "=" + value + "<br>";
}
);
Ext.Msg.alert("訊息",output);
最後我們來比較一下各種迴圈的效能, 如下列範例 12 所示 (請用 Chrome 瀏覽, 按 Ctrl+Shift+J 即可打開下方控制台看到結果, Firefox 則按 F12) :
測試範例 12 : http://mybidrobot.allalla.com/extjstest/each_12.htm [看原始碼]
//製作 100000 個元素的陣列
var arr=[];
for (var i=0; i<100000; i++) {arr[i]=i + 1;}
//使用 Ext.Array.each
var sum=0;
console.time("Ext.Array.each");
Ext.Array.each(arr, function(item, index) {sum += item;});
console.timeEnd("Ext.Array.each");
//使用 Ext.Array.forEach
sum=0;
console.time("Ext.Array.forEach");
Ext.Array.forEach(arr, function(item, index) {sum += item;});
console.timeEnd("Ext.Array.forEach");
//使用 Ext.Object.each 迴圈
sum=0;
console.time("Ext.Object.each");
Ext.Object.each(arr,function(key, value) {sum += value;});
console.timeEnd("Ext.Object.each");
//使用 Ext.iterate 迴圈
sum=0;
console.time("Ext.iterate");
Ext.iterate(arr,function(key, value) {sum += value;});
console.timeEnd("Ext.iterate");
//使用 for 迴圈
sum=0;
console.time("for");
for (var i=0; i<100000; i++) {sum += arr[i];}
console.timeEnd("for");
//使用 for in 迴圈
sum=0;
console.time("forin");
for (item in arr) {sum += item;}
console.timeEnd("forin");
//使用 forEach 迴圈
sum=0;
console.time("forEach");
arr.forEach(function(item, index) {sum += item;});
console.timeEnd("forEach");
此處我們利用瀏覽器的 console.time() 與 console.timeEnd() 方法來計算迴圈所耗費的時間 (注意所傳入之參數起訖必須一致才能正確計算時間), 每次執行所得數據都會稍有變化, 但基本上差異不大. 由上可知這四種迴圈的效能依序是 :
第一名=for
第二名=forEach, Ext.iterate, Ext.Array.forEach
第三名=for in
第四名=Ext.Object.each
可見 Javascript 原生的 for 效能還是最棒的, 但同樣原生的 for in 卻很遜, 所以最好少用. 其次, Ext.Array.forEach 委由 原生的 Array.prorotype.forEach 來執行, 效能確實比 each 要稍好, 雖然差距不大, 但迴圈數多時就很可觀了.
沒有留言:
張貼留言