iterator in object

62 阅读1分钟

🌧 question

As we know, type Array has iterator but object doesn't.

const [a, b] = [1, 2] // this is gonna work
const [a_, b_] = {
  a_: '1',
  b_: '2'
}  // end up with a typeError: 'object is not iterable'

Take this into consideration, how to make objects iterable?

⭐ generator

Once I saw a description in introduction about Generator function.

function* gen(){}
const g = gen()
g === g[Symbol.iterator]()

So, if we wanna make a object iterable, generator is gonna to be a good choice.

const obj = {
  a_: 1,
  b_: 2,
  [Symbol.iterator](){
    let index = 0
    let value_array = Object.values(this)
    return {
      next() {
        if (index < value_array.length) {
          return {
            done: false,
            value: value_array[index]
          }
        }
        return { done: true, value: undefined }
      }
    }
  }
}

const [a, b] = obj
console.log(a, b) // well this is valid

Till here we solve the problem

By the way, if a object can achieve [Symbol.iterator] and it is able to be circulated by for...of

🐸 what's more

Here is a simple implement of Generator function below.

function* gen() {
  let x = 1
  let y = yield x + 1
  let z = yield y * 4
  let s = yield z % 3
  return x + y + z + s
}

const g = gen()

g.next()
// guess what will `g.next` return 

If you guys have learned or met some situation like this, it is easy to understand that only the first yield will return { value: 2, done: false } and others will get a result either { value: NaN, done: false } or { value: undefined, done: true }

Ok, if you know why, you can just skip to the next chapter; and if you still wonder why, I will show you my opinions.

Well, just take yield as a temporary return and its priority is higher than = .When you get this, the code has changed

function* gen() {
  let x = 1
  yield x + 1
  let y                  // undefined
  yield y * 4            // NaN
  let z                  // undefined
  yield z % 3            // NaN
  let s                  // undefined
  return x + y + z + s   // NaN
}

const g = gen()

g.next()

And also we have a solution for this problem -- just add a parameter in function next

g.next(2)

this gonna change the code running process

function* gen() {
  let x = 1
  yield x + 1
  let y = 2              // 2 is come from `next`            
  yield y * 4           
  let z                  // undefined
  yield z % 3            // NaN
  let s                  // undefined
  return x + y + z + s   // NaN
}

const g = gen()

g.next()

What you should notice is that: by the parameter in next is representing which is last yield expression returns, so the first time when you call next the parameter is invalid.