js ES6新增数据类型Set、Map、Symbol

  1. js ES6新增数据类型Set/Map/Symbol
  2. Set
    1. Set结构的实例属性和方法
    2. Set的遍历
  3. Map
    1. Map实例的属性和操作方法
  4. WeakSet & WeakMap
  5. Symbol

js ES6新增数据类型Set/Map/Symbol

本文参考阮一峰ES6基础教程

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

注意,两个对象是不相等的,而NaNnullundefined都视为相等的,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数据结构的keyvalue是相同的;

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的遍历

  1. 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"]
  1. forEach()
    Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
let set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
  1. Set结合遍历的应用
    数组的mapfilter方法也可以间接用于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结构实现。如果你需要“键值对”的数据结构,MapObject更合适。

Map构造函数传入的参数一定要是一个可迭代对象;

let map = new Map([['a', 1], ['b', 2]])
let map1 = new Map(new Set([['c', 3], ['d', 4]]))
console.log(map, map1);

也可以对一个map实例动态添加,任何数据类型都可以作为Mapkey

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

WeakSet

WeakMap

Symbol

ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefinednull、布尔值(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