[JavaScript基础] 四、集合引用类型

266 阅读7分钟

1、Object

Object是ECMAScript中最常用的类型之一。

创建

显式地创建Object的实例有两种方式。第一种是使用new操作符和Object构造函数,

let person = new Object();

另一种方式是使用对象字面量(object literal)表示法。

let person = {
 name: "Nicholas",
 age: 29
};

属性的存储

虽然属性一般是通过点语法来存取的,这也是面向对象语言的惯例,但也可以使用中括号来存取属性。使用中括号的主要优势就是可以通过变量访问属性,

2、Array

ECMAScript数组也是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据。

创建

一种是使用Array构造函数,另一种创建数组的方式是使用数组字面量(array literal)表示法

检测

  • 使用instanceof操作符
  • Array.isArray()方法

迭代

在ES6中,Array的原型上暴露了3个用于检索数组内容的方法:keys()、values()和entries()。

  • keys()返回数组索引的迭代器
  • values()返回数组元素的迭代器
  • 而entries()返回索引/值对的迭代器:

栈方法

  • push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度。

  • pop()方法则用于删除数组的最后一项,同时减少数组的length值,返回被删除的项。

队列方法

  • shift(),它会删除数组的第一项,然后数组长度减1,返回被删除的项。
  • unshift()就是执行跟shift()相反的操作,在数组开头添加任意多个值,然后返回新的数组长度。

排序方法

  • reverse() 将数组反向排列,返回调用它们的数组的引用。

  • sort() 会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。并返回调用它们的数组的引用。

    sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。

    let values = [0, 1, 5, 10, 15];
    values.sort();
    alert(values); // 0,1,10,15,5
    

    sort方法可以接受一个比较函数,用于判断哪个值应该排在前面。

    function compare(value1, value2) {
     if (value1 < value2) {
     return -1;
     } else if (value1 > value2) {
     return 1;
     } else {
     return 0;
     }
    }
    let values = [0, 1, 5, 10, 15];
    values.sort(compare);
    alert(values); // 0,1,5,10,15
    

操作方法

  • contact()方法 用于连接数组元素,它首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组。

  • **slice()方法 **用于创建一个包含原有数组中一个或多个元素的新数组。

    let colors = ["red", "green", "blue", "yellow", "purple"];
    let colors2 = colors.slice(1);
    let colors3 = colors.slice(1, 4);
    alert(colors2); // green,blue,yellow,purple
    alert(colors3); // green,blue,yellow
    
  • **splice() **主要目的是在数组中间插入元素,但有3种不同的方式使用这个方法。splice()方法始终返回这样一个数组,它包含从数组中被删除的元素(如果没有删除元素,则返回空数组)。

    • 删除。需要给splice()传2个参数:要删除的第一个元素的位置和要删除的元素数量。可以从数组中删除任意多个元素,比如splice(0, 2)会删除前两个元素。

    • 插入。需要给splice()传3个参数:开始位置、0(要删除的元素数量)和要插入的元素,可以在数组中指定的位置插入元素。第三个参数之后还可以传第四个、第五个参数,乃至任意多个要插入的元素。比如,splice(2, 0, "red", "green")会从数组位置2开始插入字符串"red"和"green"。

    • 替换。splice()在删除元素的同时可以在指定位置插入新元素,同样要传入3个参数:开始位置、要删除元素的数量和要插入的任意多个元素。要插入的元素数量不一定跟删除的元素数量一致。比如,splice(2, 1, "red", "green")会在位置2删除一个元素,然后从该位置开始向数组中插入"red"和"green"。

搜索和位置方法

ECMAScript提供两类搜索数组的方法:按严格相等搜索和按断言函数搜索。

严格相等

  • indexOf()、lastIndexOf()方法都返回要查找的元素在数组的位置。indexOf()是从数组开始进行搜索,lastIndexOf()从数组末尾开始
  • includes() 返回布尔值,表示是否至少找到一个与指定元素匹配的项。

断言函数

断言函数接收**3个参数:元素、索引和数组本身。**其中元素是数组中当前搜索的元素,索引是当前元素的索引,而数组就是正在搜索的数组。

find()和findIndex()方法使用了断言函数。这两个方法都从数组的最小索引开始。find()返回第一个匹配的元素,findIndex()返回第一个匹配元素的索引。这两个方法也都接收第二个可选的参数,用于指定断言函数内部this的值。

const people = [
 {
 name: "Matt",
 age: 27
 },
 {
 name: "Nicholas",
 age: 29
 }
];
alert(people.find((element, index, array) => element.age < 28));
// {name: "Matt", age: 27}
alert(people.findIndex((element, index, array) => element.age < 28));
// 0

找到匹配项后,这两个方法都不再继续搜索。

