ES6如何让不支持遍历的结构可遍历?数据结构
规定函数
let authors = { allAuthors: { fiction: [ 'Agatha Christie', 'J. K. Rowling', 'Dr. Seuss' ], scienceFiction: [ 'Neal Stephenson', 'Arthur Clarke', 'Isaac Asimov', 'Robert Heinlein' ], fantasy: [ 'J. R. R. Tolkien', 'J. K. Rowling', 'Terry Pratchett' ] } }
ES5的作法post
对allAuthors
进行遍历,看看取值状况学习
for(let [k,v] of Object.entries(authors.allAuthors)){ console.log(k,v) } //fiction (3) ["Agatha Christie", "J. K. Rowling", "Dr. Seuss"] //scienceFiction (4) ["Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein"] // fantasy (3) ["J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"] // 因此正确的写法是: let r = [] for(let [k,v] of Object.entries(authors.allAuthors)){ r = r.concat(v) } console.log(r) //["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]
每次都须要遍历,并且还要对obj不支持遍历的对象进行改造。
并且还须要知道authors的内部结构。this
咱们的目标是:spa
let r = [] for(let v of authors){ console.log(v) r.push(v) }
因此ES6的写法prototype
//固定写法 authors[Symbol.iterator] = function () { // 输入 this,对象自己 let allAuthors = this.allAuthors let keys = Reflect.ownKeys(allAuthors) console.log(keys) // ["fiction", "scienceFiction", "fantasy"] let values = [] // 是key的值 return { // 必须返回一个方法 next () { console.log(values) //一开始values.length是0,若是是0就进入循环过程 if (!values.length) { if (keys.length) { values = allAuthors[keys[0]] keys.shift() //永远取第一个元素,用完以后进行弹出 } } // 必须返回两个值 return { done: !values.length, value: values.shift() } } } } let r = [] for(let v of authors){ r.push(v) } console.log(r) //["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]
Object.entries
进行转化let obj = { 1: "hello", "a":"hi" }; for(let i of obj){console.log(i,obj[i]);} //报错:Uncaught TypeError: obj is not iterable let obj1 = Object.entries(obj); console.log(obj1) //[Array(2), Array(2)] for(let [k,v] of obj1){ console.log(k,v) } //1 hello //a hi
- 可迭代协议容许 JavaScript 对象去定义或定制它们的迭代行为, 例如(定义)在一个 for…of 结构中什么值能够被循环(获得)。一些内置类型都是内置的可迭代类型而且有默认的迭代行为, 好比 Array or Map, 另外一些类型则不是 (好比Object) 。
- 为了变成可迭代对象, 一个对象必须实现 @@iterator 方法, 意思是这个对象(或者它原型链 prototype chain 上的某个对象)必须有一个名字是 Symbol.iterator 的属性
- 这里参考 遍历 —— for-of(ES6新增)
next
,next
返回一个对象包含done
和value
属性PS: 这个结构是否是和Generator
特别像?若是
next
函数返回一个非对象值(好比false
和undefined
) 会展现一个TypeError (“iterator.next() returned a non-object value”)
的错误code
//固定写法 authors[Symbol.iterator] = function () { // 输入 this,对象自己 // 输出 返回值(格式要求) return { // 必须返回一个next方法 next () { // 必须返回两个值 return { done: false, // boolean false-遍历没有结束 true-遍历结束 value: 1 // 当前遍历的项目的值 } } } }
想了解Generator
,参考文章 ES6(十四)—— Generator对象
//可迭代协议 加*就是Generator了 authors[Symbol.iterator] = function * () { // 输入 this,对象自己 let allAuthors = this.allAuthors let keys = Reflect.ownKeys(allAuthors) console.log(keys) // ["fiction", "scienceFiction", "fantasy"] let values = [] // 是key的值 // 无线循环,若是退出以后,会自动停止退出的 while(1){ if(!values.length){ if(keys.length){ values = allAuthors[keys[0]] keys.shift() yield values.shift() }else{ // 退出循环 return false } }else{ yield values.shift() } } }
// each方法,是todos内部暴露的方法 // 更好的是把todos直接变成一个可迭代的对象 const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语文', '数学', '外语'], work: ['喝茶'], each: function (callback) { const all = [].concat(this.life, this.learn, this.work) for( const item of all) { callback(item) } }, [Symbol.iterator]: function() { const all = [...this.life, ...this.learn, ...this.work] let index = 0 return { next: function () { return { value: all[index], done: index++ >= all.length } } } } } todos.each(function(item){ console.log(item) }) // 吃饭 // 睡觉 // 打豆豆 // 语文 // 数学 // 外语 // 喝茶 for(const item of todos){ console.log("for-of: " +item) }
使用Generator
函数实现Iterator
方法,对上面的案例进行改进blog
const todos = { life: ['吃饭', '睡觉', '打豆豆'], learn: ['语文', '数学', '外语'], work: ['喝茶'], each: function (callback) { const all = [].concat(this.life, this.learn, this.work) for( const item of all) { callback(item) } }, [Symbol.iterator]: function * () { const all = [...this.life, ...this.learn, ...this.work] for(const item of all) { yield item } } } for(const item of todos){ console.log(item) } // 吃饭 // 睡觉 // 打豆豆 // 语文 // 数学 // 外语 // 喝茶