集合引用类型

对象

适合存储和应用程序间交换数据

创建Object实例方法

  1. 使用new操作符newObject
  2. 对象字面量{}
    1. 属性名可以是字符串或数值,数值属性会自动转换为字符串
    2. 属性名也可以是包含非字母数字字符,但要用中括号去存取

属性一般通过点语法获取,也能通过中括号,中括号内使用属性名的字符串形式

数组与定型数组

数组

创建方法

  1. 使用Array构造函数
    • new Array()Array()
    • 可以传入一个数值,然后length属性就会被自动创建并设置为这个值
    • 也可以给Array构造函数传入要保存的元素
  2. 数组字面量
    • []
  3. from()
    • 类数组结构转换为数组实例
    • 第一个参数是类数组对象,即任何可迭代的结构,或者有一个length属性和可索引元素的结构
    • 可选第二个参数,映射函数参数。这个函数可以直接增强新数组的值
    • 可选第三个参数,用于指定映射函数中this的值,但这个重写的this值在箭头函数中不适用
  4. of()
    • 一组参数转换为数组实例

ECMAScript会将逗号之间相应索引位置的值当成空位

数组索引:在中括号中提供的索引表示要访问的值

  • 如果索引小于数组包含的元素数,则返回存储在相应位置的元素
  • 如果把一个值设置给超过数组最大索引的索引,则数组长度会自动扩展到该索引值加1

通过修改length属性,可以从数组末尾删除或添加元素。

检测数组

  • 通过instanceof操作符
    • 缺陷:如果网页里有多个框架,则可能涉及两个不同的全局执行上下文,如果要把数组从一个框架传给另一个框架,则这个数组的构造函数将有别于在第二个框架内本地创建的数组
  • Array.isArray()
    • 确定一个值是否为数组,而不用管它是在哪个全局执行上下文中创建的。

迭代器方法

  1. keys():返回数组索引的迭代器
  2. values():返回数组元素的迭代器
  3. entries():返回索引/值对的迭代器

复制和填充方法

两个都不会改变数组大小

  1. 批量复制方法:copyWithin()

    • 会按照指定范围浅复制数组中的部分内容,然后将它们插入到指定索引开始的位置。开始索引和结束索引则与fill()使用同样的计算方法
    • 索引过低、过高、反向都会忽略
  2. 填充数组方法:fill()

    • 可以向一个已有的数组中插入全部或部分相同的值
    • 开始索引用于指定开始填充的位置,它是可选的
    • 如果不提供结束索引,则一直填充到数组末尾
    • 负值索引从数组末尾开始计算
    • 参数个数
      • 1个:fill(x),用x填充整个数组
      • 2个:fill(x,a),用x填充索引大于a的元素
      • 3个:fill(x,a,b),用x填充索引大于a小于b的元素
    • 索引过低、过高、反向都会忽略

转换方法

  1. toLocaleString
    • 可能返回跟toString()valueOf()相同的结果,但也不一定。在调用数组的toLocaleString()方法时,会得到一个逗号分隔的数组值的字符串。它与另外两个方法唯一的区别是,为了得到最终的字符串,会调用数组每个值的toLocaleString()方法,而不是toString()方法
  2. toString
    • 返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串
  3. valueOf
    • 返回的还是数组本身
  4. join()
    • 接收一个参数,即字符串分隔符,返回包含所有项的字符串
    • 如果不传入任何参数,或者传入undefined,则仍然使用逗号作为分隔符

栈方法

  1. push()
  2. pop()

队列方法

  1. shift()
    • 从数组头取走一个元素
  2. unshift()
    • 可添加任意多个值

排序方法

  1. reverse()
    • 将数组元素反向排列
  2. sort()
    • 按照升序重新排列数组元素,即最小的值在前面,最大的值在后面
    • 会在每一项上调用String()转型函数,然后比较字符串来决定顺序
    • 可以接收一个比较函数,用于判断哪个值应该排在前面
      • 比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相等,就返回0;如果第一个参数应该排在第二个参数后面,就返回正值。

