-(四)Set、Map、(Weak)、Symbol和BigInt
作者:互联网
set与map:这两个数据结构都要通过new来构造。两个数据结构接收的参数,一个为一维数组一个为二维数组。
一、Set数据结构
Set(集合)实际上就是一个类似于数组的数据结构。不同的是,Set的成员中没有重复的基本类型值或者地址相同的引用类型值(简单来说就是类似于使用了Object.is()比较)。
注:Set内部的比较类似于Object.is(), 不过es6还是对比较规则做了一点修改,比如,+0和-0在Set内部是会被去重的,但是Object.is()返回fasle
// Set(2) {0, NaN} false
let aSet = new Set([+0,-0,NaN,NaN]);
console.log(aSet,Object.is(+0,-0));
(1)用Array.from()和set方法实现数组去重
let aset = new Set([1,2,1]);
Array.from(aset);
(2)用拓展运算符和Set方法实现数组去重
var arr = [1,2,1,11];
console.log([...new Set(arr)]); // (3)[1, 2, 11]
0、set和数组的区别
- (1)键名:set的键名和键值都是一样的。
- (2)获取内部值:set不能像数组那样通过下标来访问内部值,而必须要通过遍历(一般就用forEach)来获取
// demo1
let aSet = new Set([2,3,[4,5]]);
console.log(aSet[2]); // undefined
// demo2
let aSet = new Set([2,3,[4,5]]);
aSet.forEach((value, index, arr) => {
console.log(value, index, arr);
})
// 3次输出:
// 2 2 Set(3) {2, 3, Array(2)}
// 3 3 Set(3) {2, 3, Array(2)}
// [4,5] [4,5] Set(3) {2, 3, Array(2)}
1、创建set
直接用new操作符创建。set可以接收一个数组作为参数。如果这个数组中有重复的基本类型值或者地址相同的引用类型值,则只会保留一个值。而且,set有一个size属性,用来表示去重之后set中数据的个数。
// demo1
var aSet = new Set([1,2,1]);
typeof aSet, aSet, aSet.size // object Set(2){1, 2} 2
// demo2
let aArr1 = [1,2],
bArr1 = aArr1,
cArr1 = [1,2];
let aSet = new Set([1,3,1,aArr1,bArr1,cArr1]); // Set(4) {1, 3, Array(2), Array(2)},bArr1被忽略,返回的结果中的两个arr为aArr1和cArr1
2、set的方法
操作方法(用于操作数据)
- (1)set.add(value): 返回修改后的set结构本身,并且会修改原Set。添加一个数据。
var aSet = new Set([1,2]);
// Set(5){1, 2, "a", Array(1), {…}} Set(5){1, 2, "a", Array(1), {…}}
console.log(aSet.add('a').add([5]).add({o:"1"}),aSet);
- (2)set.delete(value): 返回一个表示是否删除成功的布尔值。删除指定数据,如果需要删除引用类型,那么需要提供引用类型对应的地址来删除
// demo1
var aSet = new Set([1,2]);
console.log(aSet.delete(1),aSet); // true Set(1){2}
// demo2,删除引用类型失败
let aSet = new Set([1,3,1,[1,2],[1,2]]);
console.log(aSet.delete([1,2]), aSet); // false Set(4) {1, 3, Array(2), Array(2)}
// demo3,删除引用类型成功
let arr = [1,2];
let aSet = new Set([1,3,1,arr,[1,2,3]]);
console.log(aSet.delete(arr), aSet); // true Set(3) {1, 3, Array(3)}
- (3)set.has(value): 返回一个布尔值。判断该值是否为set的成员。
var aSet = new Set([1,2]);
console.log(aSet.has(2),aSet); // true Set(2){1, 2}
- (4)set.clear(): 清除所有数据,没有返回值
遍历方法(用于遍历成员),这个实际上就是对象上面的几个遍历方法
注:set结构的键名和键值是一样的
- (1)set.keys(): 返回键名的遍历器(Iterator)对象
- (2)set.values(): 返回键值的遍历器(Iterator)对象。由于set结构没有键名或者键名和键值是一样的,所以这两个方法返回相同的值。Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法,所以,可以省略values方法,直接用for…of循环遍历 Set
- (3)set.entries(): 返回键值对的遍历器(Iterator)对象.由于它同时返回键值对,所以如果用for of遍历这个值,则每次输出一个数组
var aSet = new Set([1,2]);
// SetIterator{1, 2} SetIterator{1, 2} SetIterator{1, 2} Set(2){1, 2}
console.log(aSet.keys(),aSet.values(),aSet.entries(),aSet);
for(var item of aSet.values()) {
console.log(item); // 1 2
}
for(var item of aSet) {
console.log(item); // 1 2
}
for(var item of aSet.entries()) {
console.log(item); // [1,1] [2,2]
}
- (4)set.forEach(): 这个实际上和数组的forEach差不多,一般要获取set结构内部的值,就用这个方法
var aSet = new Set([2,3]);
aSet.forEach(function(currentValue,index,set){console.log(currentValue, index);});
// 两段输出
// 2 2
// 3 3
二、WeakSet数据结构
- WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 区别如下。
- (1)WeakSet 的成员只能是对象,而不能是其他类型的值。
- (2)WeakSet 中的对象成员都是弱引用,即WeakSet对该对象的引用不计入垃圾回收机制(即不会让引用计数的数量+1);也就是说,如果其他对象都不再引用该成员对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中
- (3)WeakSet只有WeakSet.prototype.add(value)、WeakSet.prototype.delete(value)、WeakSet.prototype.has(value)这三个方法
- (4)WeakSet没有办法遍历它的成员, 即没有size属性也不能使用forEach等
- (4)WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏
const ws = new WeakSet();
ws.add(1) // TypeError: Invalid value used in weak set
ws.add(Symbol()) // TypeError: invalid value used in weak set
ws.add(null); // 报错
ws.add({a:1}) // 成功,添加的成员只能是对象
const ws = new WeakSet([3, 4]); // 报错,
const ws = new WeakSet([[1, 2], [3, 4]]); // 成功,数组的每一项只能是 对象
ws.size // undefined。WeakSet不能访问 size
ws.forEach // undefined。WeakSet不能使用 forEach 进行遍历
ws.forEach(function(item){ console.log('WeakSet has ' + item)}) // // TypeError: undefined is not a function
三、Map数据结构
Map(字典)实际上就是一个类似于对象的数据结构。不同的是,对象中的键只能是字符串或者Symbol,而Map中的键可以包括各种数据类型。
1、创建Map
直接用new操作符创建。Map可以接收一个二维数组作为参数。二维数组里层数组的前两个项,即表示创建的Map的键和值(里层数组后面的多余项无效)。Map依然有一个size属性,用来表示Map中数据的个数
var aMap = new Map([['1',2,3],[2,3]]);
console.log(typeof aMap, aMap, aMap.size); // object Map(2){"1" => 2, 2 => 3} 2
2、Map的方法
操作方法(用于操作数据):和Set相比Map多了个获取值的方法map.get(),并且多了个修改方法map.set()(set只有添加值的set.add()方法)
- (1)map.set(key,value): 返回修改后的Map结构本身。如果没有key则创建一个
- (2)map.get(key): 返回key对应的键值,找不到返回undefined(不会对Key进行任何转换)
var aMap = new Map([['1',2,3],[2,3]]);
console.log(aMap.set(2,4),aMap.get(2)); // Map(2){"1" => 2, 2 => 4}
console.log(aMap.get(1),aMap.get('1')); // undefined 2
let sym = Symbol.for('test');
let arr = [['a',1],['b',2],[sym,45]];
let map = new Map(arr);
// 获取key值为symbol
console.log(map.get(sym)); // 45
- (3)map.delete(key): 返回一个布尔值。表示是否删除指定键成功的
- (4)map.has(key): 返回一个布尔值。表示key是否在map中
- (5)map.clear():没有返回值。表示清空所有数据
遍历方法(用于遍历成员)
- (1)map.keys(): 返回包含所有键名的遍历器对象。Array.form()或者拓展运算符 是可以直接把Iterator(遍历器)对象或者map对象也转换成数组的。keys,values返回的Iterator对象,会被转换为一维数组; entries返回的Iterator对象,会倍转换为二维数组;直接转换map对象,也是返回一个二维数组
- (2)map.values(): 返回包含所有键值的遍历器对象。
- (3)map.entries(): 返回包含所有键值对的遍历器对象.
- (4)map.forEach(): 这个实际上和数组的forEach差不多
所以直接用Array.from()来把Iterator对象构建为数组,然后再进行遍历,是一个好方法,不用再多记其余的api
- 判断Set和Map类型
- 如果直接用typeof来判断Set或者Map,返回的为Object
- 那么判断Set或者Map类型,需要使用Object.prototype.toString.call() 来判断。返回的为[object Set]
const map = new Map();
map.set('a', 1)
map.set('b', 2)
map.set('c', 3)
map.forEach((value,index) => {console.log(value,index)}) // 1 "a", 2 "b", 3 "c"
const keysIterator = map.keys(); // MapIterator{"a", "b", "c"}
let tempArr = Array.from(keysIterator); // (3)["a", "b", "c"]
// 上面这里用拓展运算符 也可以实现同样的效果,入 [...keysIterator]
const valuesIterator = map.values(); // MapIterator{1, 2, 3}
tempArr = Array.from(valuesIterator); // (3)[1, 2, 3]
const entriesIterator = map.entries(); // MapIterator{"a" => 1, "b" => 2, "c" => 3}
tempArr = Array.from(entriesIterator); // (3)[["a", 1], ["b", 2], ["c", 3]]
const f = Array.from(new Map([["a",2],["b",3]])); // [["a", 2], ["b", 3]],直接把map对象转换为二维数组。但是Array.from是不能直接转换普通对象的
const ff = Array.from({"a":2}); // []。Array.from是不能直接转换普通对象的,转换结果为空
// 判断Set和Map类型
typeof new Set(); // "object"
typeof new Map(); // "object"
Object.prototype.toString.call(new Set()); // "[object Set]"
Object.prototype.toString.call(new Map()); // "[object Map]"
四、WeakMap数据结构
Weak结构即WeakSet或者WeakMap都有助于防止内存泄漏
- WeakMap结构与Map结构类似,也是用于生成键值对的集合。但是,它与 Map 区别如下。
- (1)WeakMap也只能接受对象作为键名(null除外),不接受其他类型的值作为键名。同 WeakSet
- (2)WeakMap 键名所指向的对象依然是弱引用,不计入垃圾回收机制。键值不受影响
- (3)WeakMap只有)WeakMap.prototype.get(value)、WeakMap.prototype.set(value)、WeakMap.prototype.has(value)、WeakMap.prototype.delete(value)这四个方法
- (4)WeakMap依然没有办法遍历它的成员, 即没有size属性也不能使用forEach等
- (5)WeakMap 的应用场景依然是,用DOM 节点作为键名,当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除
const map = new WeakMap();
map.set(1, 2) // TypeError: 1 is not an object!
map.set(null, 2) // TypeError: Invalid value used as weak map key
wm.set(document.getElementById('example'), 'some information'); // 成功
- (4)WeakSet 的一个用处,是储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏
五、Symbol
Symbol为es6中新增的基本数据类型,表示独一无二的值。为js中第7种数据类型。
目的:由于es5中对象的属性名只能用字符串来表示,所以就容易造成属性名的冲突。所以,这里就新增了Symbol数据类型表示独一无二的值,然后用它来作为对象的属性名字,就不会出现属性名重复这种情况了。
Symbol作为对象属性名的时候,无法被常用的for in,Object.keys()等遍历出来(提一下for…of不是遍历对象的,用for…of可以遍历出Symbol)。而要用特定的
Object.getOwnPropertySymbols(obj)
来遍历Symbol属性值(返回包含所有symbol属性名的数组)
1、创建
- (1)Symbol()函数:Symbol类型的值,用Symbol()函数来创建。但是,由于Symbol为基本类型不为对象,所以不能加new操作符。
var s1 = Symbol(),
s2 = Symbol();
console.log(typeof s1, s1, s1 === s2,); // symbol Symbol() false
typeof Symbol(); // "symbol"
- (2)参数:Symbol的函数可以传入参数,参数表示对这个Symbol的描述,用来区分每一个Symbol,并没有其它作用。如果,传入的参数为对象,则先调用对象的toString方法。相同描述的Symbol,依然不相等
var s1 = Symbol('a'),
s2 = Symbol('a');
console.log(s1, s1 === s2); // Symbol(a) false
- (3)Symbol作为对象的属性名:Symbol作为对象的属性名的时候,必须加在[]里面,不然会出错,且访问的时候也不能用点(.)操作符来访问。
var s1 = Symbol('s1'),
s2 = Symbol('s2'),
obj = {
[s1]: 'aSymbol'
};
obj[s2] = 'aSymbol2';
console.log(obj[s1],obj.s2); // aSymbol undefined
2、Symbol注意事项
- (1)Symbol 值不能与其他类型的值进行运算,不然会报错。
var s1 = Symbol('a');
console.log(s1 + '1'); // Cannot convert a Symbol value to a string
- (2)Symbol的转换:symbol可以转换为字符串和布尔值,但是不能转换为数值(会直接报错)。
var s1 = Symbol('a');
console.log(String(s1),s1.toString(),Boolean(s1)); // Symbol(a) Symbol(a) true
console.log(Number(s1)); // Cannot convert a Symbol value to a number
var s1 = Symbol('a');
if (s1) {
console.log('show me'); // 输出show me
}
- (3)Symbol作为属性名,该属性无法用for…in、Object.key等遍历出来(提一下for…of不是遍历对象的,用for…of可以遍历出Symbol)。要获取对象的symbol属性名,可以用Object.getOwnPropertySymbols(obj)方法,返回一个数组,包含对象的所有 Symbol 属性名
var s1 = Symbol('s1'),
s2 = Symbol('s2'),
obj = {
[s1]: 'aSymbol',
[s2]: 'aSymbol2',
normal: true
};
for (let item in obj) {
console.log(item); // normal
}
console.log(Object.getOwnPropertySymbols(obj)); // (2)[Symbol(s1), Symbol(s2)]
3、Symbol.for()、Symbol.keyFor()
- Symbol.for(): 它接受一个字符串作为参数,且为symbol的key值。然后搜索其它Symbol有没有这个key值,如果有,就返回这个Symbol值,没有就新建并返回一个具有该key值的Symbol值。
- Symbol.for()和Symbol()的差别:Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。而Symbol()每次一调用就生成一个新的Symbol值。
let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
s1 === s2 // true
Symbol.keyFor(): 接收一个Symbol值作为参数,返回这个Symbol值的key,没有指定key的话则返回undefined。keyFor就是返回key
let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined
六、BigInt
- BigInt数据类型提供了一种方法来表示大于2^53-1的整数。BigInt可以表示任意大的整数
- Number类型只能安全的支持 -(2^53-1) 到 2^53-1之间的整数,任何超过这个范围的数值都会失去精度
- 创建BigInt类型,方法如下:
- (1)直接在整数的末尾追加n
- (2)直接调用BigInt()构造函数
- (3)注:BigInt除了不能使用一元加号运算符外,其他的运算符都可以使用;并且 BigInt和Number之间不能进行混合操作
const a = 124n;
const b = BigInt(123);
const c = BigInt(1.1); // 报错,BigInt 类型,首先必须是个int
typeof a // "bigint"
typeof b // "bigint"
console.log(b); // 输出值为 123n
124n + 1; // 报错 Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
+123n; // 报错 Uncaught TypeError: Cannot convert a BigInt value to a number at <anonymous>:1:1
123n + 123n; // 246n
123n === 123n // true
123n + "ab" // "123ab"
标签:Map,Set,set,map,Symbol,aSet,BigInt,new 来源: https://blog.csdn.net/rocktanga/article/details/120733198