js ES6新增数据类型Set/Map/Symbol
Set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构。
Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化
// 例一
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
// 例三
const set = new Set(document.querySelectorAll('div'));
set.size // 56
// 类似于
const set = new Set();
document
.querySelectorAll('div')
.forEach(div => set.add(div));
set.size // 56
Set中没有重复的值,里面包含了各个item和一个size属性
let set = new Set([1, 1, 2, 2, 3, 3])
console.log(set);

基于这一特性,我们可以进行去重处理
// 去除数组的重复成员
[...new Set([1, 2, 3, 3, 3])]
// [1, 2, 3]
[...new Set('ababbc')].join('')
// "abc"vxcx
注意,两个对象是不相等的,而NaN,null,undefined都视为相等的,Set内部判断两个值是否不同,使用的算法叫做Same-value-zero equality,它类似于精确相等运算符(===)
let set1 = new Set();
set1.add(NaN).add(NaN);
console.log(set1); // Set结构 {NaN}
let set2 = new Set();
set2.add(null).add(null);
console.log(set2); // Set结构 {null}
let set3 = new Set();
set3.add(undefined).add(undefined);
console.log(set3); // Set结构 {undefined}
let set4 = new Set();
set4.add({}).add({});
console.log(set4); // Set结构 {{}, {}}
Set数据结构的key和value是相同的;
for (let item of colorSet.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of colorSet.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of colorSet.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Set结构的实例属性和方法
Set结构的实例有以下属性
Set.prototype.constructor构造函数,默认就是Set函数。Set.prototype.size返回Set实例的成员总数。
Set实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
Set.prototype.add(value)添加某个值,返回 Set 结构本身。Set.prototype.delete(value)删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value)返回一个布尔值,表示该值是否为Set的成员。Set.prototype.clear()清除所有成员,没有返回值。
Set的遍历
keys(),values(),entries()
for (let item of colorSet.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of colorSet.values()) {
console.log(item);
}
// red
// green
// blue
for (let item of colorSet.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
forEach()Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
Set结合遍历的应用
数组的map和filter方法也可以间接用于Set。
let set = new Set([1, 2, 3]);
set = new Set([...set].map(x => x * 2));
// 返回Set结构:{2, 4, 6}
let set = new Set([1, 2, 3, 4, 5]);
set = new Set([...set].filter(x => (x % 2) == 0));
// 返回Set结构:{2, 4}
使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}
// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}
// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}
Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比 Object更合适。
Map构造函数传入的参数一定要是一个可迭代对象;
let map = new Map([['a', 1], ['b', 2]])
let map1 = new Map(new Set([['c', 3], ['d', 4]]))
console.log(map, map1);

也可以对一个map实例动态添加,任何数据类型都可以作为Map的key
let map = new Map();
map.set(1, '1')
map.set(Symbol(), 'hello')
map.set({ key: true }, 'value')
map.set(false, 'false')
console.log(map);
console.log(map.has(false)); // true
console.log(map.has(Symbol())); // false,注意这里是false,因为symbol很特殊,其值是唯一的,即使两个symbol声明方式一样,他们也是两个不同的数据
console.log(map.get(1)); // 1
console.log(map.get(Symbol())); // undefined 原因和上面相同

Map实例的属性和操作方法
sizesize属性返回Map结构的成员总数。Map.prototype.set(key, value)set方法设置键名key对应的键值为value,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。Map.prototype.get(key)get方法读取key对应的键值,如果找不到key,返回undefined。Map.prototype.has(key)has方法返回一个布尔值,表示某个键是否在当前Map对象之中。Map.prototype.delete(key)delete方法删除某个键,返回true。如果删除失败,返回false。Map.prototype.clear()clear方法清除所有成员,没有返回值。
WeakSet & WeakMap
Symbol
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined、null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。
Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
新建一个symbol数据不能使用new的方式,因为new出来的是一个对象,而symbol并不是对象;
Symbol()可以传一个字符串作为标识,这里的字符串仅作为标识,用于区分不同的symbol,因为默认symbol打印出来就是Symbol(),无法正常区分
let sym = Symbol('sym');
let sym1 = Symbol('sym1');
当symbol作为属性名是,需要使用[]包起来,否则会认为sym是字符串而不是symbol数据
let sym = Symbol('sym');
let sym1 = Symbol('sym1');
let obj = {
[sym]: sym1
}
// 或者,注意下面的sym没有引号
let obj1 = {};
obj1[sym] = sym1
可以使用description属性打印出symbol的标识
console.log(sym.description); // 'sym'
console.log(sym1.description); // 'sym1'
即使标识符相同,也是两个不同的数据
let s1 = Symbol('aaa');
let s2 = Symbol('aaa');
console.log(s1 === s2); // false
symbol还可以作为属性值,这能确保属性值都是唯一的,且不关心值到底是什么
let obj2 = {
red: Symbol('red'),
yellow: Symbol('yellow'),
blue: Symbol('blue'),
};
使用Symbol作为对象的属性名,可以隐藏某些属性
let jerry = { name: 'jerry', age: 18, [Symbol('salary')]: 1000 }
let json = JSON.stringify(jerry) // 这里不会转化Symbol的属性
console.log(json); // {"name":"jerry","age":18}
获取到的属性名也没有包含Symbol的
let props = Object.getOwnPropertyNames(jerry)
console.log(props); // ['name', 'age']
要获取Symbol属性名,需要使用Object.getOwnPropertySymbols方法
let symbolProps = Object.getOwnPropertySymbols(jerry)
console.log(symbolProps); // [Symbol(salary)]
console.log(jerry[symbolProps[0]]); // 1000
使用Reflect.ownKeys可以获取到所有的属性名,包括Symbol的
let allProps = Reflect.ownKeys(jerry)
console.log(allProps); // ["name", "age", Symbol(salary)]
Symbol值不能与其他类型的值进行运算,会报错
let sym = Symbol('My symbol');
"your symbol is " + sym
// TypeError: can't convert symbol to string
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
但是,Symbol 值可以显式转为字符串。
let sym = Symbol('My symbol');
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
另外,Symbol 值也可以转为布尔值,但是不能转为数值。
let sym = Symbol();
conole.log(Boolean(sym)) // true
conole.log(!sym) // false
if (sym) {
// ...
}
Number(sym) // TypeError
sym + 2 // TypeError
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 289211569@qq.com