操作方法

  1. concat()

    在现有数组全部元素基础上创建一个新数组

    • 创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组
    • 如果传入一个或多个数组,则concat()会把这些数组的每一项都添加到结果数组
    • 如果参数不是数组,则直接把它们添加到结果数组末尾
  2. slice()

    用于创建一个包含原有数组中一个或多个元素的新数组

    • 接收一个或两个参数:返回元素的开始索引和结束索引
    • 如果只有一个参数,则slice()会返回该索引到数组末尾的所有元素
    • 如果有两个参数,则slice()返回从开始索引到结束索引对应的所有元素
    • 操作不影响原始数组
  3. splice()

    主要目的是在数组中间插入元素

    • 删除:传2个参数:要删除的第一个元素的位置和要删除的元素数量
    • 插入:传3个参数:开始位置、0(要删除的元素数量)和要插入的元素
    • 替换:传入3个参数:开始位置、要删除元素的数量和要插入的任意多个元素

搜索和位置方法

两种分类:按严格相等搜索和按断言函数搜索

  1. 严格相等搜索,这些方法都接收两个参数:要查找的元素和一个可选的起始搜索位置

    1. indexOf()
      • 从数组前头(第一项)开始向后搜索
      • 返回要查找的元素在数组中的位置,如果没找到则返回1
    2. lastIndexOf()
      • 从数组末尾(最后一项)开始向前搜索
      • 返回要查找的元素在数组中的位置,如果没找到则返回1
    3. includes()
      • 从数组前头(第一项)开始向后搜索

      • 返回布尔值

      • 能正确处理NaN

        • constarr=[NaN];
          console.log(arr.indexOf(NaN)) //-1
          console.log(arr.includes(NaN))//true
          
  2. 断言函数搜索

    每个索引都会调用这个函数。断言函数的返回值决定了相应索引的元素是否被认为匹配

    • 第一个参数是函数,此函数接受三个参数:元素、索引和数组本身,都从数组的最小索引开始
    • 可选第二个参数,用于指定断言函数内部this的值
    1. find()
      • 返回第一个匹配的元素
    2. findIndex()
      • 返回第一个匹配元素的索引

迭代方法

每个方法接收两个参数:以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象,传给每个方法的函数接收3个参数:数组元素、元素索引和数组本身。

  1. every()
    • 对每一项函数都返回true,则这个方法返回true
  2. filter()
    • 返回true的项会组成数组之后返回
    • 非常适合从数组中筛选满足给定条件的元素
  3. forEach()
    • 每一项都运行传入的函数,没有返回值
    • 相当于使用for循环遍历数组
  4. map()
    • 返回由每次函数调用的结果构成的数组
  5. some()
    • 如果有一项函数返回true,则这个方法返回true

归并方法

会迭代数组的所有项,并在此基础上构建一个最终返回值

  1. reduce()
    • 从数组第一项开始遍历到最后一项
    • 可接受两个参数
      • 第一个参数,是个归并函数,此函数包含四个参数:上一个归并值、当前项、当前项的索引和数组本身
      • 第二个参数可选,以之为归并起点的初始值,不选则归并函数的第一个参数是数组的第一项,第二个参数是数组的第二项
  2. reduceRight()
    • 从最后一项开始遍历至第一项
    • 同上

Map、WeakMap、Set以及WeakSet类型

Map的大多数特性都可以通过Object类型实现,但二者之间还是存在一些细微的差异

Map

Object只能使用数值、字符串或符号作为键不同,Map可以使用任何JavaScript数据类型作为键,内部使用严格相等的标准来检测键的匹配性。与Object类似,映射的值是没有限制

基本API

  1. newMap():使用new关键字和Map构造函数创建一个空映射
    • 可以传入一个可迭代对象,需要包含键/值对数组,且会按照迭代顺序插入到新映射实例
  2. set()
  3. get()
  4. has()
  5. delete()
  6. clear()

