前言
本系列文章主要根據《JavaScript設計模式與開發實踐》整理而來,其中會加入了一些自己的思考。希望對大家有所幫助。
文章系列
概念
迭代器模式是指提供一種方法順序訪問一個聚合物件中的各個元素,而又不需要暴露該物件的內部表示。迭代器模式可以把迭代的過程從業務邏輯中分離出來,在使用迭代器模式之後,即使不關心物件的內部構造,也可以按順序訪問其中的每個元素。
場景
JavaScript已經內建迭代器,如forEach Iterator等,再如jquery的$.each
分類
內部迭代器
定義
內部已經定義好了迭代規則,它完全接手整個迭代過程,外部只需要一次初始呼叫
複製程式碼
實現
var each = function (ary, callback) {
for (var i = 0; i < ary.length; i++) {
callback(i, ary[i])
}
}
each([1, 2, 3, 4, 5], function (i, item) {
console.log(i, item)
})
複製程式碼
優缺點
優點:內部迭代器在呼叫的時候非常方便,外界不用關心迭代器內部的實現,跟迭代器的互動也僅 僅是一次初始呼叫
缺點:由於內部迭代器的迭代規則已經被提前規 定,上面的 each 函式就無法同時迭代2個陣列,如下程式碼
var compare = function( ary1, ary2 ){
if ( ary1.length !== ary2.length ){
throw new Error ( 'ary1 和ary2 不相等' );
}
each( ary1, function( i, n ){
if ( n !== ary2[ i ] ){
throw new Error ( 'ary1 和ary2 不相等' );
}
});
alert ( 'ary1 和ary2 相等' );
};
compare( [ 1, 2, 3 ], [ 1, 2, 4 ] ); // throw new Error ( 'ary1 和ary2 不相等' );
複製程式碼
外部迭代器
定義
外部迭代器必須顯式地請求迭代下一個元素
複製程式碼
實現
我們模擬一個es6迭代器
var Iterator = function (ary) {
this.ary = ary
this.index = 0
}
Iterator.prototype.isDone = function () {
return this.index >= this.ary.length
}
Iterator.prototype.next = function () {
if (!this.isDone()) {
var res = this.ary[this.index]
this.index++
return {
value: res,
done: this.isDone()
}
}
}
var a = new Iterator([1, 2, 3])
while (!a.isDone()) {
console.log(a.next())
}
複製程式碼
下面解決一下上面那個問題
var a = new Iterator([1, 2, 3, 3])
var b = new Iterator([1, 2, 3])
function compare(iterator1, iterator2) {
while (!iterator1.isDone() || !iterator2.isDone()) {
if (iterator1.next().value !== iterator2.next().value) {
return false
}
}
return true
}
compare(a, b)
複製程式碼
例子
檔案上傳
實現檔案上傳物件
var getUploadObj = function () {
try {
return new ActiveXObject("TXFTNActiveX.FTNUpload");
} catch (e) {
// IE 上傳控制元件
if (supportFlash()) { // 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'));
}
}
};
複製程式碼
缺點:第一是很難閱讀,第二是嚴重違反開閉原則
改進
var getActiveUploadObj = function () {
try {
return new ActiveXObject("TXFTNActiveX.FTNUpload");
} catch (e) {
return false;
}
};
var getFlashUploadObj = function () {
if (supportFlash()) { // supportFlash 函式未提供
var str = '<object type="application/x-shockwave-flash"></object>';
return $(str).appendTo($('body'));
};
return false;
}
var getFormUpladObj = function () {
var str = '<input name="file" type = "file" class = "ui-file" / > '; // 表單上傳
return $(str).appendTo($('body'));
}
var iteratorUploadObj = function () {
for (var i = 0, fn; fn = arguments[i++];) {
var uploadObj = fn();
if (uploadObj !== false) {
return uploadObj;
}
};
}
var uploadObj = iteratorUploadObj(getActiveUploadObj, getFlashUploadObj, getFormUpladObj);
複製程式碼
es6
基於類實現
class Iterator {
constructor(conatiner) {
this.list = conatiner.list
this.index = 0
}
next() {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext() {
if (this.index >= this.list.length) {
return false
}
return true
}
}
class Container {
constructor(list) {
this.list = list
}
getIterator() {
return new Iterator(this)
}
}
// 測試程式碼
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
複製程式碼
es6中的Iterator
我們都知道Array、Map、Set、類物件(如arguments NodeList等)都有一個Symbol.iterator迭代方法,可以通過以下方式取得
var a = [1,2,3]
console.log(a[Symbol.iterator])
複製程式碼
另外generator也會返回迭代器
function* gen() {
yield 1
yield '1'
}
var a = gen()
a.next()
複製程式碼