第6章 集合引用类型

第6章 集合引用类型

6.1 Object

创建方式:

  • new Object()

    let person = new Object();
    
    person.name = "冬篱的川";
    person.age = 29;
    
  • 对象字面量 object literal

    let person = {
        name: "冬篱的川",
        age: 23
    }
    

6.2 Array

JavaScript中的数组每个位置可以存放任意类型的数据,其大小是动态的,会随着数据添加而自动增加。

6.2.1 创建数组

  • 使用Array构造函数

    let persons = new Array();
    

    也可以传入一个参数,表示元素的数量,即数组length属性会被自动创建并设置成这个值

    let persons = new Array(20);
    

    也可以给构造函数传入需要保存的元素

    let persons = new Array("冬篱", "冬篱的川");
    
  • 使用数组字面量 array literal

    let persons = ["冬篱", "冬篱的川"];
    let names = [];
    let ages = [23 ,23];
    
  • 使用Array.from() 将类数组结构转化为数组实例,其第一个参数为类数组对象,第二个参数为可选的映射函数参数,第三个参数为可选的用于指定映射函数中this的值

    console.log(Array.from("冬篱的川")); // [ '冬', '篱', '的', '川' ]
    
    const m = new Map().set(1, 2)
    					.set(3, 4);
    const s = new Set().add(1)
    					.add(2)
    					.add(3)
    					.add(4);
    
    console.log(Array.from(m)); // [[1, 2], [3, 4]]
    console.log(Array.from(s)); // [1, 2, 3, 4]
    
    const a1 = [1, 2, 3, 4];
    console.log(Array.from(a1, x => x**3)); // [ 1, 8, 27, 64 ]
    
  • 使用Array.of()将一组参数转换为数组实例

6.2.2 数组空位

初始号数组室,可以使用,来创建空位(hole)

let options = [1, , , , 5];
console.log(options); 

for (const option of options) {
    console.log(option == undefined);
}

image-20210723142039626

6.2.3 数组索引

数组length属性的独特之处是,其是可以修改的,可以通过修改length从数组末尾删除或添加元素

let colors = ["red", "blue", "green"];

console.log(colors); // ["red", "blue", "green"]

colors.length = 2;
console.log(colors); // ["red", "blue"]

colors[colors.length] = "purple";
console.log(colors); // ["red", "blue", "purple"]

注意 数组最多包括 4 294 967 295 个元素

image-20210723143836926

6.2.4 检测数组

使用instanceof检测对象是不是数组

let x = []
console.log(x instanceof Array);

image-20210723143958582

6.2.5 迭代器方法

  • keys()返回数组索引的容器
  • values()返回数组元素的迭代器
  • entries() 返回索引/值对的迭代器
let names = ['冬篱', '冬篱的川'];

console.log(Array.from(names.keys()));
console.log(Array.from(names.values()));
console.log(Array.from(names.entries()));

image-20210723144259150

6.2.6 复制和填充方法

  • fill(value, start, end) 向一个已有数组插入全部或部分相同的值,索引从start-end,但不包括end

    const zeroes = [0, 0, 0, 0, 0];
    
    zeros.fill(3, 2, 4);
    console.log(zeroes);
    
    zeros.fill(3, -4, -1);
    console.log(zeroes);
    

    image-20210723161411261

  • copyWithin() 按照指定范围浅复制数组中的部分内容,然后将它们插入到指定索引开始的位置

    let ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    
    // 将索引5开始的内容插入到索引0开始的位置
    ints.copyWithin(0, 5);
    console.log(ints);
    

    image-20210723161913753

6.2.7 转换方法

  • valueOf()返回数组本身

  • toString()返回数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串

  • toLocaleString()为了得到最终的字符串,会调用数组每个值的 toLocaleString()方法

    let person1 = {
    	toLocaleString() {
    		return "Nikolaos";
    	},
    	toString() {
    		return "Nicholas";
    	}
    };
    
    let person2 = {
    	toLocaleString() {
    		return "Grigorios";
    	},
    	toString() {
    		return "Greg";
    	}
    };
    
    let people = [person1, person2];
    
    alert(people); // Nicholas,Greg
    alert(people.toString()); // Nicholas,Greg
    alert(people.toLocaleString()); // Nikolaos,Grigorios
    

    注意 如果数组中某一项是 nullundefined,则在 join()、 toLocaleString()、toString()和 valueOf()返回的结果中会以空字符串表示。

