ES6新标准中将Set集合和Map集合添加到JS中。

ES5中Set集合和Map集合

在ES5中,开发者们用对象属性来模拟这两种集合。

var set = Object.create(null);

set.foo = true;

// 检查属性是否存在
if (set.foo) {
// ...
}

模拟Map集合同上例类似。

如果只是简单的应用上面的方法基本上能满足需求,但是如果碰到对象属性名的限制,就会产生一些问题。

var map = Object.create(null);
map[5] = "foo";
console.log(map["5"]); // "foo"
console.log(map); // {5: "foo"}

虽然map中存储的属性键名是数值型的5,但 map["5"] 引用的是同一个属性。

用对象作为键名也有类似的问题。

var map = Object.create(null),
key1 = {},
key2 = {};
map[key1] = "foo";
console.log(map[key2]); // "foo"
console.log(map); // {[object Object]: "foo"}

上例中 map[key1] 和 map[key2] 也引用了同一个值。因为 key1 和 key2 会被转换为对象的默认字符串 "[object Object]"。

ES6中的Set集合

ES6中新增的Set集合是一种有序列表,其中还有一些相互独立的非重复值,通过Set集合可以快速访问其中的数据,更有效地追踪各种离散值。

创建Set集合并添加元素

let set = new Set();
set.add(5);
set.add("5"); console.log(set.size); // 2
console.log(set); // Set(2) {5, "5"}

向Set集合中添加对象

let set = new Set(),
key1 = {},
key2 = {}; set.add(key1);
set.add(key2); console.log(set.size); // 2
console.log(set); // Set(2) {{…}, {…}}

如果多次传入相同的值,后面的调用实际上会被忽略。

let set = new Set();
set.add(5);
set.add("5");
set.add(5); // 重复 - 本次调用直接被忽略 console.log(set.size); // 2
console.log(set); // Set(2) {5, "5"}

使用数组来初始化Set集合,并且同时可以出去重复的元素。

let set = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
console.log(set.size); // 5
console.log(set); // Set(5) {1, 2, 3, 4, 5}

Note

实际上,Set构造函数可以接受所有可迭代对象作为参数,数组、Set集合、Map集合都是可迭代的,因而都可以作为Set构造函数的参数使用;构造函数通过迭代器从参数中提取值。

通过 has() 方法可以检测Set集合中是否存在某个值

let set = new Set();
set.add(5);
set.add("5");
console.log(set.has(5)); // true
console.log(set.has(6)); // false

移除元素

delete() 方法可以移除Set集合中的某一个元素;

clear() 方法可以移除集合中所有元素。

let set = new Set();
set.add(5);
set.add("5"); console.log(set.has(5)); // true
console.log(set.size); // 2 set.delete(5); console.log(set.has(5)); // false
console.log(set.size); // 1 set.clear(); console.log(set.has("5")); // false
console.log(set.size); // 0

Set集合的forEach()方法

forEach() 方法的回调函数接受以下3个参数:

  1. Set集合中下一次索引的位置
  2. 与第一个参数一样的值
  3. 被遍历的Set集合本身

需要注意的是这里的第一和第二个参数是一样的。这是为了和数组和Map集合的forEach()方法统一。

let set = new Set([1, 2]);

set.forEach(function(value, key, ownerSet) {
console.log(key + " " + value);
console.log(ownerSet === set);
}); // 输出结果:
// 1 1
// 5 true
// 4 2 2
// 5 true

如果需要在回调函数中使用this引用,则要将this作为第二个参数传入forEach()函数。

let set = new Set([1, 2]);

let processor = {
output(value) {
console.log(value);
},
process(dataSet) {
dataSet.forEach(function(value) {
this.output(value);
}, this);
}
}; processor.process(set);

这里可以使用箭头函数,这样就可以不用再将this传入forEach()方法了。

let set = new Set([1, 2]);

let processor = {
output(value) {
console.log(value);
},
process(dataSet) {
dataSet.forEach(value => this.output(value));
}
}; processor.process(set);

将Set集合转换为数组

let set = new Set([1, 2, 3, 3, 3, 4, 5]),
array = [...set];
console.log(array); // [1, 2, 3, 4, 5]

使用Set集合实现数组的去重

function eliminateDuplicates(items) {
return [...new Set(items)];
} let numbers = [1, 2, 3, 3, 3, 4, 5],
noDuplicates = eliminateDuplicates(numbers); console.log(noDuplicates);

