萬丈高樓平地起,欲練此功,必先打好基本功: )java
在瞭解 ES6 新增的變量類型前,咱們必須先知道 JavaScript 在ES6以前,有以下六種基本數據類型:Null、Undefined、Number、String、Boolean和Object。而 ES6 中,新增了第七種數據類型:Symbol。
上述七種數據類型做以下類型劃分:數組
基本類型: Undefined、Null、Boolean、String、Number,這五種類型的變量都是直接把實際值存儲在棧內存當中,操做或訪問的時候都是直接對實際值進行的.數據結構
引用類型: Object。Object 類型的變量是把指向堆內存的地址值存儲在棧內存當中的一類數據。(關於堆棧的知識將會在後面的文章中做介紹。)函數
有關基本類型和引用類型的說明,網上已經有不少文章有說明介紹,爲免篇幅過長,這裏就再也不重複敘述了。測試
這裏咱們着重說一下 Symbol 類型:
Symbol 是一個函數,調用該函數,返回的惟一值就是 Symbol 類型值;this
let symbol = Symbol(); symbol; //Symbol() typeof symbol; //symbol let symbol1 = new Symbol(); //Uncaught TypeError: Symbol is not a constructor
let testStr = 'this is a string', testObj = {obj: 'this is a object'}, testArr = ['this','is','a','array'], testFn = () => { console.log('this is a function'); }, testSym = Symbol('this is a symbol'), symbolStr = Symbol(testStr), //Symbol(this is a string) symbolObj = Symbol(testObj), //Symbol([object Object]) symnolArr = Symbol(testArr), //Symbol([object Object]) symbolFn = Symbol(testFn), //Symbol(() => {console.log('this is a function');}) symbolSym = Symbol(testSym); //Uncaught TypeError: Cannot convert a Symbol value to a string
let symbol1 = Symbol('test'), symbol2 = Symbol('test'); symbol1 == symbol2; //false symbol1 === symbol2; //false
let symbol = Symbol('this is symbol'), str = 'this is string', num = 2, symStr = symbol.toString(); let newStr = symbol + str; //Uncaught TypeError: Cannot convert a Symbol value to a string let newNum = Symbol + num; //Uncaught TypeError: Cannot convert a Symbol value to a number symStr; // Symbol(this is symbol)
let symbol = Symbol('this is symbol'), symbol1 = Symbol('this is symbol'); let obj = { [symbol]: 'this is a', [symbol1]: 'this is b' }; obj; //{Symbol(this is symbol): "this is a", Symbol(this is symbol): "this is b"} let str = 'test', str1 = 'test', obj = {}; obj[str] = '測試非symbol類型命名的屬性'; obj; //{test: "測試非symbol類型命名的屬性"} obj[str1] = '再次測試非symbol類型命名的屬性'; obj; //{test: "再次測試非symbol類型命名的屬性"}
let symbol = Symbol('this is symbol'), symbol1 = Symbol('this is symbol'); let symbolObj = { [symbol]: 'this is a', [symbol1]: 'this is b', } Object.getOwnPropertySymbols(symbolObj); //[Symbol(this is symbol), Symbol('this is symbol')]
let symbol = Symbol('this is symbol'), symbol1 = Symbol('this is symbol'); let obj = { [symbol]: 'this is a', [symbol1]: 'this is b', d: 'test' }; for(key in obj){ console.log(key); // d } Object.keys(obj); // ['d'] Object.getOwnPropertyNames(obj); // ['d'] JSON.stringify(obj); //{d:'test'} //仍可訪問到Symbol值定義的屬性鍵和屬性值 let symKeys = Object.getOwnPropertySymbols(obj); symKeys; //[Symbol(this is symbol), Symbol(this is symbol)] symKeys[0]; //Symbol(this is symbol) obj[symKeys[0]]; //this is a
let symbol = Symbol('this is symbol'), symbol1 = Symbol.for('this is symbol'), symbol2 = Symbol.for('this is symbol'); symbol === symbol1 //false symbol === symbol2 //false symbol1 === symbol2 //true
經過 Symbol 建立的值有以下兩種狀況:
被登記的與未被登記的。
什麼是被登記的?在設計
let s1 = Symbol.for("foo"); console.log(Symbol.keyFor(s1)); // "foo" var s2 = Symbol("foo"); console.log(Symbol.keyFor(s2) ); // undefined
這裏還有一些 MDN 關於 Symbol 的屬性介紹,由於感受在平常開發中使用的機率比較低,所以也不贅述一些本身的理解了,有興趣的朋友能夠去看看https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbolcode
關於 Symbol 的總結: 對於 Symbol 的使用,實用性最高的我以爲是 symbol 值用於屬性命名的狀況,在一些開發狀況下,對一個 Object 對象進行遍歷的時候,但願某一些屬性不被 for in 或 Object.keys 遍歷出來,避免作 if 或 switch 狀況處理,這時候使用 Symbol 定義屬性名是個不錯的選擇。orm
在原有 ES5 的屬性命名和賦值過程當中,多人協做開發可能會致使一些屬性名重命名而致使值覆蓋,對「半私有屬性」屬性使用 Symbol 命名屬性名會是個很好的選擇。對象
ES6 在原有的數據結構類型( Array、Object )上新增了兩種類型( Map、Set ),咱們在使用的時候還能夠經過自由組合的形式使用這些結構類型達到本身想要的數據結構,這就須要一種統一的接口機制供咱們調用處理不一樣的數據結構 —— Iterator。
ES6中,只要被遍歷「對象」存在 可迭代協議 , 即System.iterator
屬性,該對象都是被認爲是「可遍歷的」。
在 ES6 中,有三類數據結構原生具有 Iterator 接口:__數組、某些相似數組的對象(如 字符串、相似數組形式的對象)、Set 和 Map 結構數據__。
迭代器協議 定義了一種標準的方式來產生一個有限或無限序列的值,每次遍歷都會首先調用被遍歷數據集合對象中的 [Symbol.iterator]()
方法,該方法返回一個 Symbol對象的iterator屬性 ,該屬性擁有執行迭代對象的 next
方法,並返回一個對象,以下是一段模擬 next
方法的代碼
function Iterator(arr){ let nextIndex = 0; return { next: function(){ return nextIndex < arr.length ? {value: arr[nextIndex++], done: false} : {value: undefined, done: true}; }, [Symbol.iterator]: function() { return this } } } let example = Iterator(['a', 'b']); example.next() // {value: "a", done: false} example.next() // {value: "b", done: false} example.next() // {value: undefined, done: true} //遍歷結束
返回的對象中必須包含以下兩個屬性
{ done, //迭代是否已經執行完畢 迭代完畢時返回true,不然返回false,返回false時會繼續執行迭代 value //當前成員的值 迭代完畢時返回undefined,不然返回當前成員的值 }
Symbol.iterator
(Iterator接口)擴展運算符(…)會默認調用 iterator
接口。
對數組和Set結構進行解構賦值時,會默認調用 Symbol.iterator
方法。
yield*後面跟的是一個可遍歷的結構,它會調用該結構的 iterator
接口。
執行 for...of
循環時,會調用 iterator
接口對數據進行處理。
在 Array.form()
時,會遍歷數據,調用 iterator
接口返回相應數據
Map()
, Set()
, WeakMap()
, WeakSet() (好比new Map([['a',1],['b',2]]))
, Promise.all()
, Promise.race()
。順帶一提:
在ES6中,具備 System.iterator 屬性的對象都可經過 for...of
進行遍歷
let arr = ['1','2','3']; arr.pro = 'test'; for (let i in arr) { console.log(arr[i]); //1 2 3 test } for(let i of arr) { console.log(arr[i]); //1 2 3 }
for...of
的相比於 forEach
、 for...in
,其好處在於: forEach
循環沒法經過 break
、 continue
、 return
跳出循環,而 for...of
能夠; for...in
循環設計的目的是用於遍歷包含鍵值對的對象,對數組並非那麼友好,而 for...of
遍歷輸出的值會在輸出數據後默認遍歷結束。
關於 Iterator 的總結: Iterator做爲一種統一的接口機制供咱們調用處理不一樣的數據結構,讓能夠用相同的方式來遍歷集合,而不用去考慮集合的內部實現,若數據的形式發生改變,只要數據內部還存在 System.iterator
屬性,那遍歷的代碼就能夠不用作修改直接使用。 同時,其服務於 for...of
循環, for...of
又有效地避免了以往對數據集僅能經過 forEach
、 for..in
遍歷時遇到的一部分問題。
以上。
文章觀點內容若有錯誤歡迎指出交流,相互進步