javascript 設計模式之單例模式javascript
javascript 設計模式之迭代器模式markdown
迭代器模式提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。
特色:
let arr = [1, 2, 3]
arr.forEach((item) => {
console.info(item) // 1 2 3
})
複製代碼
在數組上遍歷是沒有問題的,但將它做爲在 NodeList 就會發現報錯
<div id="div1">
<a href="#">a1</a>
<a href="#">a2</a>
<a href="#">a3</a>
<a href="#">a4</a>
<a href="#">a5</a>
</div>
複製代碼
遍歷 id 爲 div1 下的全部 a 標籤
let nodeList = document.getElementById('div1').getElementsByTagName('a')
nodeList.forEach((item) => {
console.info(item)
})
複製代碼
運行會報Uncaught TypeError: nodeList.forEach is not a function
錯誤。 這是因爲 nodeList 只是類數組,並無實現 forEach 方法。
使用 for 循環遍歷
let nodeList = document.getElementById('div1').getElementsByTagName('a')
let i, length = nodeList.length
for (i = 0; i < length; i++) {
console.info(nodeList[i].innerHTML)
}
複製代碼
能夠正常打印
上述的數組跟 nodeList 採用不一樣的遍歷方式,爲了處理這種狀況,經過藉助 jQuery 的 each 方法,咱們能夠用同一套遍歷規則遍歷不一樣的集合對象:
let arr = [1, 2, 3]
let nodeList = document.getElementById('div1').getElementsByTagName('a')
let $a = $('a')
$.each(arr, function (index, item) {
console.log(`數組的第${index}個元素是${item}`)
})
$.each(nodeList, function (index, aNode) {
console.log(`DOM類數組的第${index}個元素是${aNode.innerText}`)
})
$.each($a, function (index, aNode) {
console.log(`jQuery集合的第${index}個元素是${aNode.innerText}`)
})
複製代碼
能夠正常輸出內容
// 主體
class Container {
constructor(list) {
this.list = list
}
// 生成遍歷器
getIterator() {
return new Iterator(this)
}
}
// 迭代器
class Iterator {
constructor(container) {
this.list = container.list
this.index = 0
}
next() {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext() {
if (this.index < this.list.length) {
return true
}
return false
}
}
const arr = [1, 2, 3, 4, 5, 6] // 是有序的集合,好比數組,nodeList
const container = new Container(arr)
const iterator = container.getIterator()
while (iterator.hasNext()) {
console.info(iterator.next())
}
複製代碼
ES6 新增了 Set 和 Map ,致使目前有序集合有 Array Map Set String TypedArray arguments NodeList 這麼多種,須要一個統一的遍歷接口訪問這些有序集合中的數據,因此 ES6 引入了 Iterator。
ES6 默認的 Iterator 接口部署在數據結構的 [Symbol.iterator] 屬性上,該屬性自己是一個函數,執行這個函數會返回個遍歷器對象。
遍歷器對象的特徵:
在數組上測試下
let arr = [1, 2, 3]
let iterator = arr[Symbol.iterator]();
console.info(iterator.next()) // { value: '1', done: false }
console.info(iterator.next()) // { value: '2', done: false }
console.info(iterator.next()) // { value: '3', done: false }
console.info(iterator.next()) // { value: undefined, done: true }
複製代碼
代碼優化並封裝個 each 方法:
function each(data) {
const iterator = data[Symbol.iterator]() // 生成迭代器
let item = { done: false }
while (!item.done) {
item = iterator.next()
if (!item.done) {
console.info(item)
console.info(item.value)
}
}
}
let arr = [1, 2, 3]
let nodeList = document.getElementsByTagName('P')
let map = new Map()
map.set('name', 'zhangSan')
map.set('age', 12)
each(arr)
each(nodeList)
each(map)
複製代碼
只要具備 [Symbol.iterator] 屬性的集合,就可使用 each 方法進行遍歷
Symbol.iterator並非人人都知道的,也不是每一個人都須要封裝一個 each 方法,所以,ES6提供了一個新的方法 for...of 來遍歷:
// for...of 自動遍歷擁有 Iterator 接口的數據結構
let arr = [1, 2, 3];
for (let item of arr) {
console.log(item);
}
// 輸出:1 2 3
複製代碼
說明 for...of 只是個語法糖,運行原理:
等價於下面:
// 經過調用iterator,拿到迭代器對象
const iterator = arr[Symbol.iterator]()
// 初始化一個迭代結果
let now = { done: false }
// 循環往外迭代成員
while(!now.done) {
now = iterator.next()
if(!now.done) {
console.log(`如今遍歷到了${now.value}`)
}
}
複製代碼
Iterator 的價值不限於上述幾個類型的遍歷,還有 Generator 函數的使用。Generator 返回的數據符合 Iterator 接口遍歷的要求,因此 Generator 函數也可使用 Iterator 語法。
function* helloWorldGenerator() {
yield 'hello'
yield 'world'
return 'ending'
}
// var hw = helloWorldGenerator()
// console.info(hw)
// hw.next() //{value: "hello", done: false}
// hw.next() //{value: "world", done: false}
// hw.next() //{value: "ending", done: true}
// hw.next() //{value: undefined, done: true}
// console.info(hw[Symbol.iterator])
for (var item of helloWorldGenerator()) {
console.info(item)
// hello
// world
}
複製代碼
根據這個特色能夠用 for...of 遍歷普通對象
for...of 遍歷普通對象的解決方法:
let person = {
name: 'Ken',
sex: 'Male'
}
// Generator 包裝對象
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
for (let [key, value] of entries(person)) {
console.log(`${key}: ${value}`);
}
// 輸出:
// name: Ken
// sex: Male
複製代碼
內部已經定義好了迭代規則,它徹底接手整個迭代過程,外部只須要一次初始調用
實現:
function each(arr, fn) {
for (let i = 0; i < arr.length; i++) {
fn(i, arr[i])
}
}
each([1, 2, 3], function(i, n) {
console.log(i) // 0 1 2
console.log(n) // 1 2 3
})
複製代碼
優缺點:
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 不相等' );
複製代碼
jQuery 的 $.each 以及 for...of 也是內部迭代器
外部迭代器必須顯式地請求迭代下一個元素
上面的"自定義個迭代器" 以及 Generator yield 就是外部迭代器。 解決下兩個數組比較問題:
const arr1 = [1, 2, 3], arr2 = [1, 2, 3]
const container1 = new Container(arr1), container2 = new Container(arr2)
const iterator1 = container1.getIterator(), iterator2 = container2.getIterator()
function compare(iterator1, iterator2) {
while (iterator1.hasNext() || iterator2.hasNext()) {
if (iterator1.next() !== iterator2.next()) {
return false
}
}
return true
}
console.info(compare(iterator1, iterator2))
複製代碼
優缺點:
你的點贊是對我最大的確定,若是以爲有幫助,請留下你的讚揚,謝謝!!!