28
loading...
This website collects cookies to deliver better user experience
Object.entries
or similar) it will allow us to focus attention on the subject.iteratify
function takes an object as a parameter and returns an iterable copy of it.const object = {
foo: true,
bar: 'hello',
baz: 42,
}
const itObject = iteratify(object)
for (let(key, val) of itObject) {
console.log(key, val)
}
Symbol.iterator
label. The current state:typeof object[Symbol.iterator] === 'function' // returns false
const object = {
...,
[Symbol.iterator]() {
...
}
}
[Symbol.iterator]
method would be enumerable. It's not the case. It is easily solved:function iteratify(obj) {
// create a copy of object (supposing it is flat for simplicity)
const copy = Object.assign({}, obj)
Object.defineProperty(copy, Symbol.iterator, {
enumerable: false,
writable: true,
configurable: true,
value: // next step
})
return copy
}
next
method. At each execution of the latter, an IteratorResult is obtained, that is an object that necessarily contains two specific properties:function iteratify(obj) {
const copy = Object.assign({}, obj)
Object.defineProperty(copy, Symbol.iterator, {
enumerable: false,
writable: true,
configurable: true,
value: iterator,
})
return copy
function iterator() {
const entries = Object.entries(copy)
let i = 0
return {
next() {
if (i < entries.length) {
return { value: entries[i++], done: false }
}
return { done: true } // implicit: value: undefined
},
}
}
}
next
gets an IteratorResult whose value is the entry to the index i
- also i++
happens, so the next time next
is called it will return the next entry.function iterator()
comes after the return
. Isn't that dead code?
No. function hoisting
next
? And when in the world?for...of
loop, the JavaScript internal calls next repeatedly until an IteratorResult is returned whose done
is true
. However, you can "manually" call next
as follows:const itObject = iteratify({
foo: true,
bar: 'hello',
baz: 42,
})
const it = itObject[Symbol.iterator]()
it.next() // { value: [ 'foo', true ], done: false }
it.next() // { value: [ 'bar', 'hello' ], done: false }
it.next() // { value: [ 'baz', 42 ], done: false }
it.next() // { value: undefined, done: true }
for...of
:const itObject = iteratify({
foo: true,
bar: 'hello',
baz: 42,
})
typeof itObject[Symbol.iterator] === 'function' // returns true, thus is iterable
for (let entry of itObject) {
console.log(entry) // each returns relative entry
// [ 'foo', true ]
// [ 'bar', 'string' ]
// [ 'baz', 42 ]
}
for...of
call the method under the Symbol.iterator
labelnext
method can access and interact with the variables declared in the iterator
(Closure) - you can do very cool things, not just keep track of an i
counter ;)