关于javascript:javascript高级程序设计学习笔记-72迭代器模式

27次阅读

共计 6229 个字符,预计需要花费 16 分钟才能阅读完成。

关注前端小讴,浏览更多原创技术文章

迭代器模式

  • ES6 新增了 2 个高级个性:迭代器 生成器
迭代办法 优 / 毛病
for ① 需晓得如何应用数据结构 ② 遍历程序不是固有的
forEach() ① 无奈标识迭代终止 ② 只实用数组 ③ 回调构造较蠢笨
Iterator接口 无需理解可迭代对象的构造,只需晓得如何获得间断的值

相干代码 →

可迭代协定

  • 实现 Iterable 接口要求同时具备:① 反对迭代的自我辨认 ② 创立实现 Iterator 接口的对象
  • 必须裸露一个属性作为 默认迭代器 ,属性应用Symbol.iterator 作为键
  • 默认迭代属性必须援用一个 迭代器工厂函数 ,调用工厂函数返回一个 新迭代器
  • 实现了 Iterable 接口的内置类型:字符串、数组、映射、汇合、arguments 对象、NodeList 等 DOM 汇合类型
// 未实现迭代器工厂函数
let num = 1
let obj = {}
console.log(num[Symbol.iterator]) // undefined
console.log(obj[Symbol.iterator]) // undefined

// 实现了迭代器工厂函数
let str = 'abc'
let arr = ['a', 'b', 'c']
let map = new Map().set('a', 1).set('b', 2)
let set = new Set().add('a').add('b')
console.log(str[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] }
console.log(arr[Symbol.iterator]) // ƒ values() { [native code] }
console.log(map[Symbol.iterator]) // ƒ entries() { [native code] }
console.log(set[Symbol.iterator]) // ƒ values() { [native code] }

// 调用迭代器工厂函数,生成新的迭代器
console.log(str[Symbol.iterator]()) // StringIterator {}
console.log(arr[Symbol.iterator]()) // ArrayIterator {}
console.log(map[Symbol.iterator]()) // MapIterator {"a" => 1, "b" => 2}
console.log(set[Symbol.iterator]()) // StringIterator {"a", "b"}
  • 不须要显示调用迭代器工厂函数,实现可迭代协定的 所有类型 主动兼容承受 可迭代对象的任何语言个性
  • 承受可迭代对象的原生语言个性 包含:

    • for-of循环
    • 数组解构
    • 扩大操作符
    • Array.from()
    • 创立汇合
    • 创立映射
    • Promise.all()接管由期约组成的可迭代对象
    • Promise.race()接管由期约组成的可迭代对象
    • yield*操作符,在生成器中应用
for (let el of arr) {console.log(el) // for-of 循环
  /* 
    'a'
    'b'
    'c'
   */
}

let [a, b, c] = arr // 数组解构
console.log(a, b, c) // 'a' 'b' 'c'

let arr2 = [...arr] // 扩大操作符
console.log(arr2) // ['a', 'b', 'c']

let arr3 = Array.from(arr) // Array.from()
console.log(arr3) // ['a', 'b', 'c']

let set2 = new Set(arr)
console.log(set2) // Set(3) {'a', 'b', 'c'}

let pairs = arr.map((x, i) => [x, i])
console.log(pairs) // [[ 'a', 0], ['b', 1], ['c', 2] ]
let map2 = new Map(pairs)
console.log(map2) // Map(3) {'a' => 0, 'b' => 1, 'c' => 2}
  • 如果对象原型链上的 父类实现了 Iterabe 接口,该对象也实现了这个接口
class FooArray extends Array {}
let fooArr = new FooArray('foo', 'bar', 'baz')

for (let el of fooArr) {console.log(el)
  /* 
    foo
    bar
    baz
   */
}

迭代器协定

  • 迭代器 API 应用 next() 办法在可迭代对象中遍历数据,每次胜利调用 next() 都返回一个 IteratorResult 对象:

    • IteratorResult蕴含 2 个属性 donevalue
    • done 是一个布尔值,false 为未耗尽,true 为耗尽
    • value蕴含可迭代对象的下一个值(若 done 为 true 则 value 为 undefined)
    • 迭代器达到 done:true 状态,后续再调用 next() 始终返回{done:true,value:undefined}
