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
实例的属性和操作方法
size
size
属性返回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