本节是第四讲的第二十七小节,上一节我们为大家介绍了Symbol类型、Date对象、Error对象、RegExp对象,本节将为大家介绍JavaScript的数据结构Map、Set、WeakMap、WeakSet等。
JavaScript数据结构
数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。JavaScript的常用的数据结构Array, Object,结构型数据:JSON,其它数据结构:Map, Set, WeakMap , WeakSet。
Map对象
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
new Map([iterable])
//Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, 'one' ],[ 2, 'two' ]])。每个键值对都会添加到新的 Map。null 会被当做 undefined。
例如:let myMap = new Map([[1, 'one'],[2, 'two']]);//myMap.get(1)
let myMap = new Map();
let keyObj = {};
let keyFunc = function() {};
let keyString = 'a string';
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键keyObj关联的值");
myMap.set(keyFunc, "和键keyFunc关联的值");
myMap.size; // 3
// 读取值
myMap.get(keyString); // "和键'a string'关联的值"
myMap.get(keyObj); // "和键keyObj关联的值"
myMap.get(keyFunc); // "和键keyFunc关联的值"
let myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
Objects 和 maps 的比较
Objects 和 Maps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps 使用。不过 Maps 和 Objects 有一些重要的区别,在下列情况里使用 Map 会是更好的选择:
意外的键:Map 默认情况不包含任何键。只包含显式插入的键。一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。
键的类型:一个 Map的键可以是任意值,包括函数、对象或任意基本类型。一个Object 的键必须是一个 String 或是Symbol。
键的顺序:Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。一个 Object 的键是无序的
Size:Map的键值对个数可以轻易地通过size 属性获取Object 的键值对个数只能手动计算
迭代:Map 是 iterable 的,所以可以直接被迭代。迭代一个Object需要以某种方式获取它的键然后才能迭代。
性能:在频繁增删键值对的场景下表现更好。在频繁添加和删除键值对的场景下未作出优化。
Map对象常用属性和方法
Map.size//返回Map对象的键/值对的数量。
Map.clear()//移除Map对象的所有键/值对 。
Map.delete(key)//如果 Map 对象中存在该元素,则移除它并返回 true;否则如果该元素不存在则返回 false。随后调用 Map.prototype.has(key) 将返回 false 。
Map.entries()//返回一个新的 Iterator 对象,它按插入顺序包含了Map对象中每个元素的 [key, value] 数组。
Map.get(key)//返回键对应的值,如果不存在,则返回undefined。
Map.has(key)//返回一个布尔值,表示Map实例是否包含键对应的值。
Map.keys()//返回一个新的 Iterator对象, 它按插入顺序包含了Map对象中每个元素的键 。
Map.set(key, value)//设置Map对象中键的值。返回该Map对象。
Map.values()//返回一个新的Iterator对象,它按插入顺序包含了Map对象中每个元素的值 。
let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (let [key, value] of myMap) {
console.log(key + " = " + value);//将会显示两个log。一个是"0 = zero"另一个是"1 = one"
}
for (let key of myMap.keys()) {
console.log(key); //// 将会显示两个log。一个是 "0" 另一个是 "1"
}
for (let value of myMap.values()) {
console.log(value);
}
// 将会显示两个log。一个是 "0 = zero" 另一个是 "1 =one"
for (let [key, value] of myMap.entries()) {
console.log(key + " = " + value);
}
//将会显示两个logs。一个是 "0 = zero" 另一个是 "1 = one"
myMap.forEach(function(value, key) {
console.log(key + " = " + value);
})
Set对象
Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。Set对象是值的集合,你可以按照插入的顺序迭代它的元素。Set中的元素只会出现一次,即 Set 中的元素是唯一的。另外,NaN和undefined都可以被存储在Set 中, NaN之间被视为相同的值(NaN被认为是相同的,尽管 NaN !== NaN)。
new Set([iterable])
//Iterable 如果传递一个可迭代对象,它的所有元素将不重复地被添加到新的 Set中。如果不指定此参数或其值为null,则新的 Set为空。
let myArray = ["value1", "value2", "value3"];
// 用Set构造器将Array转换为Set
let mySet = new Set(myArray);
mySet.has("value1"); // returns true
// 用...(展开操作符)操作符将Set转换为Array
console.log([...mySet]); // 与myArray完全一致
let text = 'India';
let mySet = new Set(text); // Set {'I', 'n', 'd', 'i', 'a'}
mySet.size; // 5
// 大小写敏感
new Set("Firefox") // Set(7) [ "F", "i", "r", "e", "f", "o", "x" ]
new Set("hello") // Set(4) [ "h", "e", "l", "o" ]
let mySet = new Set();
mySet.add(1); // Set [ 1 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add(5); // Set [ 1, 5 ]
mySet.add("some text"); // Set [ 1, 5, "some text" ]
let o = ;
mySet.add(o);
mySet.has(1); // true
mySet.has(3); // false
mySet.has(5); // true
mySet.has(Math.sqrt(25)); // true
mySet.has("Some Text".toLowerCase()); // true
mySet.has(o); // true
mySet.size; // 5
Set对象常用属性及方法
Set.size //返回 Set 对象中的值的个数
Set.add(value) //在Set对象尾部添加一个元素。返回该Set对象。
Set.clear() //移除Set对象内的所有元素。
Set.delete(value) //移除Set的中与这个值相等的元素,返回Set.prototype.has(value)在这个操作前会返回的值。
Set.entries() //返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值的[value, value]数组。
Set.has(value) //返回一个布尔值,表示该值在Set中存在与否。
Set.keys() //与values()方法相同,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
Set.values() //返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。
Set对象迭代
for (let item of mySet) console.log(item); // 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let item of mySet.keys()) console.log(item); // 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let item of mySet.values()) console.log(item); // 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
for (let [key, value] of mySet.entries()) console.log(key);// 按顺序输出:1, "some text", {"a": 1, "b": 2}, {"a": 1, "b": 2}
// 用forEach迭代
mySet.forEach(function(value) {
console.log(value);
});
WeakMap对象
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
在 JavaScript 里,map API 可以通过使其四个 API 方法共用两个数组(一个存放键,一个存放值)来实现。给这种 map 设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该 map 取值的时候,需要遍历所有的键,然后使用索引从存储值的数组中检索出相应的值。
但这样的实现会有两个很大的缺点,首先赋值和搜索操作都是 O(n) 的时间复杂度( n 是键值对的个数),因为这两个操作都需要遍历全部整个数组来进行匹配。另外一个缺点是可能会导致内存泄漏,因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。
相比之下,原生的 WeakMap 持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行。原生 WeakMap 的结构是特殊且有效的,其用于映射的 key 只有在其没有被回收时才是有效的。
正由于这样的弱引用,WeakMap 的 key 是不可枚举的 (没有方法能给出所有的 key)。如果key 是可枚举的话,其列表将会受垃圾回收机制的影响,从而得到不确定的结果。因此,如果你想要这种类型对象的 key 值的列表,你应该使用 Map。
基本上,如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。
new WeakMap([iterable])
//Iterable 是一个数组(二元数组)或者其他可迭代的且其元素是键值对的对象。每个键值对会被加到新的 WeakMap 里。null 会被当做 undefined。
WeakSet对象
WeakSet 对象允许你将弱保持对象存储在一个集合中。
WeakSet 对象是一些对象值的集合, 并且其中的每个对象值都只能出现一次,在WeakSet的集合中是唯一的。
它和 Set 对象的区别有两点:
与Set相比,WeakSet 只能是对象的集合,而不能是任何类型的任意值。
WeakSet持弱引用:集合中对象的引用为弱引用。如果没有其他的对WeakSet中对象的引用,那么这些对象会被当成垃圾回收掉。这也意味着WeakSet中没有存储当前对象的列表。正因为这样,WeakSet 是不可枚举的。
new WeakSet([iterable]);
//iterable 如果传入一个可迭代对象作为参数, 则该对象的所有迭代值都会被自动添加进生成的 WeakSet 对象中。null 被认为是 undefined。
以上内容部分摘自视频课程04网页游戏编程JavaScript-27数据结构,更多示例请参见网站示例。跟着张员外讲编程,学习更轻松,不花钱还能学习真本领。
领取专属 10元无门槛券
私享最新 技术干货