Symbol
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。
基本用法
1.Symbol
函数栈不能用 new
命令,因为 Symbol
是原始数据类型,不是对象。
//使用 typeof,结果为 "symbol"
let s = Symbol();
console.log(typeof s);//symbol
//instanceof 的结果为 false
let s1 = Symbol("foo");
console.log(s1 instanceof Symbol);//false
2.可以接受一个字符串作为参数,为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分。
//可以接受一个字符串作为参数,表示对 Symbol 实例的描述
let s2 = Symbol('foo');
console.log(s1); // Symbol(foo)
3.如果 Symbol
的参数是一个对象,就会调用该对象的 toString
方法,将其转为字符串,然后才生成一个 Symbol
值。
// Symbol 的参数是一个对象
const obj = {
toString() {
return 'abc';
}
};
const sym = Symbol(obj);
console.log(sym); // Symbol(abc)
4.Symbol 函数的参数只是表示对当前 Symbol 值的描述,相同参数的 Symbol 函数的返回值是不相等的。
// 没有参数的情况
let s1 = Symbol();
let s2 = Symbol();
console.log(s1 === s2); // false
// 有参数的情况
let s1 = Symbol('foo');
let s2 = Symbol('foo');
console.log(s1 === s2); // false
使用场景
作为属性名
由于每一个 Symbol 的值都是不相等的,所以 Symbol 作为对象的属性名,可以保证属性不重名。
let mySymbol = Symbol();
//写法一
let a = {};
a[mySymbol] = "Hello";
//写法二
let a = {
[mySymbol]: "Hello",
};
//写法三
let a = {};
Object.defineProperty(a, mySymbol, {value: "Hello"});
console.log(a[mySymbol]); //Hello
Symbol 作为对象属性名时不能用.
运算符,要用方括号
。因为.
运算符后面是字符串,所以取到的是字符串 mySymbol 属性,而不是 Symbol 值 mySymbol 属性。
注意: Symbol 作为属性名,该属性不会出现在 for...in
、for...of
循环中,也不会被 Object.keys()
、Object.getOwnPropertyNames()
、JSON.stringify()
返回。但是,它也不是私有属性,有一个 Object.getOwnPropertySymbols
方法,可以获取指定对象的所有 Symbol 属性名。
定义常量
使用 Symbol 定义常量,这样就可以保证这一组常量的值都不相等。
const COLOR_RED = Symbol("red");
const COLOR_YELLOW = Symbol("yellow");
const COLOR_BLUE = Symbol("blue");
Symbol.for() 和 Symbol.keyFor()
如果我们希望使用同一个 Symbol 值,可以使用 Symbol.for。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。
let s1 = Symbol.for("foo");
let s2 = Symbol.for("foo");
console.log(s1); //Symbol(foo)
console.log(s2); //Symbol(foo)
console.log(s1 === s2); //true
Symbol.keyFor 方法返回一个已登记的 Symbol 类型值的 key。
let s1 = Symbol.for("foo");
let s2 = Symbol.for("foo");
console.log(Symbol.keyFor(s1)); //foo
console.log(Symbol.keyFor(s2));//foo
set
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
初始化
Set 本身是一个构造函数,用来生成 Set 数据结构。
Set 函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
let set = new Set([1, 2, 3, 4]);
console.log(set);
set = new Set(document.querySelectorAll('div'));
console.log(set.size); // 66
set = new Set(new Set([1, 2, 3, 4]));
console.log(set.size); // 4
属性和方法
属性 | 描述 |
---|---|
Set.prototype.constructor | 构造函数,默认就是 Set 函数。 |
Set.prototype.size | 返回 Set 实例的成员总数。 |
操作方法 | 描述 |
---|---|
add(value) | 添加某个值,返回 Set 结构本身。 |
delete(value) | 删除某个值,返回一个布尔值,表示删除是否成功。 |
has(value) | 返回一个布尔值,表示该值是否为 Set 的成员。 |
clear() | 清除所有成员,无返回值。 |
let set = new Set();
console.log(set.add(1).add(2)); // Set { 1, 2 }
console.log(set.delete(2)); // true
console.log(set.has(2)); // false
console.log(set); // Set { 1 }
console.log(set.clear()); // undefined
console.log(set.has(1)); // false
console.log(set); // Set {}
遍历方法 | 描述 |
---|---|
keys() | 返回键名的遍历器 |
values() | 返回键值的遍历器 |
entries() | 返回键值对的遍历器 |
forEach() | 使用回调函数遍历每个成员,无返回值 |
注意 keys()、values()、entries() 返回的是遍历器
let set = new Set(["a", "b", "c"]);
console.log(set.keys());//[Set Iterator] { 'a', 'b', 'c' }
console.log([...set.keys()]);//[ 'a', 'b', 'c' ]
console.log(set.values()); //[Set Iterator] { 'a', 'b', 'c' }
console.log([...set.values()]); //[ 'a', 'b', 'c' ]
console.log(set.entries());//[Set Entries] { [ 'a', 'a' ], [ 'b', 'b' ], [ 'c', 'c' ] }
console.log([...set.entries()]);//[ [ 'a', 'a' ], [ 'b', 'b' ], [ 'c', 'c' ] ]
set.forEach((value, key) => {
console.log(key + " " + value);
});
// a a
// b b
// c c
Set 对象作用
数组去重
let mySet = new Set([1, 2, 3, 4]);
console.log([...mySet]);//[ 1, 2, 3, 4 ]
并集
let set1 = new Set([1, 2, 3]);
let set2 = new Set([2, 3, 4]);
let union = new Set([...set1, ...set2]);
console.log(union); //Set { 1, 2, 3, 4 }
交集
let set1 = new Set([1, 2, 3]);
let set2 = new Set([2, 3, 4]);
let intersect = new Set([...set1].filter(x => set2.has(x)));
console.log(intersect);//Set { 2, 3 }
差集
let set1 = new Set([1, 2, 3]);
let set2 = new Set([2, 3, 4]);
let intersect = new Set([...set1].filter(x => !set2.has(x)));
console.log(intersect);//Set { 1 }
WeakSet
1.只接受对象作为键名
2.只有add()、delete()、clear()、has()三个方法,不能遍历,没有size属性等
3.WeakMap 的键名所引用的对象是弱引用
即垃圾回收机制不将该引用考虑在内。只要所引用的对象的其他引用都被清除,垃圾回收机制就会释放该对象所占用的内存。也就是说,一旦不再需要,WeakMap 里面的键名对象和所对应的键值对会自动消失,不用手动删除引用。
4.WeakSet主要用来储存DOM节点,当这些节点从文档移除时,不会引发内存泄漏。
Map
Map 对象保存键值对。任何值(对象或者原始值) 都可以作为一个键或一个值。
Map 和 Object 的区别
一个Object
的键只能是字符串或者Symbol
,但一个Map
的键可以是任意值。Map
中的键值是有序的(FIFO 原则),而添加到对象中的键则不是。Map
的键值对个数可以从 size
属性获取,而 Object
的键值对个数只能手动计算。Object
都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
Map中的key
key 是 NaN
虽然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),NaN作为Map的键来说是没有区别的。
let myMap = new Map();
myMap.set(NaN, "not a number");
console.log(myMap.get(NaN)); //not a number
let otherNaN = Number("foo");
console.log(myMap.get(otherNaN)); //not a number
Map的迭代
对 Map 进行遍历,有如下两个方法:
for...of
forEach()
Map对象的操作
Map 与 Array的转换
let kvArray = [["key1", "value1"], ["key2", "value2"]];
// Map 构造函数可以将一个 二维 键值对数组转换成一个 Map 对象
let myMap = new Map(kvArray);
console.log(myMap);//Map { 'key1' => 'value1', 'key2' => 'value2' }
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维键值对数组
let outArray = Array.from(myMap);
console.log(outArray); //[ [ 'key1', 'value1' ], [ 'key2', 'value2' ] ]
WeakMap
- WeakMap 只接受对象作为键名
- WeakMap 的键名所引用的对象是弱引用
- WeakMap 不像 Map,一是没有遍历操作(即没有keys()、values()和entries()方法),也没有 size 属性,也不支持 clear 方法,所以 WeakMap只有四个方法可用:get()、set()、has()、delete()。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。