顺序与迭代

  • 可以通过entries()方法(或者Symbol.iterator属性,它引用entries())取得这个迭代器。因为entries()是默认迭代器,所以可以直接对映射实例使用扩展操作,把映射转换为数组
  • 可以调用映射的forEach(callback,opt_thisArg)方法并传入回调,依次迭代每个键/值对
    • 传入的回调接收可选的第二个参数,这个参数用于重写回调内部this的值
  • keys()values()分别返回以插入顺序生成键和值的迭代器
  • 键和值在迭代器遍历时是可以修改的,但映射内部的引用则无法修改

ObjectMap的比较

  1. 内容占用
    • 存储单个键/值对所占用的内存数量都会随键的数量线性增加
    • 给定固定大小的内存,Map大约可以比Object多存储50%的键/值对
  2. 插入性能
    • 插入新键/值对的消耗大致相当
    • 插入速度并不会随着键/值对数量而线性增加
    • 大量插入操作,那么显然Map的性能更佳
  3. 查找速度
    • 性能差异极小
    • 查找速度不会随着键/值对数量增加而线性增加
    • 涉及大量查找操作,那么某些情况下可能选择Object更好一些
  4. 删除性能
    • Map的delete()操作都比插入和查找更快
    • 涉及大量删除操作,那么毫无疑问应该选择Map

WeakMap

  • 弱映射中的键只能是Object或者继承自Object的类型,尝试使用非对象设置键会抛出TypeError。值的类型没有限制。
  • 没有指向这个对象(键)的其他引用->键就会被当作垃圾回收->键值对从弱映射消失,成为空映射->值就没了引用->所以值就可以被当作垃圾进行回收
  • 因为WeakMap中的键/值对任何时候都可能被销毁,所以没必要提供迭代其键/值对的能力
  • WeakMap实例之所以限制只能用对象作为键,是为了保证只有通过键对象的引用才能取得值

基本API

  1. newWeakMap()
    • 可以传入一个可迭代对象,需要包含键/值对数组,且会按照迭代顺序插入到新映射实例
    • 初始化是全有或全无的操作,只要有一个键无效就会抛出错误,导致整个初始化失败
  2. set()
  3. get()
  4. has()
  5. delete()

使用场景

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

Set

Set可以包含任何JavaScript数据类型作为值

基本API

  1. newSet():使用new关键字和Set构造函数创建一个空集合
    • 可以传入一个可迭代对象
  2. add()
  3. has()
  4. delete()
    • 返回一个布尔值,表示集合中是否存在要删除的值
  5. clear()

顺序与迭代

Set会维护值插入时的顺序,因此支持按顺序迭代。

  • 可以提供一个迭代器(Iterator),能以插入顺序生成集合内容。可以通过values()方法及其别名方法keys()(或者Symbol.iterator属性,它引用values())取得这个迭代器
  • 因为values()是默认迭代器,所以可以直接对集合实例使用扩展操作,把集合转换为数组
  • 集合的entries()方法返回一个迭代器,可以按照插入顺序产生包含两个元素的数组,这两个元素是集合中每个值的重复出现
  • 如果不使用迭代器,而是使用回调方式,则可以调用集合的forEach()方法并传入回调,依次迭代每个键/值对。传入的回调接收可选的第二个参数,这个参数用于重写回调内部this的值

WeakSet

弱集合中的值只能是Object或者继承自Object的类型,尝试使用非对象设置值会抛出TypeError

当没有指向这个对象的其他引用->这个对象值就会被当作垃圾回收->这个值就从弱集合中消失了,成为空集合。

WeakSet之所以限制只能用对象作为值,是为了保证只有通过值对象的引用才能取得值。

基本API

  1. newWeakSet()
    • 可以传入一个可迭代对象
    • 初始化是全有或全无的操作,只要有一个值无效就会抛出错误,导致整个初始化失败
  2. add()
  3. has()
  4. delete()
    • 返回一个布尔值,表示集合中是否存在要删除的值

使用场景

  • 打标签

迭代与扩展操作

Array、所有定型数组、MapSet

  • 都定义了默认迭代器
  • 都支持顺序迭代,都可以传入for-of循环
  • 都兼容扩展操作符。扩展操作符在对可迭代对象执行浅复制时特别有用
    • 浅复制意味着只会复制对象引用
  • 对于期待可迭代对象的构造函数,只要传入一个可迭代对象就可以实现复制