集合引用类型
Object
- 字面量或构造函数创建
- 点语法取值或中括号取值
Array
跟其他语言中的数组一样,ECMAScript 数组也是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。这意味着可以创建一个数组,它的第一个元素是字符串,第二个元素是数值,第三个是对象。ECMAScript 数组也是动态大小的,会随着数据添加而自动增长
创建
使用构造函数或字面量
Array 构造函数还有两个 ES6 新增的用于创建数组的静态方法:from()和 of()。from()用于将类数组结构转换为数组实例,而 of()用于将一组参数转换为数组实例
Array.from() 可接收第二个参数函数,对每一项进行处理
判断
instanceof 操作符或 isArray()方法
迭代器方法
在 ES6 中,Array 的原型上暴露了 3 个用于检索数组内容的方法:keys()、values()和entries()。keys()返回数组索引的迭代器,values()返回数组元素的迭代器,而 entries()返回索引/值对的迭代器
const a = ["foo", "bar", "baz", "qux"];
// 因为这些方法都返回迭代器,所以可以将它们的内容
// 通过 Array.from()直接转换为数组实例
const aKeys = Array.from(a.keys());
const aValues = Array.from(a.values());
const aEntries = Array.from(a.entries());
console.log(aKeys); // [0, 1, 2, 3]
console.log(aValues); // ["foo", "bar", "baz", "qux"]
console.log(aEntries); // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]
使用 ES6 的解构可以非常容易地在循环中拆分键/值对:
const a = ["foo", "bar", "baz", "qux"];
for (const [idx, element] of a.entries()) {
alert(idx);
alert(element);
}
// 0
// foo
// 1
// bar
// 2
// baz
// 3
// qux
复制和填充方法
ES6 新增了两个方法:批量复制方法 copyWithin(),以及填充数组方法 fill()
copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,而不修改其大小。
fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引
转换方法
toLocaleString()、toString()、 valueOf()、 join()
如果数组中某一项是 null 或 undefined,则在 join()、toLocaleString()、toString()和 valueOf()返回的结果中会以空字符串表示。
基本添加删除
- push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度
- pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度
- shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
- unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度
排序方法
- reverse() 方法将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组。
- sort() 方法用原地算法对数组的元素进行排序,并返回数组。排序算法现在是稳定的。默认排序顺序是根据字符串Unicode码点。
reverse()和 sort()都返回调用它们的数组的引用
sort()方法可以接收一个比较函数,用于判断哪个值应该排在前面,比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相等,就返回 0;如果第一个参数应该排在第二个参数后面,就返回正值
function compare(value1, value2) {
if (value1 < value2) {
return -1;
} else if (value1 > value2) {
return 1;
} else {
return 0;
}
}
values.sort(compare);
values.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);
操作方法
- concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
- slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变
- splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
搜索和位置方法
- indexOf()、lastIndexOf()和 includes(): 搜索方法
- find()和 findIndex(): 断言函数,find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引
迭代方法
- every():对数组每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返回 true
- filter():对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回
- forEach():对数组每一项都运行传入的函数,没有返回值
- map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组
- some():对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true
这些方法都不改变调用它们的数组
归并方法
- reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值
- reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。
定型数组(typed array)
暂时理解有困难,个人理解为:JavaScript 数组对象内元素类型是任意的,可以存储 number、string、object 等,但在特定的情况下,如图形驱动API(webGL),图形驱动程序 API 通常不需要以 JavaScript 默认双精度浮点格式传递给它们的数值,而这恰恰是 JavaScript数组在内存中的格式。因此,每次 WebGL 与 JavaScript 运行时之间传递数组时,WebGL 绑定都需要在目标环境分配新数组,以其当前格式迭代数组,然后将数值转型为新数组中的适当格式,而这些要花费很多时间。为了解决这个问题,提供了一个 JavaScript 接口的、C语言风格的浮点值数组
page(180-189)
Map
基本 API
ES6 新增集合类型,键/值存储机制
如果想在创建的同时初始化实例,可以给 Map 构造函数传入一个可迭代对象,需要包含键/值对数组。可迭代对象中的每个键/值对都会按照迭代顺序插入到新映射实例中
初始化之后,可以使用 set()方法再添加键/值对。另外,可以使用 get()和 has()进行查询,可以通过 size 属性获取映射中的键/值对的数量,还可以使用 delete()和 clear()删除值
与 Object 只能使用数值、字符串或符号作为键不同,Map 可以使用任何 JavaScript 数据类型作为键
const m1 = new Map([
["key1", "val1"],
["key2", "val2"],
["key3", "val3"]
]);
/** Map(3) {'key1' => 'val1', 'key2' => 'val2', 'key3' => 'val3'}
[[Entries]]
0: {"key1" => "val1"}
1: {"key2" => "val2"}
2: {"key3" => "val3"}
size: 3
[[Prototype]]: Map **/
顺序与迭代
Map 实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作
- entries() ?方法返回一个新的包含 [key, value] ?对的 Iterator ?对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
- keys()和 values()分别返回以插入顺序生成键和值的迭代器
选择 Object 还是 Map
- 内存占用: 给定固定大小的内存,Map 大约可以比 Object 多存储 50%的键/值对
- 插入性能: 大致相当,Map 稍微快一点
- 查找速度: 大致相当,Object 快一点
- 删除性能: Map
WeakMap
ES6 新增集合类型,其 API 也是 Map 的子集。WeakMap 中的“weak”(弱),描述的是 JavaScript 垃圾回收程序对待“弱映射”中键的方式
弱映射中的键只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置键会抛出TypeError。值的类型没有限制
弱键
WeakMap 中“weak”表示弱映射的键是“弱弱地拿着”的。意思就是,这些键不属于正式的引用,不会阻止垃圾回收。但要注意的是,弱映射中值的引用可不是“弱弱地拿着”的。只要键存在,键/值对就会存在于映射中,并被当作对值的引用,因此就不会被当作垃圾回收
不可迭代键
WeakMap 实例之所以限制只能用对象作为键,是为了保证只有通过键对象的引用才能取得值。如果允许原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了
使用弱映射
- 私有变量
- DOM 节点元数据:WeakMap 实例不会妨碍垃圾回收,所以非常适合保存关联元数据
Set
ES6 新增集合类型,集合数据结构
基本 API
初始化实例,给 Set 构造函数传入一个可迭代对象,其中需要包含插入到新集合实例中的元素
初始化之后,可以使用 add()增加值,使用 has()查询,通过 size 取得元素数量,以及使用 delete()和 clear()删除元素
顺序与迭代
Set 会维护值插入时的顺序,因此支持按顺序迭代
WeakSet
ES6 新增集合类型
弱集合中的值只能是 Object 或者继承自 Object 的类型,尝试使用非对象设置值会抛出 TypeError
迭代与扩展操作
4 种原生集合类型定义了默认迭代器
- Array
- 所有定型数组
- Map
- Set
这些类型都支持 for-of 循环和扩展运算符(…)对于期待可迭代对象的构造函数,只要传入一个可迭代对象就可以实现复制
let map1 = new Map([[1, 2], [3, 4]]);
let map2 = new Map(map1);
console.log(map1); // Map {1 => 2, 3 => 4}
console.log(map2); // Map {1 => 2, 3 => 4}
这些类型都支持多种构建方法,比如 Array.of()和 Array.from()静态方法。在与扩展操作符一起使用时,可以非常方便地实现互操作
let arr1 = [1, 2, 3];
// 把数组复制到定型数组
let typedArr1 = Int16Array.of(...arr1);
let typedArr2 = Int16Array.from(arr1);
console.log(typedArr1); // Int16Array [1, 2, 3]
console.log(typedArr2); // Int16Array [1, 2, 3]
// 把数组复制到映射
let map = new Map(arr1.map((x) => [x, 'val' + x]));
console.log(map); // Map {1 => 'val 1', 2 => 'val 2', 3 => 'val 3'}
// 把数组复制到集合
let set = new Set(typedArr2);
console.log(set); // Set {1, 2, 3}
// 把集合复制回数组
let arr2 = [...set];
console.log(arr2); // [1, 2, 3]
小结
JavaScript 中的对象是引用值,可以通过几种内置引用类型创建特定类型的对象
- 引用类型与传统面向对象编程语言中的类相似,但实现不同
- Object 类型是一个基础类型,所有引用类型都从它继承了基本的行为
- Array 类型表示一组有序的值,并提供了操作和转换值的能力
- 定型数组包含一套不同的引用类型,用于管理数值在内存中的类型
- Date 类型提供了关于日期和时间的信息,包括当前日期和时间以及计算
- RegExp 类型是 ECMAScript 支持的正则表达式的接口,提供了大多数基本正则表达式以及一些高级正则表达式的能力
JavaScript 比较独特的一点是,函数其实是 Function 类型的实例,这意味着函数也是对象。由于函数是对象,因此也就具有能够增强自身行为的方法
因为原始值包装类型的存在,所以 JavaScript 中的原始值可以拥有类似对象的行为。有 3 种原始值包装类型:Boolean、Number 和 String。它们都具有如下特点
- 每种包装类型都映射到同名的原始类型
- 在以读模式访问原始值时,后台会实例化一个原始值包装对象,通过这个对象可以操作数据
- 涉及原始值的语句只要一执行完毕,包装对象就会立即销毁
JavaScript 还有两个在一开始执行代码时就存在的内置对象:Global 和 Math。其中,Global 对象在大多数 ECMAScript 实现中无法直接访问。不过浏览器将 Global 实现为 window 对象。所有全局变量和函数都是 Global 对象的属性。Math 对象包含辅助完成复杂数学计算的属性和方法
ECMAScript 6 新增了一批引用类型:Map、WeakMap、Set 和 WeakSet。这些类型为组织应用程序数据和简化内存管理提供了新能力