Map 和 Set 就不说了,很常规的概念,不过Map和Object有点区别的,比如Map可以用一个object作为key。
这里重点说一下WeakMap。 WeakMap和Map主要有如下几个区别:
1. WeakMap 只能用Object作为key,不能用基本数据类型比如字符串作为key
2. WeakMap 中的key是弱引用
3. WeakMap 没有size
其中1和3非常好理解,重点就是第2点,也是WeakMap名字的由来。
无论任何GA,最重要的一点都是要通过对象引用来判断对象是否存活,不过Node中具体实现比较复杂,可以参见《深入浅出Nodejs》第五章。
Map 的一个最大弊端就是它会导致作为key的对象增加一个引用,因此导致GA无法回收这个对象,如果大量使用object作为Map的key会导致大量的内存泄露。
WeakMap就是为了解决这个问题,在WeakMap中对作为key的对象是一个弱引用,也就是说,GA在计算对象引用数量的时候并不会把弱引用计算进去。这样当一个对象除了WeakMap没有其他引用的时候就会被GA回收掉。
集合
/**
* ECMSCRIPT 6 already have a Set class implementation:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
* We will try to copy the same functionalities
* @constructor
*/
function Set() {
let items = {};
this.add = function(value){
if (!this.has(value)){
items[value] = value;
return true;
}
return false;
};
this.delete = function(value){
if (this.has(value)){
delete items[value];
return true;
}
return false;
};
this.has = function(value){
return items.hasOwnProperty(value);
//return value in items;
};
this.clear = function(){
items = {};
};
/**
* Modern browsers function
* IE9+, FF4+, Chrome5+, Opera12+, Safari5+
* @returns {Number}
*/
this.size = function(){
return Object.keys(items).length;
};
/**
* cross browser compatibility - legacy browsers
* for modern browsers use size function
* @returns {number}
*/
this.sizeLegacy = function(){
let count = 0;
for(let key in items) {
if(items.hasOwnProperty(key))
++count;
}
return count;
};
/**
* Modern browsers function
* IE9+, FF4+, Chrome5+, Opera12+, Safari5+
* @returns {Array}
*/
this.values = function(){
let values = [];
for (let i=0, keys=Object.keys(items); i<keys.length; i++) {
values.push(items[keys[i]]);
}
return values;
};
this.valuesLegacy = function(){
let values = [];
for(let key in items) {
if(items.hasOwnProperty(key)) {
values.push(items[key]);
}
}
return values;
};
this.getItems = function(){
return items;
};
this.union = function(otherSet){
let unionSet = new Set(); //{1}
let values = this.values(); //{2}
for (let i=0; i<values.length; i++){
unionSet.add(values[i]);
}
values = otherSet.values(); //{3}
for (let i=0; i<values.length; i++){
unionSet.add(values[i]);
}
return unionSet;
};
this.intersection = function(otherSet){
let intersectionSet = new Set(); //{1}
let values = this.values();
for (let i=0; i<values.length; i++){ //{2}
if (otherSet.has(values[i])){ //{3}
intersectionSet.add(values[i]); //{4}
}
}
return intersectionSet;
};
this.difference = function(otherSet){
let differenceSet = new Set(); //{1}
let values = this.values();
for (let i=0; i<values.length; i++){ //{2}
if (!otherSet.has(values[i])){ //{3}
differenceSet.add(values[i]); //{4}
}
}
return differenceSet;
};
this.subset = function(otherSet){
if (this.size() > otherSet.size()){ //{1}
return false;
} else {
let values = this.values();
for (let i=0; i<values.length; i++){ //{2}
if (!otherSet.has(values[i])){ //{3}
return false; //{4}
}
}
return true;
}
};
}
set2
let Set2 = (function () {
const items = new WeakMap();
class Set2 {
constructor () {
items.set(this, {});
}
add(value){
if (!this.has(value)){
let items_ = items.get(this);
items_[value] = value;
return true;
}
return false;
}
delete(value){
if (this.has(value)){
let items_ = items.get(this);
delete items_[value];
return true;
}
return false;
}
has(value){
let items_ = items.get(this);
return items_.hasOwnProperty(value);
}
clear(){
items.set(this, {});
}
size(){
let items_ = items.get(this);
return Object.keys(items_).length;
}
values(){
let values = [];
let items_ = items.get(this);
for (let i=0, keys=Object.keys(items_); i<keys.length; i++) {
values.push(items_[keys[i]]);
}
return values;
}
getItems(){
return items.get(this);
}
union(otherSet){
let unionSet = new Set();
let values = this.values();
for (let i=0; i<values.length; i++){
unionSet.add(values[i]);
}
values = otherSet.values();
for (let i=0; i<values.length; i++){
unionSet.add(values[i]);
}
return unionSet;
}
intersection(otherSet){
let intersectionSet = new Set();
let values = this.values();
for (let i=0; i<values.length; i++){
if (otherSet.has(values[i])){
intersectionSet.add(values[i]);
}
}
return intersectionSet;
}
difference(otherSet){
let differenceSet = new Set();
let values = this.values();
for (let i=0; i<values.length; i++){
if (!otherSet.has(values[i])){
differenceSet.add(values[i]);
}
}
return differenceSet;
};
subset(otherSet){
if (this.size() > otherSet.size()){
return false;
} else {
let values = this.values();
for (let i=0; i<values.length; i++){
if (!otherSet.has(values[i])){
return false;
}
}
return true;
}
};
}
return Set2;
})();
UsingSets
let set = new Set();
set.add(1);
console.log(set.values()); //outputs [1]
console.log(set.has(1)); //outputs true
console.log(set.size()); //outputs 1
set.add(2);
console.log(set.values()); //outputs [1, 2]
console.log(set.has(2)); //true
console.log(set.size()); //2
console.log(set.sizeLegacy()); //3
set.delete(1);
console.log(set.values()); //outputs [2]
set.delete(2);
console.log(set.values()); //outputs []
Operations
//--------- Union ----------
let setA = new Set();
setA.add(1);
setA.add(2);
setA.add(3);
let setB = new Set();
setB.add(3);
setB.add(4);
setB.add(5);
setB.add(6);
let unionAB = setA.union(setB);
console.log(unionAB.values());
//--------- Intersection ----------
let setA = new Set();
setA.add(1);
setA.add(2);
setA.add(3);
let setB = new Set();
setB.add(2);
setB.add(3);
setB.add(4);
let intersectionAB = setA.intersection(setB);
console.log(intersectionAB.values());
//--------- Difference ----------
let setA = new Set();
setA.add(1);
setA.add(2);
setA.add(3);
let setB = new Set();
setB.add(2);
setB.add(3);
setB.add(4);
let differenceAB = setA.difference(setB);
console.log(differenceAB.values());
//--------- Subset ----------
let setA = new Set();
setA.add(1);
setA.add(2);
let setB = new Set();
setB.add(1);
setB.add(2);
setB.add(3);
let setC = new Set();
setC.add(2);
setC.add(3);
setC.add(4);
console.log(setA.subset(setB));
console.log(setA.subset(setC));
UsingES6Set
let set = new Set();
set.add(1);
console.log(set.values()); //outputs @Iterator
console.log(set.has(1)); //outputs true
console.log(set.size); //outputs 1
set.add(2);
console.log(set.values()); //outputs [1, 2]
console.log(set.has(2)); //true
console.log(set.size); //2
set.delete(1);
console.log(set.values()); //outputs [2]
set.delete(2);
console.log(set.values()); //outputs []
let setA = new Set();
setA.add(1);
setA.add(2);
setA.add(3);
let setB = new Set();
setB.add(2);
setB.add(3);
setB.add(4);
//--------- Union ----------
let unionAb = new Set();
for (let x of setA) unionAb.add(x);
for (let x of setB) unionAb.add(x);
console.log(unionAb);
//--------- Intersection ----------
let intersection = function(setA, setB){
let intersectionSet = new Set();
for (let x of setA){
if (setB.has(x)){
intersectionSet.add(x);
}
}
return intersectionSet;
};
let intersectionAB = intersection(setA, setB);
console.log(intersectionAB);
//alternative - works on FF only
//intersectionAb = new Set([x for (x of setA) if (setB.has(x))]);
//console.log(intersectionAB);
//--------- Difference ----------
let difference = function(setA, setB){
let differenceSet = new Set();
for (let x of setA){
if (!setB.has(x)){
differenceSet.add(x);
}
}
return differenceSet;
};
let differenceAB = difference(setA, setB);
console.log(differenceAB);
//alternative - works on FF only
//differenceAB = new Set([x for (x of setA) if (!setB.has(x))]);
//console.log(differenceAB);