Article series

Javascript design pattern singleton pattern

The adaptor pattern of javascript design patterns

The Decorator pattern of javascript design patterns

Proxy mode of javascript design pattern

Comparison of javascript adaptation, proxy, and decorator patterns

State mode of javascript design mode

The iterator pattern of javascript design patterns

Policy mode of javascript design pattern

The Observer pattern of javascript design patterns

Publish subscriber mode of javascript design pattern

concept

The iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing the object’s internal representation.

Features:

  1. Provide a uniform interface for traversing “collections” of different data structures;
  2. The ability to traverse and access items in the collection data, regardless of the item’s data structure

From the Array. The prototype. The forEach

let arr = [1.2.3]
arr.forEach((item) = > {
	console.info(item) / / 1 2 3
})
Copy the code

Iterating over the array is fine, but treating it as if it were in NodeList will result in an error

<div id="div1">
    <a href="#">a1</a>
    <a href="#">a2</a>
    <a href="#">a3</a>
    <a href="#">a4</a>
    <a href="#">a5</a>
</div>
Copy the code

Iterate over all a tags with ID div1

let nodeList = document.getElementById('div1').getElementsByTagName('a')
nodeList.forEach((item) = > {
	console.info(item)
})
Copy the code

Uncaught TypeError: nodeList. ForEach is not a function error is reported. This is because nodeList is just an array of classes and does not implement the forEach method.

Use a for loop to iterate

let nodeList = document.getElementById('div1').getElementsByTagName('a')
let i, length = nodeList.length
for (i = 0; i < length; i++) {
	console.info(nodeList[i].innerHTML)
}
Copy the code

It can print normally

JQuery’s each method

To handle this, we can use the same set of traversal rules to traverse different collection objects using jQuery’s each method:

let arr = [1.2.3]
let nodeList = document.getElementById('div1').getElementsByTagName('a')
let $a = $('a')
$.each(arr, function (index, item) {
    console.log(The 1st of the array${index}An element is${item}`)
})
$.each(nodeList, function (index, aNode) {
    console.log(DOM class array${index}An element is${aNode.innerText}`)
})
$.each($a, function (index, aNode) {
    console.log('the first of the jQuery collection${index}An element is${aNode.innerText}`)})Copy the code

Normal output content

Custom iterators

/ / the main body
class Container {
	constructor(list) {
		this.list = list
	}
	// Generate a traverser
	getIterator() {
		return new Iterator(this)}}/ / the iterator
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] // Is an ordered collection, such as an array,nodeList
const container = new Container(arr)
const iterator = container.getIterator()
while (iterator.hasNext()) {
	console.info(iterator.next())
}

Copy the code

The iterator after ES6

ES6 adds a Set and a Map, which leads to an Array, a Map, a Set, a String, TypedArray arguments, and a NodeList. So ES6 introduced Iterator.

The default Iterator interface in ES6 is deployed on the [Symbol. Iterator] property of the data structure, which is itself a function whose execution returns a Iterator object.

The characteristics of the traverser object:

  • Have the next method
  • Next () returns an object containing the value and done properties
    • Value: The value of the current data structure member
    • Done: A Boolean value indicating whether the traversal is complete

Test it on the array

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 }
Copy the code

Code optimizes and encapsulates an each method:

function each(data) {
	const iterator = data[Symbol.iterator]() // Generate iterators
	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)
Copy the code

Any collection that has the [Symbol. Iterator] attribute can be traversed using the each method

for of

Symbol. Iterator is not universally known and not everyone needs to encapsulate an each method. Therefore, ES6 provides a new method for… Of to traverse:

// for... Of automatically traverses data structures that have an Iterator interface
let arr = [1.2.3];
for (let item of arr) {
  console.log(item);
}

// Output: 1, 2, 3
Copy the code

Instructions for… Of is just a grammatical sugar. How it works:

  1. First, we call the symobo. iterator method to get the iterator object.
  2. On each loop, the next() method of the traverser object is called, resulting in {value:… , done: … } object

Is equivalent to the following:

// Get the iterator object by calling iterator
const iterator = arr[Symbol.iterator]()

// Initialize the result of an iteration
let now = { done: false }

// The loop iterates through members
while(! now.done) { now = iterator.next()if(! now.done) {console.log('Now the traversal is done${now.value}`)}}Copy the code

ES6 Iterator and the Generator

The value of Iterator is not limited to the above types of traversal, but also to the use of Generator functions. The data returned by the Generator meets the Iterator interface traversal requirements, so Generator functions can also use the Iterator syntax.

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
}

Copy the code

We can use for… Of traversing plain objects for… Of traversing a common object

  1. Use objet.keys to generate an array of object key names and then iterate over the array.
  2. The Generator function rewraps the object
let person = {
  name: 'Ken'.sex: 'Male'
}

// The Generator wraps the object
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}`);
}

/ / output:
// name: Ken 
// sex: Male
Copy the code

Iterator classification

Internal iterator

The iteration rules are defined internally, which takes over the entire iteration process completely, and the outside only needs one initial call

Implementation:

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
})
Copy the code

The advantages and disadvantages:

  • Advantages: Internal iterators are convenient to call, the outside world does not have to worry about the implementation inside the iterator, and the interaction with the iterator is only an initial call
  • Disadvantages: Because the internal iterator iteration rules are defined in advance, the each function cannot iterate over two arrays at the same time. For example, to compare whether two arrays are equal, you can only use its callback function as follows:
var compare = function( ary1, ary2 ){
  if( ary1.length ! == ary2.length ){throw new Error ( 'ARY1 and ARY2 are not equal' );
  }
  each( ary1, function( i, n ){
    if( n ! == ary2[ i ] ){throw new Error ( 'ARY1 and ARY2 are not equal'); }}); alert ('ARY1 and ARY2 are equal' );
};
compare( [ 1.2.3 ], [ 1.2.4]);// Throw new Error ('ary1 and ary2 are not equal ');
Copy the code

$. Each and for… Of is also an internal iterator

External iterator

An external iterator must explicitly request to iterate over the next element

The above “custom iterators” and Generator yield are external iterators. Solve the following two array comparison problems:

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))
Copy the code

The advantages and disadvantages:

  • Advantages: External iterators transfer the power of traversal to the outside, so you have more freedom when calling,
  • Disadvantages: The invocation mode is more complex

Refer to the link

JavaScript design patterns and development practices

conclusion

Your “like” is my biggest affirmation, if you think it is helpful, please leave your praise, thank you!!