现行的编程语言都会提供几种类型的数据集合支持,在 ES6 之前,JavaScript 仅提供了对数组的支持.在以数组和对象为编程主力的 JavaScript 语言,ES6 中引入了 4 种新的数据结构,分别是:集合(Set),若集合(WeakSet),映射(Map),若映射(WeakMap).下面,我们看一下它们各自的表现方式吧.
一,Set
1,概述
Set 对象是值的集合,可以按照插入的顺序迭代它的元素.Set 中的元素只会出现一次,即 Set 中的元素是唯一的.
使用方式: new Set([iterable]);
参数 iterable :是一个可迭代的对象,它的所有元素将被添加到新的 Set 中.
由于 Set 中的值总是唯一的,所以需要判断两个值是否相等.在对象的扩展章节中,我们讲到可以通过 Object.is() 来判断是否严格相等,在 Set 中,-0 和 +0 是两个不同的值,NaN 和 undefined 是可以被存储在 Set 中的,因为 NaN 在 ES6 中是严格相等的.
new Set([NaN, NaN, 2, 3, 5, 5]); // Set(4) {NaN, 2, 3, 5}
2,属性
length 属性:
Set.length; // 0
Set.prototype:表示 Set 构造器的原型,允许想所有 Set 对象添加新的属性.
Set.prototype.constructor:返回实例的构造函数,默认是 Set.
Set.prototype.size:返回 Set 对象的值的个数.
var mySet1 = new Set([NaN, NaN, 2, 3, 5, 5]);
mySet1.size; // 4
3,方法
(1),在 Set 对象尾部添加一个元素:Set.prototype.add(value)
var mySet2 = new Set([NaN, NaN, 2, 3, 5, 5]);
mySet2.add(1);
mySet2.add(1).add("undefined");
console.log(mySet2); // Set(6) {NaN, 2, 3, 5, 1,"undefined" }
(2),清楚 Set 中所有的元素:Set.prototype.clear()
(3),判断值是否存在于 Set 中:Set.prototype.has(value);
var mySet3 = new Set();
mySet3.add("yuan");
mySet3.add("monkey");
mySet3.has("yuan"); // true
mySet3.clear();
mySet3.has("yuan"); // false
(4),删除 Set 中的某个值: Set.prototype.delete(value);
var mySet = new Set();
mySet.add("foo");
mySet.delete("bar"); // 返回 false,不包含 "bar" 这个元素
mySet.delete("foo"); // 返回 true,删除成功
mySet.has("foo"); // 返回 false,"bar" 已经成功删除
(5)遍历 Set 对象中的元素:forEach(),keys(),values(),entries()
forEach:
function logSetElements(value1, value2, set) {
console.log("s[" + value1 + "] = " + value2);
}
new Set(["foo", "bar", undefined]).forEach(logSetElements);
// "s[foo] = foo"
// "s[bar] = bar"
// "s[undefined] = undefined"
keys():
var mySet = new Set();
mySet.add('foo');
mySet.add('bar');
mySet.add('baz');
var setIter = mySet.keys();
console.log(setIter.next().value); // "foo"
console.log(setIter.next().value); // "bar"
console.log(setIter.next().value); // "baz"
values():
var mySet = new Set();
mySet.add('foo');
mySet.add('bar');
mySet.add('baz');
var setIter = mySet.values();
console.log(setIter.next().value); // "foo"
console.log(setIter.next().value); // "bar"
console.log(setIter.next().value); // "baz"
entries():
var mySet = new Set();
mySet.add('foo');
mySet.add('bar');
mySet.add('baz');
var setIter = mySet.entries();
console.log(setIter.next().value); // ["foo", "foo"]
console.log(setIter.next().value); // ["foo", "foo"]
console.log(setIter.next().value); // ["baz", "baz"]
4,应用
(1)Array 与 Set 相互转换
var myArray = ["value1", "value2", "value3"];
// 用Set构造器将Array转换为Set
var mySet = new Set(myArray);
mySet.has("value1"); // returns true
// 用...(扩展运算符)操作符将Set转换为Array
console.log([...mySet]); // 与myArray完全一致
(2),数组去重
// 用数组静态方法 Array.from
let array1 = Array.from(new Set([1, 1, 1, 2, 3, 2, 4]));
console.log(array1);
// => [1, 2, 3, 4]
// 或用扩展运算符(...)
let array2 = [...new Set([1, 1, 1, 2, 3, 2, 4])];
console.log(array2);
// => [1, 2, 3, 4]
(3),字符串转成 Set
var text = "yuan";
var stringSet = new Set(text);
console.log(sringSet); // Set(4) {"y", "u", "a", "n"
stringSet.size; //
(4),实现并集,交集,差集
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}
// 差集
let difference = new Set([...a].filter (x => !b.has(x))); // Set { 1 }
二,WeakSet
1,含义
WeakSet 结构与 Set 结构类似,WeakSet 是一个构造函数,可以使用 new 命令创建 WeakSet 数据结构.
const a= ["yuan", "monkey"];
const myWeakSet = new WeakSet(a); // WeakSet {"yuan", "monkey" }
2,与 Set 区别
(1),WeakSet 的成员只能是对象,而不能是其他类型的值.
(2),WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用.
(3),WeakSet 没有 size 属性,没有办法遍历其成员.
3,方法
(1),WeakSet.prototype.add(value):添加新成员;
(2),WeakSet.prototype.delete(value):清楚指定成员;
(3),WeakSet.prototype.has(value):判断是否存在某个成员.
三,Map
1,概述
Map 对象保存键值对.任何值 (对象或者原始值) 都可以作为一个键或一个值.
一个 Map 对象以插入顺序迭代其元素 - 一个 for...of 循环为每次迭代返回一个 [key,value] 数组.
使用方式:new Map([iterable])
参数 iterable:可以是一个数组或者其它的 iterable 对象,其元素或为键值对,或为两个元素的数组.
2,属性
(1),length 属性
Map.length; // 0
(2),Map.prototype.constructor
返回一个函数,创建了实例的原型,默认是 Map 函数.
(3)Map.prototype.size
返回对象的键值对的数量:
var myMap = new Map();
myMap.set("a", "alpha");
myMap.set("b", "beta");
myMap.set("g", "gamma");
myMap.size; // 3
3,方法
(1),Map.prototype.set(key, value)
设置 Map 对象中键的值.返回该 Map 对象.
(2),Map.prototype.clear()
移除 Map 对象的所有键 / 值对 .
(3),Map.prototype.has(key)
返回一个布尔值,表示 Map 实例是否包含键对应的值.
var myMap = new Map();
myMap.set("bar", "baz");
myMap.set(1, "foo");
myMap.size; // 2
myMap.has("bar"); // true
myMap.clear();
myMap.size; // 0
myMap.has("bar") // false
(4)Map.prototype.delete(key)
移除任何与键相关联的值,并且返回该值,该值在之前会被
var myMap = new Map();
myMap.set("bar", "foo");
myMap.delete("bar"); // 返回 true.成功地移除元素
myMap.has("bar"); // 返回 false."bar" 元素将不再存在于 Map 实例中
(5),Map.prototype.entries()
返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的 [key, value] 数组.
(6),Map.prototype.keys()
返回一个新的 Iterator 对象, 它按插入顺序包含了 Map 对象中每个元素的键 .
(6),Map.prototype.values()
返回一个新的 Iterator 对象,它按插入顺序包含了 Map 对象中每个元素的值
var myMap = new Map();
myMap.set("0", "foo");
myMap.set(1, "bar");
myMap.set({}, "baz");
var mapIter1 = myMap.entries();
var mapIter 2= myMap.keys();
var mapIter3 = myMap.values();
console.log(mapIter1.next().value); // ["0", "foo"]
console.log(mapIter1.next().value); // [1, "bar"]
console.log(mapIter1.next().value); // [Object, "baz"]
console.log(mapIter2.next().value); // "0"
console.log(mapIter2.next().value); // 1
console.log(mapIter2.next().value); // Object
console.log(mapIter3.next().value); // "foo"
console.log(mapIter3.next().value); // "bar"
console.log(mapIter3.next().value); // "baz"
(7),Map.prototype.forEach(callbackFn[, thisArg])
按插入顺序,为 Map 对象里的每一键值对调用一次 callbackFn 函数.如果为 forEach 提供了 thisArg,它将在每次回调中作为 this 值.
function logMapElements(value, key, map) {
console.log("m[" + key + "] = " + value);
}
Map([["foo", 3], ["bar", {}], ["baz", undefined]]).forEach(logMapElements);
// logs:
// "m[foo] = 3"
// "m[bar] = [object Object]"
// "m[baz] = undefined"
(8),Map.prototype.get(key)
返回键对应的值,如果不存在,则返回 undefined.
var myMap = new Map();
myMap.set("bar", "foo");
myMap.get("bar"); // 返回 "foo".
myMap.get("baz"); // 返回 undefined.
4,应用
(1),Map 与 数组之间的相互转换
// Map 转数组
var myMap = new Map();
myMap.set("bar", "foo");
myMap.set(1, "bar");
[...myMap]; // [ ["bar", "foo"], [1, "bar"] ]
// 数组转Map
const arr = new Map( [ ["bar", "foo"], [1, "bar"] ]);
console.log(arr); // Map {"bar" => "foo", 1 => "bar"
(2),Map 与对象相互转换
// Map 转对象
function strMapToObj(strMap) {
let obj = Object.create(null);
for (let [k, v] of strMap) {
obj[k] = v;
}
return obj;
}
const myMap = new Map();
myMap.set("bar", "foo")
.set(1, "ooo");
strMapToObj(myMap ); // Object {1: "ooo", bar: "foo"
// 对象转 Map
function objToStrMap(obj) {
let strMap = new Map();
for (let k of Object.keys(obj)) {
strMap.set(k, obj[k]);
}
return strMap;
}
objToStrMap({1: "ooo", bar: "foo"); // Map {"1" => "ooo", "bar" => "foo"
(3),Map 与 JSON 相互转换
// Map 转 JSON
// Map 的键名为字符串
function strMapToJson(jsonStr) {
return JSON.stringify(strMapToObj(jsonStr));
}
const myMap = new Map();
myMap.set("bar", "foo")
.set(1, "ooo");
strMapToJson(myMap); // "{"1":"ooo","bar":"foo"}"
// Map 的键名为非字符串
function mapToArrayJson(map) {
return JSON.stringify([...map]);
}
mapToArrayJson(myMap); // "[["bar","foo"],[1,"ooo"]]"
// Json 转 Map
// 正常情况下所有键名都为字符串
function jsonToStrMap(jsonStr) {
return objToStrMap(JSON.parse(jsonStr));
}
jsonToStrMap("{"1":"ooo","bar":"foo"}"); // Map {"1" => "ooo", "bar" => "foo"
// 整个JSON 是数组
function jsonToMap(jsronStr) {
return new Map(JSON.parse(jsronStr));
}
jsonToMap([["bar","foo"],[1,"ooo"]]); // Map {"1" => "ooo", "bar" => "foo"
四,WeakMap
1,含义
WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合.
2,与 Map 区别
(1),WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名.
(2),WeakMap 的键名所指向的对象不计入垃圾回收机制.
(3),没有 keys(),values(),entries() 遍历操作.
(4),没有 size 属性.
(5),不支持 clear() 方法.
3,应用
(1),以 DOM 节点作为键名的场景应用
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();
myWeakmap.set(myElement, {timesClicked: 0});
myElement.addEventListener('click', function() {
let logoData = myWeakmap.get(myElement);
logoData.timesClicked++;
}, false);
(2),部署私有属性
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
const c = new Countdown(2, () => console.log('DONE'));
c.dec()
c.dec()
本章介绍了 Map 和 Set 数据结构,重点是要掌握两个数据结构的具体代表的含义,用法以及各数据结构与 Map 和 Set 之间的相互转换.下一章我们将介绍 ES6 中 Proxy(代理)和 Reflect(反射),尽情期待吧.
来源: http://www.jianshu.com/p/e35a4916377c