Object
Object是ECMAScript中最常用的类型之一,虽然Object类型的实例没有什么功能,但是很适合在存储和在应用程序之间交换数据
new操作符创建
let person = new Object();
字面量创建
使用字面量方式创建时,并不会实际调用Object构造函数
let person = {};
属性存取
属性一般通过点语法来存取的,但也可以使用中括号加字符串的方式来存取
Array
ECMAScript数组也是一组有序的数据,与其它语言不同的是,数组中每个槽位可以存储任意类型的数据,并且数组也是动态大小的,会随着数据添加而自动增长
new操作符创建
let ar = new Array() // 创建长度为0的数组
let ar1 = new Array(1) // 创建长度为1的数组
let ar2 = new Array(2,) // 创建长度为1的数组
let ar3_4 = new Array(3,4) // 创建长度为2的数组,元素为3、4
字面量创建
使用字面量方式创建时,并不会实际调用Object构造函数
let ar = []// 创建长度为0的数组
let ar1 = [1]// 创建长度为1的数组,元素为1
let ar2 = [2,]// 创建长度为1的数组,元素为2
数组空位
在使用字面量初始化数组时,可以使用一串逗号来创建空位
let ar = [,,,] // 三个空元素
需要注意的是,ES6之前的方法会忽略这个空位,但具体的行为也会因方法而异,由于存在差异在实践中要避免使用数组空位
const optins = [1,,,,5];
console.log(optins.map(()=>6)) // map方法会跳过空位
console.log(optins.join('-')) // join方法视空位置为空字符串
from
用于将类数组结构(任何可迭代的结构或者有一个length属性和可索引元素的结构)转换为数组实例
let ob = { export: 1 }
let ar = Array.from("Math", function (e) {
this.export = 3
return e + this.export
}, ob) // 参数2,3为可选,需要注意的是对this.export修改会导致ob的修改
console.log(ar, ob) // ['M3', 'a3', 't3', 'h3'] {export: 3}
of
用于将一组参数转换为数组实例
let ar = Array.of('M','a','t','h') // ['M', 'a', 't', 'h']
索引
需要注意的是数组的length属性不是只读的,可以将length属性修改为非负整数,最多可以包含4294967295个元素
let ar = [1,2,3,4]
ar.length = 1
console.log(ar) // [1]
ar.length = 4
console.log(ar) // [1, 空 ×3] 注意2、3、4的数据丢失
检测数组
ECMAscript提供了Array.isArray方法来确定一个值是否为数组,而不管它是在哪个全局执行上下文中创建的
迭代器方法
keys()
返回数组索引迭代器
values()
返回数组元素迭代器
entries()
返回键值对的迭代器
fill()
fill函数用于填充数组,接收负数为参数
let ar = [1,2,3,4,5];
ar.fill(5) // 用5填充整个数组
ar.fill(6,3) // 用6填充索引大于3的元素
ar.fill(7,1,3) // 用7填充索引大于等于1小于3的元素
copyWithin()
copyWithin会按照指定范围浅复制数组中的部分内容,然后将它们插入到索引开始的位置。JavaScript引擎在插值前会完整复制范围内的值,因此在复制期间不存在重写的风险
let arr1 = ['a', 'b', 'c', 'd', 'e'];
arr1.copyWithin(0, 3, 4);
console.log(arr1); //["d", "b", "c", "d", "e"]
toLocaleString()
调用每个元素的toLocaleString方法并将返回值以逗号分隔的字符串
toString()
调用每个元素的toString方法并将返回值以逗号分隔的字符串
valueOf()
返回数组本身
join()
以参数作为分隔符
如果数组中的某一项是null或undefined,则在join()、toLocaleString()、toString()和valueOf()返回的结果中以空字符串表示
栈方法
push()
接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度
pop()
用于删除数组的最后一项,同时减少数组的length值,返回被删除的项
队列方法
shift()
用于删除数组的第一项并返回它,然后数组长度减一(每个元素下标都要减一)
unshift()
在数组开头添加任意数量元素,然后返回新的数组长度(每个元素下标都会递加)
排序方法
reverse()和sort()方法都会修改数组本身
reverse()
翻转数组
sort()
按照升序对数组元素(通过字符串顺序来决定,所以会调用元素的toString方法)进行排序
sort方法可以接收一个函数作为参数,参数1为第n+1个元素,参数2为第n个元素
如果第一个参数在第二个参数前面,就返回负值
如果两个参数相等,就返回0
如果第一个参数在第二个参数后面,就返回正值
在排序数组时会使用一种叫做 "Timsort" 的排序算法。Timsort 是由 Tim Peters 在 2002 年设计的,是一种稳定的混合排序算法,它将归并排序和插入排序结合在一起,以提高效率。它的名字来自 "Tim" 和 "merge sort",表示这是一个由 Tim Peters 设计的归并排序。
concat()
在现有数组全部元素基础上创建一个新数组
let ar = [1,2,4,3];
let ar1 = [15]
ar1[Symbol.isConcatSpreadable] = false // 设置不允许打平数组
console.log(ar.concat(ar1))
slice()
创建一个包含原有数组中一个或多个元素的新数组
splice()
需要注意的是splice是对数组本身进行操作
删除
splice(0,1):删除第一个元素
插入
splice(2,0,'1'):将'1'插入到下标2元素的后面
替换
splice(0,1,'1'):用‘1’替换第一个元素
严格相等
在进行查找时会使用'==='进行比较
indexOf()
从前往后查找,找到返回下标否则返回-1
lastIndexOf()
从后往前查找,找到返回下标否则返回-1
includes()
从前往后查找,返回布尔值
断言函数
find()
该函数接收一个函数为参数和一个可选的参数2用于指定参数1函数的this。如果参数1函数返回true则find终止
迭代方法
这些方法都不改变调用它们的数组
every()
每一项函数都返回true,则这个方法返回true
filter()
对返回true的项,会被组成新数组返回
forEach()
对数组的每一项都执行函数,没有返回值
map()
每一项都执行传入的函数,返回由函数返回值组成的数组
some()
每一项都执行传入的函数,如果有一项函数返回true,则这个方法返回true,后续不再执行
归并方法
reduce、reduceRight只是遍历的方向不一样,接收一个函数和一个可选的并归起始值
let ar = [1,2,4,3];
ar.reduce((prev,cur,index,ar)=>{
console.log(prev)
return prev+cur
},10)
reduce()
reduceRight()
定型数组
定型数组是ECMAScript新增的结构,目的是提升向原生库传输数据的效率
定型数组可以直接传给底层图形驱动程序API,也可以直接从底层获取到
定型数组一旦创建,就不能再调整大小
历史
在WebGL的早期版本中,因为javascript数组与原生数组之间不匹配,所以出现了性能问题。图形驱动程序API通常不需要以javaScript默认双精度浮点格式传递给它们的数值,而这恰恰是JavaScript数组在内存中的格式。因此,每次WebGL与JavaScrirpt运行时之间传递数组时,WebGL绑定都需要在目标环境分配新数组,以其当前格式迭代数组,然后将数值转型为新数组中的适当格式,而这些要花费很多时间。
ArrayBuffer
允许JavaScript运行时访问一块名为ArrayBuffer的预分配内存
ArrayBuffer是所有定型数组及试图引用的基本单位,不能仅通过对ArrayBuffer引用直接读取值,需配合DataView使用操作读取数据
SharedArrayBuffer是ArrayBuffer的一个变体,可以无须复制就在执行上下文间传递它
与malloc()区别
ArrayBuffer在某种程度上类似与C++的malloc函数,但也有几个明显的区别
malloc | ArrarBuffer |
---|---|
在分配失败时会返回一个null指针 | 在分配失败时会抛出错误 |
可以利用虚拟内存,因此最大可分配内存受寻址系统限制 | 分配的内存不能超过Number.MAX_SAFE_INTEGER(2的53方-1)字节 |
调用成功不会初始化内存 | 所有二进制位初始化为0 |
分配的堆内存除非调用free或程序退出才能释放 | 分配的堆内存可以被当垃圾回收,不用手动释放 |
DataView
通过DataView视图可以操作ArrayBuffer,这个视图专为文件i/o和网络i/o设计,其API支持对缓冲数据的高度控制,但相比与其它试图性能也会差一些。对缓存内容没有任何预设,也不能迭代。默认是大端模式
ElementType
虽然DataView对存储在缓冲区内的数据类型没有预设,但是它暴露的API强制开发者在读、写时指定一个ElementType,然后DataView就会诚实地读、写而完成相应的转换。ECMAScript6支持8种不同的ElementType
ElementType | 字节 | 说明 | 等价C类型 | 取值范围 |
---|---|---|---|---|
Int8 | 1 | 8位有符号整数 | signed char | -128~127 |
Uint8 | 1 | 8位无符号整数 | unsigned char | 0~255 |
Int16 | 2 | 16位有符号整数 | short | -32768~32767 |
Uint16 | 2 | 16位无符号整数 | unsigned short | 0~65535 |
Int32 | 4 | 32位有符号整数 | int | -2147486648~2147483647 |
Uint32 | 4 | 32位符号整数 | unsigned int | 0~4294967295 |
Float32 | 4 | 32位IEEE-754浮点数 | float | -3.4e+38~3.4e+38 |
Float64 | 8 | 64位IEEE-754浮点数 | double | -1.7e+308~+1.7e+308 |
DataView为上表中的每种类型都暴露了get和set方法,这些方法使用byteOffset定位要读取或写入的位置,并且类型是可以相互使用的
const buf = new ArrayBuffer(2);
const view = new DataView(buf);
console.log(view.getInt8(0));
console.log(view.getInt16(0));
view.setUint8(0,255);
view.setUint8(1,0xff);
console.log(view.getInt16(0))
字节序
JavaScript运行时所在系统的原生字节序决定了如何读取或写入字节,但DataView并不遵守这个约定。对一段内存而言,DataView是一个中立接口,它会遵循你指定的字节序。DataView的所有api方法都默认以大端字节序列,但接收一个可选的布尔参数,设置为true即可开启小端字节序列
const buf = new ArrayBuffer(2);
const view = new DataView(buf);
view.setUint8(0,255,true);
view.getUint8(0,true);
边界情况
DataView完成读、写操作的前提是必须有充足的缓冲区,否则就会抛出RangeErrot
DataView在写入缓冲时,会尽最大努力把一个值转换为合适的类型,后备为0。如果无法转换,则抛出错误
ElementTypeArray
特定于一种ElementType(同DataView中介绍的ElementType) 且遵循系统原生的字节序,提供了适用面更广的API和高性能
读取已有缓冲区创建
const buf = new ArrayBuffer(12);
const int32Ar = new Int32Array(buf)
console.log(int32Ar.length)
console.log(int32Ar.buffer.byteLength)
自有缓冲区创建
const int32Ar = new Int32Array(6)
console.log(int32Ar.length)
console.log(int32Ar.buffer.byteLength)
填充创建
const int32Ar = new Int32Array([1,2,34])
console.log(int32Ar.length)
console.log(int32Ar.buffer.byteLength)
与js数组区别
拥有js数组的非变异方法,通过拥有set和subarray两个特有方法
set()
从数组或定型数组中把值复制到当前定型数组中的指定索引位置
const container = new Int16Array(8)
container.set(Int16Array.of(1,2,3,4),1)
console.log(container)
subarray()
从原始定型数组中复制一个新的定型数组,复制的开始和结束都是可选的
const container = Int16Array.of(1,2,3)
const copy = container.subarray(1,3)
console.log(copy)
上溢下溢
对上下溢出处理都是直接截取靠右边的二进制,存取都是补码形式
Uint8ClampedArray
除了前面介绍的8种类型,还有一种“夹板”数组类型,不允许任何方向溢出。超出最大值255会被取舍为255,超出最小值0会被向上舍入为0
const clampedArray = new Uint8ClampedArray([-1,256,255]);
console.log(clampedArray);
Uint8ClampedArray完全是HTML5 canvas元素的历史遗留,除非真的做canvas相关的开发,否则不用使用它
Map
ECMAScript6以前,在JavaScript中实现“键值对”式存储可以使用Object来方便高效地完成,也就是使用对象属性作为键,再使用属性来引用值。但这种实现并非没有问题,为此TC39委员会专门为“键/值”存储定义了一个规范。
初始化
想在创建的同时初始化实例,可以给map构造函数传入一个可迭代对象,需要包含键/值对数据。可迭代对象中的每个键/值对都会按照迭代顺序插入到新映射实例中
const m1 = new Map([
["k1","v1"],
["k2","v2"]
])
console.log(m1.size)
方法
has
map实例上是否拥有传入的键
get
获取传入的键对应的值,如果没有则返回undefined
set
覆盖设置传入的键值对,支持链式操作
forEach
通过forEach方法可以迭代键值对
keys
以数组形式返回映射键副本
values
以数组形式返回映射值副本
delete
根据传入的键,删除对应的键值对
clear
清除Map中的所有元素
属性
size
返回键值对的数量
与Object差异
1. 与Object只能使用数值、字符串或符号作为键不同,Map可以使用任何JavaScript数据作为键。Map内部使用SameValueZero比较操作(ECMAScript规范内部定义,语言中不能使用),基本上相当于使用严格对象相等的标准来检查键的匹配性
let myMap = new Map()
let nm = 2;
myMap.set(nm,1)
myMap.get(nm)
console.log(myMap);
2. Map实例会维护键值对的插入顺序(覆盖赋值也不会改变这个顺序),映射实例可以提供一个迭代器,能够以插入循序生成[key,value]形式的数组
let myMap = new Map([
['k1','v1'],
['k2','v2']
])
console.log(myMap.entries === myMap[Symbol.iterator]) // true
3.内存占用给定固定大小的内存,Map大约可以比Object多存储50%的键/值对
4.插入性能代码涉及大量插入操作,那么Map的性能更佳
5.查找速度,代码设计大量的查找操作,某些情况下Object更好些
6.删除性能,Map的delete操作比插入何查找更快,代码涉及大量的删除操作,Map的性能更佳
WeakMap
ECMASCript6新增的“弱映射”是一种新的集合类型,为这门语言带来了增强的键/值对存储机制。WeakMap是Map的“兄弟”类型,其API也是Map的子集。WeakMap中的“weak”描述的是JavaScript垃圾回收程序中对待“弱映射”中键的方式
弱映射中的键只能是Object或者继承自Object的类型,尝试使用非对象设置键会抛出TypeError。值的类型没有限制
只是键是弱引用,值不是弱引用(弱引用:不是正式的引用,不会阻止垃圾回收)
不可迭代键
因为WeakMap中的键/值对任何时候都可能被销毁,所以没必要提供迭代器键/值对的能力
DOM节点元数据
因为WeakMap实例不会妨碍垃圾回收,所以非常适合保存关联元数据
const vm = new WeakMap()
const loginButton = document.querySelector('#login');
vm.set(loginButton,{disabled:true}) // 当loginButton节点从页面移除时,自动释放内存,如果是Map需要手动释放对象的引用
Set
ECMAScript6新增的Set是一种集合类型,为这门语言带来了集合数据结构。Set再很多方面都像加强的Map,这个因为它们的大多数API和行为都是共有的
和Map类似,Set可以包含任何JavaScript数据类型作为值,使用Same Value Zero操作进行比较
初始化
在创建时同时初始化Set,需要在构造函数传入一个可迭代对象,其中需要包含插入到新集合实例中的元素
const s1 = new Set([1,2])
方法
add
支持链式操作
delete
has
clear
values、keys、[Symbol.iterator]
返回一个维护插入顺序的迭代器
entries
返回两个元素都是相同的迭代器
forEach
迭代的两个元素都是相同的
属性
size
WeakSet
考核Map和WeakMap的区别
扩展操作符
扩展操作符在对可迭代对象执行浅复制时特别有用,只需简单的语法就可以复制整个对象
let ar1 = [1,2,3];
let ar2 = [...ar1];