YBFACC / blog

仅记录个人学习使用
3 stars 0 forks source link

Iterator #22

Open YBFACC opened 4 years ago

YBFACC commented 4 years ago

遍历器

遍历器(Iterator)就是一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作

平常使用的(...)扩展运算符for...of... 都在隐式的调用的 Iterator

原生具备 Iterator 接口。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函数的 arguments 对象
  • NodeList 对象

已经存在的Symbol.iterator具有以下特性

属性 True/False
writable false
enumerable false
configurable false

自己实现一个Iterator

let obj = {
  data: [1, 2, 3],
  [Symbol.iterator]: function () {
    let context = this
    let index = 0
    let length = this.data.length
    return {
      next: function () {
        if (index < length) {
          return { value: context.data[index++], done: false }
        } else {
          return { value: void 0, done: true }
        }
      }
    }
  }
}

for (const item of obj) {
  console.log(item)//1 2 3
}

原本你不可以直接在对象上使用for...of... 。现在我们在对象上定义[Symbol.iterator],使得for...of... 可以找到 iterator属性进行遍历。

for...of...

for...of... 可以约等于以下代码:

let iterator = obj[Symbol.iterator]()
let result = iterator.next()
while (!result.done) {
  console.log(result.value)
  //....
  result = iterator.next()
}

覆盖原有的Symbol.iterator

我们不可以更改对象原型链上的 Symbol.iterator ,只能在当前对象上定义新的 Symbol.iterator

例如以下例子:

let a = [1, 2, 3]

a[Symbol.iterator] = function () {
  let conext = this
  let index = 0
  return {
    next() {
      if (Number.isInteger(conext[index])) {
        return { value: conext[index++] + 1, done: false }
      } else {
        return { value: void 0, done: true }
      }
    }
  }
}
for (const item of a) {
  console.log(item)//2 3 4
}

扩展运算符

扩展运算符也是调用 Symbol.iterator

let a = [1, 2, 3]

a[Symbol.iterator] = function () {
  let conext = this
  let index = 0
  return {
    next() {
      if (Number.isInteger(conext[index])) {
        return { value: conext[index++] + 1, done: false }
      } else {
        return { value: void 0, done: true }
      }
    }
  }
}
let b = [...a] //[2,3,4]

return()和 throw()

return() 用于循环提前结束的情况。

return方法必须返回一个对象,这是 Generator 规格决定的。

let a = [1, 2, 3]

a[Symbol.iterator] = function () {
  let conext = this
  let index = 0
  return {
    next() {
      if (Number.isInteger(conext[index])) {
        return { value: conext[index++] + 1, done: false }
      } else {
        return { value: void 0, done: true }
      }
    },
    return() {
      console.log('提前结束')
      return { value: void 0, done: true }
    }
  }
}
for (const item of a) {
  console.log(item)
  break
}
for (const item of a) {
  console.log(item)
  throw new Error()
}

throw() 用于配合 Generator 函数使用。

var g = function* () {
  try {
    yield 123
  } catch (e) {
    console.log('内部捕获', e)
  }
}

var i = g()
console.log(i.next())

try {
  i.throw('a')
  i.throw('b')
} catch (e) {
  console.log('外部捕获', e)
}

关于String的补充

let a = 'abc'
for (const item of a) {
  console.log(item)
}
//a b c

这里的string是字面量,本身没有Symbol.iterator,这里应该是隐式字面量转化为String包装类,包装类上就有Symbol.iterator,符合预期。

let a = 'abc'
let A = new String(a)
for (const item of A) {
  console.log(item)
}
//a b c

参考

引用和部分代码都来自:ECMAScript 6 入门