Symbol原始数据类型
1. 概述
- ES6 引入的一种新的原始数据类型Symbol,表示独一无二的值。
let s = Symbol(); typeof s; //'symbol'
- Symbol函数可以接受一个字符串作为参数,表示对 Symbol 实例的描述,主要是为了在控制台显示,或者转为字符串时,比较容易区分。
let s1 = Symbol('foo'); let s2 = Symbol('bar'); s1 //Symbol(foo) s2 //Symbol(bar)
- Symbol函数的参数只是表示对当前 Symbol 值的描述,因此相同参数的Symbol函数的返回值是不相等的。
// 没有参数的情况 let s1 = Symbol(); let s2 = Symbol(); s1 === s2 // false // 有参数的情况 let s1 = Symbol('foo'); let s2 = Symbol('foo'); s1 === s2 // false
- Symbol 值不能与其他类型的值进行运算,会报错。
- 但是,Symbol 值可以显式转为字符串。
- 另外,Symbol 值也可以转为布尔值,但是不能转为数值
2.作为属性名的 Symbol
- 由于每个Symbol值都不相等,所以其可以作为标识符,用于对象的属性名,就能保证不会出现同名的属性。可以防止某一个键被不小心改写或覆盖。
- 注意,Symbol 值作为对象属性名时,不能用点运算符。
let mySymbol = Symbol(); // 第一种写法 let a = {}; a[mySymbol] = 'Hello!'; // 第二种写法 let a = { [mySymbol]: 'Hello!' }; // 第三种写法 let a = {}; Object.defineProperty(a, mySymbol, { value: 'Hello!' }); // 以上写法都得到同样结果 a[mySymbol] // "Hello!" const mySymbol = Symbol(); const a = {}; a.mySymbol = 'Hello!'; a[mySymbol] // undefined a['mySymbol'] // "Hello!" //在对象的内部,使用 Symbol 值定义属性时,Symbol 值必须放在方括号之中 let s = Symbol(); let obj = { [s]: function (arg) { ... } }; obj[s](123);
3.属性名的遍历
- Symbol 作为属性名,该属性不会出现在for…in、for…of循环中,也不会被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有属性,有一个Object.getOwnPropertySymbols方法,可以获取指定对象的所有 Symbol 属性名。
//Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值 const obj = {}; let a = Symbol('a'); let b = Symbol('b'); obj[a] = 'Hello'; obj[b] = 'World'; const objectSymbols = Object.getOwnPropertySymbols(obj); objectSymbols // [Symbol(a), Symbol(b)]
- Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名
let obj = { [Symbol('my_key')]: 1, enum: 2, nonEnum: 3 }; Reflect.ownKeys(obj) // ["enum", "nonEnum", Symbol(my_key)]
4.Symbol.for(),Symbol.keyFor()
- Symbol.for 重新使用同一个 Symbol 值。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
let s1 = Symbol.for('foo'); let s2 = Symbol.for('foo'); s1 === s2 // true
- Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key
let s1 = Symbol.for("foo"); Symbol.keyFor(s1) // "foo" //变量s2属于未登记的 Symbol 值,所以返回undefined let s2 = Symbol("foo"); Symbol.keyFor(s2) // undefined
Set和Map数据结构
1. Set
- Set类似于数组,但是成员的值都是唯一的,没有重复的值
const s = new Set(); //通过add方法向Set结构加入成员,Set结构不会添加重复的值 [2,3,5,4,5,2,2].forEach(x => s.add(x)); for(let i of s){ console.log(i); } //2 3 5 4
- Set函数接受一个数组作为参数,用来初始化,可以对数组进行去重
const set = new Set([1,2,3,4,4]); [..set]; //[1,2,3,4] const items = new Set([1,2,3,4,5,5,5,5]); items.size //5
- 在 Set 内部,两个NaN是相等
let set = new Set(); let a = NaN; let b = NaN; set.add(a); set.add(b); set //Set {NaN}
- 在 Set 内部,两个对象总是不相等的
- Set 实例的属性和方法
//add(value):添加某个值,返回 Set 结构本身 s.add(1).add(2).add(2); //返回Set实例的成员总数 s.size //2 //has(value):返回一个布尔值,表示该值是否为Set的成员 s.has(1) // true s.has(2) // true s.has(3) // false //delete(value):删除某个值,返回一个布尔值,表示删除是否成功 s.delete(2); //clear():清除所有成员,没有返回值
- Array.from方法可以将 Set 结构转为数组
const items = new Set([1, 2, 3, 4, 5]); const array = Array.from(items);
- 遍历操作
(1)keys(),values(),entries()
(2)forEach()//由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致 let set = new Set(['red', 'green', 'blue']); //keys():返回键名的遍历器 for (let item of set.keys()) { console.log(item); } // red // green // blue //values():返回键值的遍历器 for (let item of set.values()){ console.log(item); } // red // green // blue //entries():返回键值对的遍历器 for (let item of set.entries()) { console.log(item); } // ["red", "red"] // ["green", "green"] // ["blue", "blue"] //可以省略values方法,直接用for...of循环遍历Set for (let x of set) { console.log(x); }
let set = new Set([1, 4, 9]); set.forEach((value, key) => console.log(key + ' : ' + value)) // 1 : 1 // 4 : 4 // 9 : 9
2.WeakSet
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
3.Map
Map类似obj对象键值对的集合,obj的key值只能是字符串,但是map“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
- 含义和基本用法
const m = new Map(); const o = {p: 'Hello World'}; m.set(o, 'content') m.get(o) // "content" m.has(o) // true m.delete(o) // true m.has(o) // false
- 实例的属性和操作方法
(1)size属性返回 Map 结构的成员总数
(2)set(key, value),set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
(3)get(key) get方法读取key对应的键值,如果找不到key,返回undefinedlet map = new Map() .set(1, 'a') .set(2, 'b') .set(3, 'c');
(4)has(key) has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
(5)delete(key) delete方法删除某个键,返回true。如果删除失败,返回false。
(6)clear() clear方法清除所有成员,没有返回值 - 遍历方法
(1)keys():返回键名的遍历器。
(2)values():返回键值的遍历器。
(3)entries():返回所有成员的遍历器。
(4)forEach():遍历 Map 的所有成员。
4.WeakMap
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
WeakMap的键名所指向的对象,不计入垃圾回收机制。
5.与数组的对比
- Map与Array的对比
//数据结构横向对比:增、删、改、查 let map = new Map(); let array = []; //增 map.set('t',1); array.push({t:1}); //查 let map_exist = map.has('t'); let array_exist = array.find(item =>item.t); //改 map.set('t',2); array.forEach(item => item.t?item.t=2:' ') //删 map.delete('t'); let index = array.findIndex(item => item.t); array.splice(index,1);
- Set与Array的对比
let set = new Set(); let array = []; //增 set.add({t:1}); let array = []; //查 let set_exist = set.has({t:1}); let array_exist = array.find(item =>item.t); //改 set.forEach(item => item.t? item.t=2:''); let array_exist = array.find(item => item.t?item.t=2:' '); //删 set.forEach(item => item.t?set.delete(item):' '); let index = array.findIndex(item => item.t); array.splice(index,1);
6.与Object的对比
- 与Object的对比
let item = {t:1}; let map = new Map(); let set = new Set(); let obj = {}; //增 map.set('t',1}; set.add(item); obj['t'] = 1; //查 map.has('t'); set.has(item); 't' in obj //改 map.set('t',2); item.t=2; obj['t']=2; //删 map.delete('t'); set.delete(item); delete obj ['t'];