let arr4 = ['foo', 'bar'] // 可迭代对象
console.log(arr4[Symbol.iterator]) // ƒ values() { [native code] },迭代器工厂函数

let iter = arr4[Symbol.iterator]() // 迭代器
console.log(iter) // ArrayIterator {}

console.log(iter.next()) // {value: 'foo', done: false},执行迭代
console.log(iter.next()) // {value: 'foo', done: false},执行迭代
console.log(iter.next()) // {value: undefined, done: true},执行迭代
console.log(iter.next()) // {value: undefined, done: true},执行迭代
  • 同一个可迭代对象的 不同迭代器 实例之间 没有分割 ,每个迭代器都 独立 地遍历可迭代对象
let iter2 = arr4[Symbol.iterator]() // 迭代器 iter2,迭代可迭代对象 arr4
let iter3 = arr4[Symbol.iterator]() // 迭代器 iter2,迭代可迭代对象 arr4

console.log(iter2.next()) // {value: 'foo', done: false}
console.log(iter3.next()) // {value: 'foo', done: false}
console.log(iter3.next()) // {value: 'bar', done: false}
console.log(iter2.next()) // {value: 'bar', done: false}
  • 可迭代对象在迭代期间被批改,迭代器反映相应变动;迭代器保护的是 指向可迭代对象的援用 ,会 阻止垃圾回收程序 回收可迭代对象
let arr5 = ['foo', 'baz']
let iter4 = arr5[Symbol.iterator]()
console.log(iter4.next()) // {value: 'foo', done: false}

arr5.splice(1, 0, 'bar') // 迭代期间,可迭代对象被批改
console.log(iter4.next()) // {value: 'bar', done: false}
console.log(iter4.next()) // {value: 'baz', done: false}
console.log(iter4.next()) // {value: undefined, done: true}

自定义迭代器

  • 任何 实现了 Iterable 接口(具备 Symbol.iterator 属性)的对象都能够作为迭代器应用
class Counter {
  // 构造函数
  constructor(limit) {
    this.count = 1
    this.limit = limit
  }
  // Iterable 接口,实现自定义迭代
  [Symbol.iterator]() {return this}
  // 原型上的迭代办法
  next() {if (this.count <= this.limit) {return { value: this.count++, done: false}
    } else {return { value: undefined, done: true}
    }
  }
}

let counter = new Counter(5)
console.log(counter) // Counter {count: 1, limit: 5},构造函数
console.log(counter[Symbol.iterator]) // ƒ [Symbol.iterator]() { return this},迭代器工厂函数

for (let i of counter) {console.log(i)
  /* 实现了自定义迭代器:1
    2
    3
    4
    5 
  */
}

for (let i of counter) {console.log(i)
  /* 只能迭代 1 次
    nothing logged
  */
}
  • 将计数器变量放到闭包里,实现同一个 可迭代对象 可能 创立多个迭代器
class Counter2 {constructor(limit) {this.limit = limit}
  [Symbol.iterator]() {
    let count = 1 // 计数器变量放到闭包中
    let limit = this.limit
    return {next() {if (count <= limit) {return { value: count++, done: false}
        } else {return { value: undefined, done: true}
        }
      },
    }
  }
}
let counter2 = new Counter2(3)

for (let i of counter2) {console.log(i)
  /* 
    1
    2
    3
  */
}

for (let i of counter2) {console.log(i)
  /* 同一个可迭代对象,可能创立多个迭代器:1
    2
    3
  */
}
  • 任何迭代器,调用 Symbol.iterator 属性援用的工厂函数后,返回与原迭代器雷同的迭代器
let arr6 = ['foo', 'bar', 'baz']
let iter5 = arr6[Symbol.iterator]()
console.log(iter5) // ArrayIterator {}

let iter6 = iter5[Symbol.iterator]() // 迭代器再次调用工厂函数,生成新的迭代器
console.log(iter6) // // ArrayIterator {}
console.log(iter5 === iter6) // true

