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) } // 吃飯 // 睡覺 // 打豆豆 // 語文 // 數學 // 外語 // 喝茶