万丈高楼平地起,欲练此功,必先打好基本功: )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
遍历时遇到的一部分问题。
以上。
文章观点内容若有错误欢迎指出交流,相互进步