记录一下symbol和set的一些知识点
Symbol 使用场景 Symbol是一个新增的数据类型,下面先描述一下symbol的一个使用场景,比如在一个游戏中,用户需要选择角色的种族:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var race = { protoss : 'protoss' , terran : 'terran' , zerg : 'zerg' } function createRole (type ){ if (type === race.protoss ){ } else if (type === race.terran ){ } else if (type === race.zerg ){ } }
那么用户选择种族后,就需要调用 createRole 来创建角色:
1 2 3 4 createRole ('zerg' ) createRole (race.zerg )
一般传入字符串被认为是不好的做法,所以使用 createRole(race.zerg) 的更多。
如果使用 createRole(race.zerg),那么就会存在一个问题:race.protoss、race.terran、race.zerg 的值为多少并不重要。
改为如下写法,对 createRole(race.zerg) 毫无影响:
1 2 3 4 5 var race = { protoss : 'askdjaslkfjas;lfkjas;flkj' , terran : ';lkfalksjfl;askjfsfal;skfj' , zerg : 'qwieqwoirqwoiruoiwqoisrqwroiu' }
也就是说,只要 race.zerg
的值和另外两个不一样就行了,而Symbol的用处就出来了,Symbol可以创建一个独一无二的值,下面可以改写成:
1 2 3 4 5 var race = { protoss : Symbol (), terran : Symbol (), zerg : Symbol () }
甚至可以给他起个名字:
1 2 3 4 5 var race = { protoss : Symbol ('protoss' ), terran : Symbol ('terran' ), zerg : Symbol ('zerg' ) }
不给这个值只是一个描述,和Symbol没有任何关系。
Symbol的for和keyFor方法 可以使用 Symbol.for()
来创建一个全局的变量,也就是说又可以能不唯一了:
1 2 3 4 5 6 let a = Symbol .for ("xxx" )let b = Symbol .for ("xxx" )console .log (a == b) let c = Symbol ()let d = Symbol ()console .log (a !== b)
使用 keyFor
可以读取使用 Symbol.for()
创建的变量:
1 2 3 4 let a = Symbol ()let b = Symbol .for ('123' )console .log (Symbol .keyFor (a)) console .log (Symbol .keyFor (b))
解决字符串耦合问题 比如有一个记录成绩的变量,但是可能在一个班里面存在两个同名的人:
1 2 3 4 5 6 7 let user1 = '张三' let user2 = '张三' let grade = { [user1]: {c :95 , sql :89 }, [user2]: {c :86 ,sql :78 } } console .log (grade)
因为[user1]
和[user2]
最终都会解析成字符串,所以相当于第二次把第一次覆盖了。可以改成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let user1 = { name : '张三' , key : Symbol () } let user2 = { name : '张三' , key : Symbol () } let grade = { [user1.key ]: {c :95 , sql :89 }, [user2.key ]: {c :86 ,sql :78 } } console .log (grade[user1.key ]) console .log (grade[user2.key ])
在缓存容器中的使用 比如在一个系统中,有很多模块,和一个为所有模块提供一个储存空间的容器,如果没有Symbol可能是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Cache { static data = {} static set (name, value ) { return (this .data [name] = value) } static get (name ) { return this .data [name] } } let user = { name : "apple" , desc : "用户资料" } let cart = { name : "apple" , desc : "购物车" } Cache .set ("user-apple" , user)Cache .set ("cart-apple" , cart)console .log (Cache .get ("cart-apple" ));
当我们有symbol后可以改成这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let user = { name : "apple" , desc : "用户资料" , key : Symbol () } let cart = { name : "apple" , desc : "购物车" , key : Symbol () } Cache .set (user.key , user)Cache .set (cart.key , cart)console .log (Cache .get (cart.key ));
属性保护 Symbol有个特性就是无法用 for in
得到对象中的Symbol属性:
1 2 3 4 5 6 7 8 9 10 let s = Symbol ()let obj = { name : "my name" , [s]: "Symbol" } for (const key in obj){ console .log (key) }
可以通过 Object.getOwnPropertySymbols
方法获得:
1 2 3 4 for (const key of Object .getOwnPropertySymbols (obj)){ console .log (key) }
但是这种情况又获取不到非隐藏的属性,可以通过反射拿到所有属性:
1 2 3 4 5 for (const key of Reflect 。ownKeys (obj)){ console .log (key) }
Set 基本用法 ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 const s = new Set ();[2 ,3 ,5 ,4 ,5 ,2 ,2 ].forEach (x => s.add (x)); for (let i of s) { console .log (i); } const set = new Set ([1 ,2 ,3 ,4 ,4 ,]);console .log ([...set]);const items = new Set ([1 ,2 ,3 ,4 ,5 ,5 ,5 ,5 ,]);console .log (items.size );const set2 = new Set (document .querySelectorAll ('div' ));console .log (set2.size ); const set2 = new Set ();document .querySelectorAll ('div' ) .forEach (div => set.add (div)); console .log (set.size );let set3 = new Set ();let a = NaN ;let b = NaN ;set3.add (a); set3.add (b); console .log (set3) let set4 = new Set ();set4.add ({}); console .log (set4.size ); set4.add ({}); console .log (set4.size ); let set5 = new Set ("abc" )console .log (set5.size ) let set6 = new Set ("abc" )set6.delete ("b" ) console .log (set6) console .log (set6.values ()) set6.clear () console .log (set6)
类型转换 转换成数组:
1 2 3 let set = new Set (["abc" , "def" ])console .log (Array .from (set))console .log ([...set])
去除数组中重复的值:
1 2 3 let arr = [1 ,2 ,3 ,4 ,3 ,2 ,1 ]arr = [...new Set (arr)] console .log (arr)
遍历 1 2 3 4 5 6 7 8 9 let set = new Set ("12345" )set.forEach (function (value, key, set ) { console .log (value,key,set) }) for (const value of set) { console .log (value) }
并集/交集/差集算法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let a = new Set ("12345" )let b = new Set ("2478" )console .log (new Set ([...a, ...b]))console .log ( new Set ([...a].filter (function (item ) { return b.has (item) })) ) console .log ( new Set ([...a].filter (function (item ) { return !b.has (item) })) )
WeakSet 因为WeakSet的弱引用特性,不能使用keys、values等方法,for of也是不能用。WeakSet与Set相比,WeakSet只能是对象的集合 ,不能是任意类型的任意值。如果没有其他的对WeakSet中对象的引用,那么这些对象会被当成垃圾回收掉。 这也意味着WeakSet中没有存储当前对象的列表。 正因为这样,WeakSet 是不可枚举的。
Map类型 Map是ES6之后的哈希结构,算是对object类型的修补。在以往的Object类型中只是把字符串作为键名,而Map可以任意类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let obj = { 1 : "我是1" , "1" : "我还是1" } console .log (obj) let map = new Map ([ [1 , "a" ], ["1" , "b" ] ]) console .log (map) map.set (function ( ){}, "我是一个函数" ) map.set ({}, "我是一个对象" ) console .log (map)
set方法也支持链式操作:
1 2 let map = new Map ()map.set (1 ,"a" ).set (2 ,"b" )
map类型的增删改查和遍历 增删改查等操作
1 2 3 4 5 6 7 8 9 10 11 12 13 let map = new Map ();map.set ("key" , "value" ) map.get (key) map.delete (key) delete map[key]map.clear () map.has (key)
遍历操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 let map = new Map ();for (const key of map.key ()) { } for (const key of map.value ()) { } for (const [key, value] of map.entries ()) { } hd.forEach ((value, key ) => { })
map的类型转换 数组转换,可以使用展开语法或者 Array.form
方法转换。
1 2 3 4 5 6 let hd = new Map ([["houdunren" , "后盾人" ], ["hdcms" , "开源系统" ]]);console .log (...hd); console .log (...hd.entries ()); console .log (...hd.values ()); console .log (...hd.keys ());
WeakMap 和WeakSet相似,WeakMap对键名是弱引用,键值是正常引用。
键名必须是对象
垃圾回收不考虑WeaMap的键名,不会改变引用计数器,键在其他地方不被引用时即删除
因为WeakMap 是弱引用,由于其他地方操作成员可能会不存在,所以不可以进行forEach( )
遍历等操作
也是因为弱引用,WeaMap 结构没有keys( ),values( ),entries( )等方法和 size 属性
当键的外部引用删除时,希望自动删除数据时使用 WeakMap