6.2.8 栈方法

  • push() 接收任意数量的阐述,并将其添加至数组末尾,返回数组的新长度
  • pop()删除数组的最后一项,减少数组的length,返回被删除的项

6.2.9 队列方法

  • shift()删除数组的第一项,并返回这项,数组长度减一
  • push()

6.2.10 排序方法

  • reverse()将数组元素反向排列

  • sort()按照升序重新排练数组元素,可选一个比较函数,用于判断哪个值在前面,这个比较函数接收两个参数,如果第一个参数在第二个参数前面,返回负值,相等返回0,后面返回正值

    function compare(v1, v2) {
        if (v1 < v2) {
            return -1;
        } else if (v1 > v2) {
            return 1;
        } else {
            return 0;
        }
    }
    
    let v = [23, 1, 24, 2];
    v.sort(compare);
    console.log(v);
    

    image-20210723170036782

注意 reverse()sort()都返回调用它们的数组的引用

6.2.11 操作方法

  • concat()传入一个或多个参数(可以是数组),将里面的每一项都添加都结果数组中,返回当前数组的副本
  • slice()接收一个或两个参数,前者为开始索引,后者为结束索引,只有一个参数则返回该索引到最后末尾的元素
  • splice()主要目的是在数组中间插入元素,返回新的数组
    • 删除 需要传给splice(start, num)两个参数,start要删除的第一个元素的位置,num要删除元素的数量
    • 插入 需要给splice(start, 0, ...values)传入三个参数,start开始的位置,0表示删除0个元素,...values表示插入元素可以为多个
    • 替换 即先删除、后插入,同上面插入,只是第二个参数大于0

6.2.12 搜索和位置方法

  • 严格相等搜索,下面方法都接收两个参数,要查找的元素x,和一个可选的起始搜索位置start
    • indexOf(x, start) 返回x在数组中的位置
    • lasteIndexOf(x, start)返回x在数组中的位置
    • includes(x, start)返回是否找到至少一个元素与x匹配
  • 断言函数搜索,断言函数接收 3 个参数:元素、索引和数组本身。其中元素是数组中当前搜索的元素,索引是当前元素的索引,而数组就是正在搜索的数组断言函数返回真值,表示是否匹配。
    • find(element, index, array) 返回第一个匹配的元素
    • findIndex(element, index, array) 返回第一个匹配的元素的索引

6.2.13 迭代方法

ECMASscript为数组定义了5个迭代方法,都包含两个参数,以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中 this 的值)。传给每个方法的函数接收 3个参数:数组元素、元素索引和数组本身。下面的方法都不改变调用它们的数组

  • every((item, index, array) => {...}, object)对数组每一项都运行传入的函数,如果对每一项函数都返回 true, 则这个方法返回 true

    let nums = [2, 3, 4, 5];
    
    let everyResult = nums.every((item, index, array) => item > 2);
    console.log(everyResult); // false
    
  • filter((item, index, array) => {...}, object)对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回

let nums = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11];

function isOdd(x) {
    const sqrtx = Math.sqrt(x);
    for (let i = 2; i <= sqrtx; ++i) {
        if (x % i == 0) {
            return false
        }
    }
    
    return true;
}

let filterResult = nums.filter((item, index, array) => isOdd(item));
console.log(filterResult); // [ 2, 3, 5, 7, 11 ]
  • forEach((item, index, array) => {...}, object)对数组每一项都运行传入的函数,没有返回值

  • map((item, index, array) => {...}, object)对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组

  • some((item, index, array) => {...}, object)对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true

6.2.14 归并方法

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