Weak Set 集合

弱引用的Set集合。用法同Set集合基本一样。

let set = new WeakSet(),
key = {}; // 向集合中添加对象
set.add(key); console.log(set.has(key)); // true set.delete(key); console.log(set.has(key)); // false

两种Set类型的主要区别

Weak Set保存的是对象值得弱引用。

当Weak Set中的某个对象的所有强引用都被移除的时候,该对象也会自动从Weak Set中移除。

let set = new WeakSet(),
key = {}; // 向集合中添加对象
set.add(key); console.log(set.has(key)); // true // 移除对象key的最后一个强引用(Weak Set中的引用也会自动移除)
key = null;

Weak Set 集合和普通 Set 集合的差别

  1. 在WeakSet的实例中,如果向 add()、has()和delete()这3个方法传入非对象参数都会导致程序报错;
  2. Weak Set 集合不可迭代,所以不能被用于 for-of 循环;
  3. Weak Set 集合不暴露任何迭代器(例如 keys() 和 values() 方法),所以无法通过程序本身来检测其中的内容;
  4. Weak Set 集合不支持 forEach() 方法;
  5. Weak Set 集合不支持 size 属性。

Weak Set 集合的功能看似受限,其实这是为了让它能够正确的处理内存中数据。

总之,如果你只需要跟踪对象引用,你更应该使用 Weak Set 集合而不是 Set 集合。

ES6中的 Map 集合

ES6中的 Map 类型是一种储存着许多键值对的有序列表,其中键名和对应的值支持所有的数据类型。

键名的等价判断是通过 Object.is() 方法实现的。

let map = new Map();
map.set("site", "liujiajia.me");
map.set("year", 2017); console.log(map.get("site")); // "liujiajia.me"
console.log(map.get("year")); // 2017

使用对象作为键名

let map = new Map,
key1 = {},
key2 = {}; map.set(key1, 9);
map.set(key2, 32); console.log(map.get(key1)); // 9
console.log(map.get(key2)); // 32

Map集合支持的方法

Map集合和Set集合有如下3个通用方法:

  • has(key)

    检测指定的键名在Map集合中是否存在
  • delete(key)

    从Map集合中移除指定键名及其对应的值
  • clear()

    移除Map集合中所有的键值对
let map = new Map();
map.set("name", "JiaJia");
map.set("age", 32); console.log(map.has("name")); // true
console.log(map.get("name")); // "JiaJia"
console.log(map.has("age")); // true
console.log(map.get("age")); // 32
console.log(map.size); // 2 map.delete("name");
console.log(map.has("name")); // false
console.log(map.get("name")); // undefined
console.log(map.size); // 1 map.clear();
console.log(map.has("name")); // false
console.log(map.get("name")); // undefined
console.log(map.has("age")); // false
console.log(map.get("age")); // undefined
console.log(map.size); // 0

Map集合的初始化方法

可以传入一个数组来初始化Map集合。

数组中的每一个元素都是一个子数组,子数组中包含一个键值对的键名和值两个元素。

let map = new Map([["name", "JiaJia"], ["age", 32]]);

console.log(map.has("name")); // true
console.log(map.get("name")); // "JiaJia"
console.log(map.has("age")); // true
console.log(map.get("age")); // 32
console.log(map.size); // 2

Map集合的forEach()方法

和数组的forEach()方法类似,回调函数都接受3个参数:

  • Map集合中下一次索引的位置
  • 值对应的键名
  • Map集合本身
let map = new Map([["name", "JiaJia"], ["age", 32]]);

map.forEach(function(value, key, ownerMap) {
console.log(key + " " + value);
console.log(ownerMap === map);
});
// 执行结果:
// name JiaJia
// true
// age 32
// true

同Set集合一样,可以指定forEach()方法的第二个参数作为回调函数的this值。

Weak Map 集合

Weak Map 是弱引用的 Map 集合,也用于存储对象的弱引用。

  • Weak Map 集合中的键名必须是对象;
  • 只有键名保存的是弱引用,键名对应的值如果是个对象,则保存的是该对象的强引用。

Weak Map 最大的用途是保存Web页面中的DOM元素。