let iter7 = iter6[Symbol.iterator]() // 迭代器再次调用工厂函数,生成新的迭代器
console.log(iter7) // // ArrayIterator {}
console.log(iter6 === iter7) // true

for (let i of iter7) {console.log(i)
  /* 
    'foo'
    'bar'
    'baz'
  */
}

提前终止迭代器

  • 可选的 return() 办法可在迭代器未耗尽时,指定迭代器提前敞开时执行的逻辑,迭代器 提前敞开的可能状况 包含:

    • for-of循环通过 breakcontinuereturnthrow提前退出
    • 解构操作 未生产所有值
class Counter3 {constructor(limit) {this.limit = limit}
  [Symbol.iterator]() {
    let count = 1 // 计数器变量放到闭包中
    let limit = this.limit
    return {next() {if (count <= limit) {return { value: count++, done: false}
        } else {return { value: undefined, done: true}
        }
      },
      // 提前终止迭代器的办法
      return() {console.log('Exiting early')
        return {done: true}
      },
    }
  }
}

let counter3 = new Counter3(5)

for (let i of counter3) {if (i > 2) {break // 提前终止迭代器,调用迭代器的 return()办法
  }
  console.log(i)
  /* 
    1
    2
    'Exiting early'
  */
}

try {for (let i of counter3) {if (i > 2) {throw 'err' // 提前终止迭代器,调用迭代器的 return()办法
    }
    console.log(i)
    /* 
    1
    2
    'Exiting early'
  */
  }
} catch (e) {}

let [a2, b2, c2, d2, e2, f2] = counter3 // 解构操作,生产所有值
console.log(a2, b2, c2, d2, e2, f2) // 1 2 3 4 5 undefined
let [a3, b3, c3] = counter3 // 'Exiting early',解构操作,未生产所有值
  • 若迭代器未敞开,提前终止后能够从上次来到的中央持续迭代,数组的迭代器就是不能敞开的
let arr7 = [1, 2, 3, 4, 5]
let iter8 = arr7[Symbol.iterator]()

for (let i of iter8) {console.log(i)
  if (i > 2) {break // 提前退出迭代器,但不敞开}
  /* 
    1
    2
    3
 */
}

for (let i of iter8) {console.log(i)
  /* 持续迭代
    4
    5
 */
}
  • 迭代器实例的 return 属性 是否为函数对象 ,决定迭代器 是否可敞开

    • 给不可敞开的迭代器减少 return() 办法 不能 让该迭代器变得可敞开,但提前终止会调用新增的 return() 办法
let arr8 = [1, 2, 3, 4, 5]
let iter9 = arr8[Symbol.iterator]()

console.log(iter9.return) // undefined,迭代器不可敞开

iter9.return = function () {
  // 追加 return 办法,但无奈让迭代器变得可敞开
  console.log('Exiting early')
  return {done: true}
}

for (let i of iter9) {console.log(i)
  if (i > 2) {break // 提前退出迭代器}
  /* 
    1
    2
    3
    'Exiting early',提前终止迭代器仍会调用 return()办法
 */
}

for (let i of iter9) {console.log(i)
  /* 持续迭代
    4
    5
 */
}

总结 & 问点

  • 实现迭代有哪些形式?其优、毛病别离是什么?默认迭代器必须应用什么属性作为键?
  • 如何检测数据类型是否实现了迭代器工厂函数?调用这个工厂函数会生成什么?
  • 迭代器 API 调用哪个办法遍历数据?其返回值是什么?同一个可迭代对象的不同迭代器实例之间有分割么?
  • 迭代器真正保护的是什么?其对垃圾回收程序有什么影响?可迭代对象在迭代期间被批改,迭代器受影响么?
  • 写一段代码创立一个自定义迭代器,实现“同一个可迭代对象可能创立多个迭代器”
  • 迭代器调用 Symbol.iterator 属性援用的工厂函数会生成什么?
  • 如何指定迭代器提前敞开时执行的逻辑?哪些状况下迭代器会提前终止?
  • 提前终止的迭代器若未敞开,是否可持续迭代?如何判断迭代器是否可敞开?
  • 不可敞开的迭代器是否变成可敞开的?请用代码证实

正文完
 0