这两个方法都接收两个参数:对每一项都会运行的归并函数,以及可选的以之为归并起点的初始值。传给 reduce()reduceRight()的函数接收 4 个参数:上一个归并值、当前项、当前项的索引和数组本身。这个函数返回的任何值都会作为下一次调用同一个函数的第一个参数。如果没有给这两个方法传入可选的第二个参数(作为归并起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数的第一个参数是数组的第一项,第二个参数是数组的第二项。

  • reduce((prev, cur, index, array) => {...}, start)

  • reduceRight((prev, cur, index, array) => {...}, start)

    let values = [1, 2, 3, 4, 5];
    
    let sum = values.reduce((prev, cur, index, array) => {
        prev = prev + cur;
        console.log("prev: ", prev);
        return prev;
    });
    console.log(sum); // 15
    

    image-20210724210319362

6.3 定型数组

TODO

似乎不太重要

6.4 Map

Map类型是一种集合类型,其大多数特性可以通过Object类型实现

6.4.1 基本API

  • 创建Map对象

    • 使用new Map()创建空映射

      let m = new Map();
      
    • 创建的同时初始化实例,需要给Map构造函数传入一个可迭代的对象,需要包含键值对数组,按照迭代的顺序插入

      const m1 = new Map([
          ["冬篱", 23].
          ["冬篱的川", 23]
      ]);
      console.log(m1.size;
      
      const m2 = new Map({
          [Symbol.iterator]: function*() {
              yield ["冬篱", 23];
              yield ["冬篱的川", 23]
          }
      });
      console.log(m2.size);
      
  • 使用set()方法添加键值对,返回的是实例本身

    const m3 = new Map();
    
    m3.set("冬篱", 23)
    	.set("冬篱的川", 23);
    console.log(m3.size;
    
  • get() has()进行查询

  • size属性获取映射中键值对数量

  • delete() clear 删除值

  • Object区别,Object只能使用数值、字符串、符号作为键,Map可以使用任何JavaScript数据类型作为键,其内部使用SameValueZero比较

6.4.2 顺序和迭代

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

映射实例可以提供一个迭代器( Iterator),能以插入顺序生成[key, value]形式的数组。可以通过 entries()方法(或者 Symbol.iterator 属性,它引用 entries())取得这个迭代器:

const m = new Map([
    ["冬篱", 23].
    ["冬篱的川", 23]
]);

console.log(m.entries() == m[Symbol.iterator]); // true

for (const pair of m.entries()) {
    console.log(paire);
}

for (const pair of m[Symbol.iterator]()) {
    console.log(paire);
}

6.4.3 选择Object还是Map

  1. 内存占用

    但给定固定大小的内存, Map 大约可以比 Object 多存储 50%的键/值对

  2. 插入性能

如果代码涉及大量插入操作,那么显然Map的性能更佳

  1. 查找速度

    如果代码涉及大量查找操作,那么某些情况下可能选择 Object 更好一些

  2. 删除性能

    Mapdelete()操作都比插入和查找更快。如果代码涉及大量删除操作,那么毫无疑问应该选择Map

6.5 WeakMap

WeakMapMap 的“兄弟”类型,其 API 也是 Map 的子集。WeakMap中的“weak”(弱),描述的是 JavaScript 垃圾回收程序对待“弱映射”中键的方式

6.5.1 基本API

Map相同,可以使用new关键字来实例化一个空的WeakMap

const m = new WeakMap();

Map不同的是,WeakMap中的键只能是Object或者继承自Object的类型,尝试使用非对象设置键会跑出TypeError,值得类型无限制

6.5.2 弱键

WeakMap中的 "weak" 表示弱映射的键只是 "弱弱的拿着",意味着这些键不是正式的引用,不会阻止垃圾回收

6.5.3 不可迭代键

WeakMap实例之所以限制只能用对象作为键,是为了保证只有通过键对象的引用才能取得值。如果允许原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了

6.5.4 使用弱映射

  1. 私有变量
  2. DOM节点元数据

6.6 Set

Set在很多方面都像是加强的 Map,这是因为它们的大多数 API 和行为都是共有的

6.7 WeakSet

6.8 迭代与扩展操作

4 种原生集合类型定义了默认的迭代器

  • Array
  • 所有定型数组
  • Map
  • Set

上述所有类型都支持顺序迭代,都可以传入 for-of 循环

let iterableThings = [
    Array.of("冬篱", "冬篱的川"),
    typedArray = Int16Array.of(23, 23),
    new Map([[5, 6], [7, 8]]),
    new Set([9, 10])
]

for (const iterableThing of iterableThings) {
    for (const x of iterableThing) {
        console.log(x);
    }
}

image-20210725104444249

上面类型都兼容扩展操作符...,这里是浅复制,浅复制意味着只会复制对象引用

let arr1 = [1, 2, 3];
let arr2 = [...arr1];

console.log(arr1);
console.log(arr2);
console.log(arr1 == arr2);


let arr3 = [{}];
let arr4 = [...arr3];

arr3[0].foo = 'bar';
console.log(arr4[0].foo);

image-20210725105049592

posted @ 2021-07-25 13:16  fankaljead  阅读(78)  评论(0)    收藏  举报