迭代方法

ECMAScript为数组定义了5个迭代方法。每个方法接收两个参数:以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中this的值)。

数组的5个迭代方法如下:(这些方法都不改变调用它们的数组)

  • every():对数组每一项都运行传入的函数,如果对每一项函数都返回true,则这个方法返回true。

  • filter():对数组每一项都运行传入的函数,函数返回true的项会组成数组之后返回。

  • forEach():对数组每一项都运行传入的函数,没有返回值。

  • map():对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。

  • some():对数组每一项都运行传入的函数,如果有一项函数返回true,则这个方法返回true。

    方法名主要用途返回值
    map()创建一个与原始数组元素一一对应的新数组返回由每次函数调用的结果构成的数组
    every()判断数组中元素是否都符合某个条件对每一项函数都返回true,则这个方法返回true
    some()判断数组中元素是否有符合某个条件的元素如果有一项函数返回true,则这个方法返回true
    filter()从数组中搜索符合某个条件的元素函数返回true的项会组成数组之后返回。
    forEach()相当于使用for循环遍历数组没有返回值。
    //every some方法
    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let everyResult = numbers.every((item, index, array) => item > 2);
    alert(everyResult); // false
    let someResult = numbers.some((item, index, array) => item > 2);
    alert(someResult); // true
    //filter方法
    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let filterResult = numbers.filter((item, index, array) => item > 2);
    alert(filterResult); // 3,4,5,4,3
    //map方法
    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    let mapResult = numbers.map((item, index, array) => item * 2);
    alert(mapResult); // 2,4,6,8,10,8,6,4,2
    //forEach方法
    let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
    numbers.forEach((item, index, array) => {
     // 执行某些操作
    });
    

    归并方法

    reduce()、reduceRight()这两个方法都会迭代数组的所有项,并在此基础上构建一个最终返回值。reduce()方法从数组第一项开始遍历到最后一项。而reduceRight()从最后一项开始遍历至第一项。

3、Map

作为ECMAScript 6的新增特性,Map是一种新的集合类型,为这门语言带来了真正的键/值存储机制。Map的大多数特性都可以通过Object类型实现,但二者之间还是存在一些细微的差异。具体实践中使用哪一个,还是值得细细甄别。

基本API

使用new关键字和Map构造函数可以创建一个空映射,

初始化

与Object只能使用数值、字符串或符号作为键不同,Map可以使用任何JavaScript数据类型作为键。

// 使用new关键字和Map构造函数可以创建一个空映射,
const m = new Map();
// 使用嵌套数组初始化映射
const m1 = new Map([
 ["key1", "val1"],
 ["key2", "val2"],
 ["key3", "val3"]
]);
alert(m1.size); // 3
// 使用自定义迭代器初始化映射
const m2 = new Map({
 [Symbol.iterator]: function*() {
 yield ["key1", "val1"];
 yield ["key2", "val2"];
 yield ["key3", "val3"];
 }
});
alert(m2.size); // 3
// 映射期待的键/值对,无论是否提供
const m3 = new Map([[]]);
alert(m3.has(undefined)); // true
alert(m3.get(undefined)); // undefined

添加、删除、查询

初始化之后,可以使用set()方法再添加键/值对。另外,可以使用get()和has()进行查询,可以通过size属性获取映射中的键/值对的数量,还可以使用delete()和clear()删除值。

const m = new Map();

alert(m.has("firstName")); // false
alert(m.get("firstName")); // undefined
alert(m.size); // 0

m.set("firstName", "Matt")
 .set("lastName", "Frisbie");

alert(m.has("firstName")); // true
alert(m.get("firstName")); // Matt
alert(m.size); // 2

m.delete("firstName"); // 只删除这一个键/值对

alert(m.has("firstName")); // false
alert(m.has("lastName")); // true
alert(m.size); // 1

m.clear(); // 清除这个映射实例中的所有键/值对

alert(m.has("firstName")); // false
alert(m.has("lastName")); // false
alert(m.size); // 0

顺序与迭代

与Object类型的一个主要差异是,Map实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。

选择Object还是Map

对于多数Web开发任务来说,选择Object还是Map只是个人偏好问题,影响不大。不过,对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。

  • 内存占用: 给定固定大小的内存,Map大约可以比Object多存储50%的键/值对
  • 插入性能: Map的性能更佳。
  • 查找速度 大型Object和Map中查找键/值对的性能差异极小,但如果只包含少量键/值对,则Object有时候速度更快。
  • 删除性能 Map的性能更佳。

4、set

ECMAScript 6新增的Set是一种新集合类型,为这门语言带来集合数据结构。Set在很多方面都像是加强的Map,这是因为它们的大多数API和行为都是共有的。

项目中用的较少,这里暂时不写了~