let map = new WeakMap(),
element = document.querySelector(".element"); map.set(element, "Original"); let value = map.get(element);
console.log(value); // "Original" // 移除element元素
element.parentNode.removeChild(element);
element = null; // 此时 Weak Map 集合为空

私有对象数据

Weak Map 还可以用于存储对象实例的私有数据。

在ES6中所有属性都是公开的。如果想存储一些只对对象开放的数据,则需要一些创造力。

下例使用约定作为私有属性:

function Person(name) {
this._name = name;
} Person.prototype.getName = function() {
return this._name;
};

看似是只允许通过 getName() 方法获取name属性,但其实仍然可以通过给实例的 _name 属性赋值来更改该属性值。

let user = new Person("JiaJia")
console.log(user.getName()); // "JiaJia"
user._name = "Dlph";
console.log(user.getName()); // "Dlph"

在ES5中,可以通过下面这种模式创建一个对象接近真正的私有数据。

var Person = (function() {
var privateData = {},
privateId = 0; function Person(name) {
Object.defineProperty(this, "_id", { value: privateId++ }); privateData[this._id] = {
name: name
};
} Person.prototype.getName = function() {
return privateData[this._id].name;
} return Person;
}()); let user1 = new Person("JiaJia");
console.log(user1); // Person {_id: 0}
console.log(user1.getName()); // "JiaJia" var user2 = new Person("Dlph");
console.log(user2); // Person {_id: 1}
console.log(user2.getName()); // "Dlph"

上例中的 privateData 和 privateId 变量被隐藏了起来,在外面无法查看及更改。

该方法最大的问题是,如果不主动管理,由于无法获知对象实例何时被销毁,因此 privateData 中的数据就永远不会消失。而使用 Weak Map 集合就可以解决该问题。

let Person = (function() {
let privateData = new WeakMap(); function Person(name) {
privateData.set(this, { name });
} Person.prototype.getName = function() {
return privateData.get(this).name;
} return Person;
}()); let user1 = new Person("JiaJia");
console.log(user1); // Person {}
console.log(user1.getName()); // "JiaJia" let user2 = new Person("Dlph");
console.log(user2); // Person {}
console.log(user2.getName()); // "Dlph" user1 = null;
user2 = null;

使用 Weak Map 来存储私有数据,只要对象实例被销毁,相关信息也会被销毁,从而保证了信息的私有性。

Weak Map 集合的使用方式及使用限制

当要在 Weak Map 和 普通 Map 集合之间进行选择时,如果只用对象作为集合的键名,那么 Weak Map 是最好的选择。

相对于 Map 集合而言, Weak Map 集合对用户的可见度更低,其不支持通过 forEach()方法、size属性及clear()方法来管理集合中的元素。

如果只想使用非对象名作为键名,那么普通的Map集合是唯一的选择。

最新文章

  1. WCF入门教程(三)定义服务协定--属性标签
  2. BZOJ3172——[Tjoi2013]单词
  3. 在CSDN中添加友情连接
  4. 排序,求几个最值问题,输入n个整数,输出其中最小的k个元素。
  5. bzoj1221
  6. HDU 1864最大报销额(一维背包)
  7. Eclipse添加Web和java EE插件
  8. 正确使用volatile场景--状态标志
  9. Linux双网卡搭建NAT服务器之网络应用
  10. ThreadLocal深入剖析
  11. JavaScript match()方法和正则表达式match()
  12. 使php支持pdo_mysql
  13. (一)python 数据模型
  14. three.js 第二篇:场景 相机 渲染器 物体之间的关系
  15. 自学Linux Shell14.1-理解输入输出
  16. react native 之 Android物理返回键
  17. 4C - 七夕节
  18. python测试开发django-28.发送邮件send_mail
  19. OpenCV-bwLabel-实现图像连通组件标记与分析
  20. centos 7 mount usb hard disk(ntfs format)

热门文章

  1. NBUT 1217 Dinner
  2. oracle实现主键自增
  3. 「mysql优化专题」单表查询优化的一些小总结,非索引设计(3)
  4. .NET 绝对路径的配置
  5. spring boot利用swagger和spring doc生成在线和离线文档
  6. iOS masonry九宫格 单行 多行布局
  7. Swift3.0 UITextField
  8. java.lang基础数据类型boolean、char、byte、short、int、long、float、double (JDK1.8)
  9. boost::algorithm(字符串算法库)
  10. 第2章KNN算法笔记_函数classify0