JavaScript設(shè)計模式五(迭代器模式)
迭代器模式的定義:
迭代器模式的意思就是提供一種方法能夠順序的訪問聚合對象中的各個元素,同時又不暴露該對象的內(nèi)部表示。大多數(shù)瀏覽器已經(jīng)支持了Array.prototype.forEach
由于兼容性問題宙拉,我們目前使用原生的forEach比較少延塑,我們先看看jQuery中的each方法
jQuery中的迭代器
jQuery中提供了each方法蠢护,來迭代聚合對象例如:
$.each([1,2,3], function(i, n) {
console.log('下標:' + i);
console.log('對應(yīng)的值:' + n);
});
如何自己實現(xiàn)一個迭代器
其實大家用到很多是for循環(huán)堤结,我們利用for循環(huán)就可以很快的實現(xiàn)一個迭代器
cons each = function(ary, callback) {
for(let i = 0; i < ary.length; i++) {
callback.call(null, i, ary[i]);
}
}
each([1,2,3], function(i, n) {
console.log('下標:' + i);
console.log('對應(yīng)的值:' + n);
});
內(nèi)部迭代器與外部迭代器
迭代器根據(jù)不同的場景分為內(nèi)部迭代器和外部迭代器
內(nèi)部迭代器
上面的each函數(shù)就是屬于內(nèi)部迭代器,each函數(shù)的內(nèi)部定義好了規(guī)則肉拓,外部只需要一次調(diào)用吩案,不需要關(guān)心內(nèi)部迭代器是如何實現(xiàn)的。但是由于內(nèi)部迭代器的迭代規(guī)則被提前定義好了帝簇,each函數(shù)就沒法同時迭代連個數(shù)組了,例如我們需要判斷兩個數(shù)組是否相等靠益,這里就只能利用each的回調(diào)函數(shù)了丧肴。
var compare = function(array1, array2) {
if (array1.length !== array2.length) {
console.log('not equal');
throw new Error();
}
each(array1, function(i, n) {
if (array1[i] !== array2[i]) {
console.log('not equal');
throw new Error();
}
});
console.log('equal');
}
compare([1,2,3], [1,2,3]);
但是其實這里其實雖然滿足了我們的需要,但是有一些缺點胧后,大家可以自己思考一下
在ES6中芋浮,新增了3中內(nèi)部迭代器,大家有興趣可以了解一下:entries()壳快、values()纸巷、keys()
外部迭代器
外部迭代器必須顯式的請求迭代下一個元素,我們先看一下ES6中的迭代器Iterator
迭代器Iterator的遍歷過程:
- 創(chuàng)建一個指針對象眶痰,指向當前數(shù)據(jù)結(jié)構(gòu)的起始位置瘤旨,也就是說迭代器的本質(zhì)是一個指針對象
- 第一次調(diào)用指針對象的next方法,可以將指針指向第一個數(shù)據(jù)成員
- 第二次調(diào)用指針對象的next方法竖伯,可以將指針指向第二個數(shù)據(jù)成員
- 不斷調(diào)用指針對象的next方法存哲,直到指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置
在ES6中,一般迭代器和生成器一起使用
function *createIterator() {
yield 1;
yield 2;
yield 3;
}
let iterator = createIterator();
iterator.next();
iterator.next();
iterator.next();
這里每當執(zhí)行一次yield時七婴,函數(shù)會停止執(zhí)行祟偷,直到再次調(diào)用迭代器的next方法。老生長談的問題打厘,我們看看ES5中如何實現(xiàn)類似的效果
function createIterator(items) {
var i = 0;
var done = (i >= items.length);
var value = !done? items[i]: void 0;
return {
next: function() {
done = (i >= items.length);
value = !done? items[i++]: void 0;
return {
done: done,
value: value,
};
},
isDone: function() {
return done;
},
getCurrItem: function(){
return value;
}
}
}
var iterator = createIterator([1,2,3]);
iterator.next();
iterator.next();
iterator.next();
iterator.next();
那我們在回到上面的問題如何判斷兩個數(shù)組是否相等的問題修肠?
var compare = function(iterator1, iterator2) {
while(!iterator1.isDone() && !iterator2.isDone()) {
if (iterator1.getCurrItem() !== iterator2.getCurrItem()) {
console.log('not equal');
};
iterator1.next();
iterator2.next();
}
console.log('equal');
}
var iterator1 = createIterator([1,2,3]);
var iterator2 = createIterator([1,2,3]);
compare(iterator1, iterator2);
這兩種迭代器沒有優(yōu)劣之分,主要是根據(jù)場景來定
迭代類數(shù)組對象和字面量常量
不僅僅是數(shù)組可以被迭代户盯,對象嵌施、arguments這些有l(wèi)ength屬性,并且可以利用下標訪問的都可以被迭代莽鸭。在ES6中規(guī)定了可迭代的對象需要具備Symbol.iterator屬性艰管,具體可以參考阮一峰的ES6入門。一般來說我們對于數(shù)組采用的是for循環(huán)蒋川,對于字面量對象可以利用for in來迭代
迭代器模式應(yīng)用例子
例如我們需要做一個文件上傳的代碼牲芋,開始是這樣的:
var getUploadObj = function() {
try {
// IE的上傳組件
return new ActiveXObject("TXFTNActiveX.FTNUpload")
} catch (e){
if (supportFlash()) {
var str = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
} else {
var str = '<input name="file" type="file" />'
return $(str).appendTo($('body'));
}
}
}
這段代碼本身沒啥問題,我們可能平常就是這么寫的,但是是否有優(yōu)化的空間呢缸浦?
這些try catch還有if else總是感覺不夠友好夕冲,而且如果我們增加了html5的上傳方式,我們又需要修改getUploadObj函數(shù)裂逐,這樣大家好像想到了方法歹鱼,把這3中上傳方式各自封裝成方法
var getActiveUploadObj = function(){
try {
// IE的上傳組件
return new ActiveXObject("TXFTNActiveX.FTNUpload")
} catch (e){
return false;
}
}
var getFlashUploadObj = function() {
if (supportFlash()) {
var str = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
}
return false;
}
var getFormUploadObj = function() {
var str = '<input name="file" type="file" />'
return $(str).appendTo($('body'));
}
var uploadObj = iteratorObj(getActiveUploadObj, getFlashUploadObj, getFormUploadObj);
function iteratorObj() {
for(var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] === 'Function') {
var uploadObj = arguments[i]();
if (uploadObj !== false) {
return uploadObj;
}
}
}
}