這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 最近每天學習的時候,發現了一道很有趣的面試題 1.const [a, b] = { a: 100, b: 200 } 2.console.log(a) 3.console.log(b) 如何在不改變1的情況下,讓代碼不報錯 這個時候,我突 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
最近每天學習的時候,發現了一道很有趣的面試題
1.const [a, b] = { a: 100, b: 200 } 2.console.log(a) 3.console.log(b) 如何在不改變1的情況下,讓代碼不報錯
這個時候,我突然想起來,之前看紅寶書的時候,有過這麼一篇內容
最後我的解題代碼為
Object.prototype[Symbol.iterator] = function(){ return Object.values(this)[Symbol.iterator] } const [a, b] = { a: 100, b: 200 } console.log(a) console.log(b)
試了下,果然ok
接下來給大家分享下我在紅寶書的基礎上,在網上找到的幾個基礎資料,方便大家理解代碼
1.什麼是symbol
首先symbol是es6新增的類型,它是一個基本類型,symbol英文翻譯是符號的意思。
在敘述symbol之前,推出一個概念,“以操作目標為程式本身的行為特性的編程,我們稱為元編程。”
我們已知symbol是es6新增類型,按照邏輯推演:作為新增的類型,symbol一定是為了彌補了之前的不足,所以才出現的,是這樣的嗎?
首先我們說說它的特別之處:
symbol屬性值對應的值是唯一的,這解決了命名衝突的問題,類似於id的作用。 symbol值不能與其他數據進行計算,包括與字元串拼接。 for/in ,for/of遍歷時不會遍佈symbol屬性 這樣看是不是覺得沒什麼特別的?最多也就彌補了命名衝突的問題。
下麵推出第二個問題:
2.Iterotor是什麼?
Iterator是一個遍歷器的介面,是部署在數據結構上的,很多數據結構原生具備Iterator介面,這就意味著,我們不需要任何處理就可以使用for/of了,擁有symbol.Iterator屬性返回的對象,都會在使用for/of時被當作Iterator介面當這個對象符合Iterator介面的標準時,for/of就可以完成任務,不符合就會報錯!
刨除這個問題,我們知道symbol可以作為屬性值存在。並且它具有唯一的特性,舉個慄子:
直接let s = Symbol();測試s就是Symbol類型了。怎麼說他是唯一的呢? let s = Symbol(); let ss = Symbol(); s == ss ; 結果是false;或者 let s = Symbol('a'); let ss = Symbol('a'); s == ss ; 結果是false 有人會好奇Symbol('a')裡面的參數a又是怎麼回事呢?字元串a表示一種修飾,對你當前創建的Symbol類型的一種修飾,作為區分使用,否則當你創建多個Symbol數據時,容易混淆。 現在我們回過頭說Iterotor是symbol的內置符號,而字元串a是自定義的符號。
3.symbol的內置符號symbol.Iterator,有什麼用?
這個符號可以是任意對象上的一個專門屬性,語言機制會自動的在這個屬性上尋找一個方法,這個方法會構造一個迭代器來迭代這個對象的值,這個方法就是next方法,...展開和for/of迴圈會自動使用它,我們可以自定義symbol.iterator屬性為任意對象值定義自己的迭代器邏輯,他將覆蓋預設的迭代器。相當於是定義了一種元編程行為,提供給Javascript其他部分(也就是運算符和迴圈結構)在處理定義的對象時使用。
在Js中迭代器對象實現了可迭代協議,迭代器對象由Symbol.iterator屬性的值返回。 Symbol.iterator屬性的值是一個函數,它返回一個迭代器對象。 迭代器指的是擁有next方法的對象。 該next方法必須返回一個帶有value和done的對象。
4.為什麼會出現for..of?
為了理解條件語句,我們曾想象JavaScript解釋器在源代碼中會經過不同路徑。而迴圈語句則是把這些路徑彎曲又折回起點,以重覆執行代碼中的某部分。
es6定義了一個新迴圈語句:for/of。這種新迴圈雖然使用for關鍵字,但它們是完全不一同的兩種迴圈,你說和for/in像不像?為什麼?
for/of迴圈是專門用於可迭代對象的,什麼是可迭代對象呢?
我們前文提到具有symbol.iterator屬性的對象就是可以迭代的。而這個對象就是可迭代對象。
對象本身預設是不可迭代的。運行時嘗試對常規對象使用for/of會拋出TypeError:
let o={x:1,y:2,z:3}; for(let element of o){//拋出TypeError,因為o不是迭代的對象 console.log(element); }
如果你想迭代對象,可以使用for/in迴圈,或者基於Object.keys()方法的結果使用for/of:
let o={x:1;y:2,z:3}; let keys=""; for(let k of Object .keys(o)){ keys+=k; } console.log(keys) //x y z /*這是因為Object.keys()返回一個對象屬性名的數組,而數組是通過for/of來迭代的。也要註意,這種對象的鍵的迭代並不是實時的,在迴圈體內修改o不會影響迭代。如果你不在乎對象的屬性,也可以像下麵這樣迭代每個鍵對應的值: */ let o={x:1;y:2,z:3}; let sum=0; for(let v of Object .values(0)){ sum+=v; } console.log(sum) //6 如果你即想要對象的屬性還想要屬性的值,可以基於Object.entries()和解構賦值來使用for/of: let pairs=""; for(let [k , v] of Object .entries(o)){ pairs+=k + v; } console.log(pairs)//x 1 y 2 z 3 Object .entries()返回一個數組的數組,其中每個內部數組表示對象的一個屬性的鍵值對。這裡使用解構賦值把這些內部數組分開並且分別把它們的元素賦值給兩個變數。
不知道你懂了沒有,如果沒有,那麼接下來一篇,會加深你的印象
es6中有三類結構生來就具有Iterator介面:數組、類數組對象、Map和Set結構。
var arr = [1,2,3,4]; let iterator = arr[Symbol.iterator](); console.log(iterator.next()); //{ value: 1, done: false } console.log(iterator.next()); //{ value: 2, done: false } console.log(iterator.next()); //{ value: 3, done: false } console.log(iterator.next()); //{ value: 4, done: false } console.log(iterator.next()); //{ value: undefined, done: true }
至於對象沒有佈置iterator介面的原因,不知道最近大家有沒有看根據《你一生的故事》拍成的電影“降臨",片中出現的外星語言是一門非線性的語言。而我們說的數組,Map等結構中的成員都是有順序的,即都是線性的結構,而對象,各成員並沒有一個確定的順序,所以遍歷時先遍歷誰後遍歷誰並不確定。所以,給一個對象部署iterator介面,其實就是對該對象做一種線性轉換。如果你有這種需要,就需要手動給你的對象部署iterator介面咯~
如:
let obj = { data: [ 'hello', 'world' ], [Symbol.iterator]() { const self = this; let index = 0; return { next() { if (index < self.data.length) { return { value: self.data[index++], done: false }; } else { return { value: undefined, done: true }; } } }; } };
可以看到,Symbol.iterator會返回一個對象,這就是一個遍歷器對象,而作為遍歷器對象,其必須具備的特征就是必須具備next()方法。
我們還可以據此實現指針結構的數據結構。具體略~
至於可以使用Array.from轉換成數組的類數組對象,部署iterator有一種很簡單的方法,即直接使用數組的[Symbol.iterator]介面。
fakeArray.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
當然,不知道你們看到next是否想到了es6的一個新玩意兒,即Generator函數。用Generator函數來實現Symbol.iterator介面,事半功倍。
var yieldIterator = {}; yieldIterator[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...yieldIterator] // [1, 2, 3] 註意,yield* 後面跟的是一個可遍歷的結構,它會調用該結構的遍歷器介面。
註意,yield* 後面跟的是一個可遍歷的結構,它會調用該結構的遍歷器介面。
其次,其它調用到遍歷器的操作還有解構賦值、擴展操作符、其它任何接受數組作為參數的場合,如:
for...of Array.from() Map(), Set(), WeakMap(), WeakSet()(比如) Promise.all() Promise.race()