Object
创建方式:
let obj={};//推荐此方法(代码少)
或
let obj new Object();
访问对象属性的方法:
let obj={name:"lly",sex:"Man"}
obj.name//lly通常使用此方法
或
obj["name"]//lly
关于Object更全面,更深层次的学习,作者会单独开篇。
Array
创建方式:
let arr=[];//添加子元素 let arr1=new Array();//可以指定数组长度,或者直接添加子元素(如果是数字指定数组长度,其他类型添加子元素) //Array静态方法: 1.Array.from()//将类数组结构转换为数组实例 //场景1:(字符串会被拆分为单字符数组) Array.from("1234567890")// ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] //场景2:(集合和映射转换为一个新数组) const m = new Map().set(1, 2).set(7,8); const s = new Set().add(1).add(2).add(3).add(4); Array.from(m); // [[1, 2], [7, 8]] Array.from(s); // [1, 2, 3, 4] //场景3:(现有数组执行浅复制) let arr1=[1, 2, 3, 4] let arr2=Array.from(arr1) arr1===arr2//false //场景4:(可以使用任何可迭代对象) *[Symbol.iterator]() { yield 1; yield 2; yield 3; yield 4; } }; console.log(Array.from(iter)); // [1, 2, 3, 4] //场景5:(arguments 对象可以被轻松地转换为数组) function getArgsArray() { return Array.from(arguments); } console.log(getArgsArray(1, 2, 3, 4)); // [1, 2, 3, 4] //场景6:(转换带有必要属性的自定义对象) const arrayLikeObject = { 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }; console.log(Array.from(arrayLikeObject)); // [1, 2, 3, 4] //from()接受三个参数参数一对应的是处理的对象参数二对应的是映射函数参数参数三对应的是指定映射函数中 this 的值 let arr=[0,1,2,3] let arrNew=Array.from(arr,function(x){return x**this.exponent},{exponent:3})//[0,1,8,27] 2.Array.of()//用于将一组参数转换为数组实例等价Array.prototype.slice.call(arguments) Array.of(1,2,3,4,5,6)//[1,2,3,4,5,6] Array.of(unedfined)//[unedfined]
数组空位:
let arr=[,,] console.log(arr.length)//2 for(let i of arr){console.log(i)//unedfined,unedfined} //注意 !!!!! //避免使用数组空位。如果确实需要空位,则可以显式地用 undefined 值代替。
数组索引
let arr=["A","B","C","D"] arr[0]//A arr[arr.length-1]//D
检测数组
value instanceof Array
let arr=[];
console.log(arr instanceof Array)//true
2.Array.isArray()
Array.isArray(arr)//true
3.Object.prototype.toString.call(value)
Object.prototype.toString.call(arr)//[object Array]迭代器方法
//访问Array的方法: xx.keys()//数组索引; xx.values()//数组元素; xx.entriess()//索引/值对; let arr=["A","B","C"]; Array.from(arr.keys())// [0, 1, 2] Array.from(arr.values())//["A", "B", "C"] Array.from(arr.entries())//[[0, "A"][1, "B"][2, "C"]]
复制和填充方法
//以下方法需要指定既有数组实例上的一个范围,包含开始索引,不包含结束索引不改变数组的大小 //批量复制方法 copyWithin,按照指定范围浅复制数组中的部分内容,然后将它们插入到指定索引开始的位置。 let ints, reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; reset(); // 从 ints 中复制索引 0 开始的内容,插入到索引 5 开始的位置 // 在源索引或目标索引到达数组边界时停止 ints.copyWithin(5); console.log(ints); // [0, 1, 2, 3, 4, 0, 1, 2, 3, 4] reset(); // 从 ints 中复制索引 5 开始的内容,插入到索引 0 开始的位置 ints.copyWithin(0, 5); console.log(ints); // [5, 6, 7, 8, 9, 5, 6, 7, 8, 9] reset(); // 从 ints 中复制索引 0 开始到索引 3 结束的内容 // 插入到索引 4 开始的位置 ints.copyWithin(4, 0, 3); alert(ints); // [0, 1, 2, 3, 0, 1, 2, 7, 8, 9] reset(); // JavaScript 引擎在插值前会完整复制范围内的值 // 因此复制期间不存在重写的风险 ints.copyWithin(2, 0, 6); alert(ints); // [0, 1, 0, 1, 2, 3, 4, 5, 8, 9] reset(); // 支持负索引值,与 fill()相对于数组末尾计算正向索引的过程是一样的 ints.copyWithin(-4, -7, -3); alert(ints); // [0, 1, 2, 3, 4, 5, 3, 4, 5, 6] //copyWithin静默忽略超出数组边界、零长度及方向相反的索引范围: let ints, reset = () => ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; reset(); // 索引过低,忽略 ints.copyWithin(1, -15, -12); alert(ints); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; reset() // 索引过高,忽略 ints.copyWithin(1, 12, 15); alert(ints); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; reset(); // 索引反向,忽略 ints.copyWithin(2, 4, 2); alert(ints); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; reset(); // 索引部分可用,复制、填充可用部分 ints.copyWithin(4, 7, 10) alert(ints); // [0, 1, 2, 3, 7, 8, 9, 7, 8, 9]; //填充数组方法 fill,可以向一个已有的数组中插入全部或部分相同的值。开始索引用于指定开始填充的位置,它是可选的。如果不提供结束索引,则一直填充到数组末尾。负值索引从数组末尾开始计算。 const zeroes = [0, 0, 0, 0, 0]; // 用 5 填充整个数组 zeroes.fill(5); console.log(zeroes); // [5, 5, 5, 5, 5] zeroes.fill(0); // 重置 // 用 6 填充索引大于等于 3 的元素 zeroes.fill(6, 3); console.log(zeroes); // [0, 0, 0, 6, 6] zeroes.fill(0); // 重置 // 用 7 填充索引大于等于 1 且小于 3 的元素 zeroes.fill(7, 1, 3); console.log(zeroes); // [0, 7, 7, 0, 0]; zeroes.fill(0); // 重置 // 用 8 填充索引大于等于 1 且小于 4 的元素 // (-4 + zeroes.length = 1) // (-1 + zeroes.length = 4) zeroes.fill(8, -4, -1); console.log(zeroes); // [0, 8, 8, 8, 0]; //fill静默忽略超出数组边界、零长度及方向相反的索引范围: const zeroes = [0, 0, 0, 0, 0]; // 索引过低,忽略 zeroes.fill(1, -10, -6); console.log(zeroes); // [0, 0, 0, 0, 0] // 索引过高,忽略 zeroes.fill(1, 10, 15); console.log(zeroes); // [0, 0, 0, 0, 0] // 索引反向,忽略 zeroes.fill(2, 4, 2); console.log(zeroes); // [0, 0, 0, 0, 0] // 索引部分可用,填充可用部分 zeroes.fill(4, 3, 10) console.log(zeroes); // [0, 0, 0, 4, 4]
转换方法
toLocaleString()// toString()和 valueOf()相同的结果 toString()//每个值的等效字符串拼接而成的一个逗号分隔的字符串 valueOf()//数组本身; let arr=["A","B","C"]; arr.toString()//A,B,C arr.valueOf()//["A","B","C"] arr.toLocaleString()//A,B,C //修改数组分割符join("要使用分割符") arr.join('~')//A~B~C //注意!!!! 如果数组中某一项是 null 或 undefined,则在 join()、toLocaleString()、toString()和 valueOf()返回的结果中会以空字符串表示。
栈方法
//方法修改原来的数组 1.push()//在数组后添加元素接受任意个值 2.pop()//获取数组最后一个元素 let arr=[1,2,3] arr.push(4,5) console.log(arr)//[1,2,3,4,5] arr.pop()//5 console.log(arr)//[1,2,3,4]
队列方法
//方法修改原来的数组 1.shift()//获取数组第一元素 2.unshift()//在数组第一个元素前添加元素 let arr=[1,2,3] console.log(arr.shift())//1 arr.unshift(0,1) console.log(arr)//[0,1,2,3]
排序方法
1.reverse()//反向排序 2.sort()//升序排列可接受一个比较函数 let arr=[1,12,3,0,7,18,5] arr.reverse();//[5,18,7,0,3,12,1] function compare(value1, value2) { if (value1 < value2) { return -1; } else if (value1 > value2) { return 1; } else { return 0; } } arr.sort(compare); console.log(arr);//[0, 1, 3, 5, 7, 12, 18]
操作方法
1.concat()//复制原来数组在此基础之上添加元素,不改变原来数组,可以接受任意参数 let arr=[0,1,2] let arr1=arr.concat('4',[5,6]) arr//[0,1,2] arr1//[0,1,2,"4",5,6] 关于数组会拉平 2.slice()//截取接受一个或两个参数表示起始索引和结束索引,不改变原有数组参数可以接受负值,(以数组长度加上对应的负值为索引位置) let arr=[0,1,2,3,4,5,6,7,8,9] arr.slice(1)//[1,2,3,4,5,6,7,8,9] arr.slice(0,4)//[0,1,2,3] arr.slice(-5,-2)//[5,6,7]
- 删除,参数说明:参数1索引起始位置,参数2要删除的元素数
arr.splice(0,3)//[0,1,2]
arr//[3,4,5,6,7,8,9] - 插入 ,参数说明:参数1索引位置,参数2删除元素个数,参数3+ 要查入的元素
arr.splice(0,2,"name","sex","age")//[0,1]
arr//["name", "sex", "age", 2, 3, 4, 5, 6, 7, 8, 9] 替换,参数说明:参数1索引位置,参数2删除元素个数,参数3+ 要查入的元素
arr.splice(0,1,'name')//[0]
arr//["name",1,2, 3, 4, 5, 6, 7, 8, 9]搜索和位置方法
- indexOf()//从前搜索元素,查询到返回元素索引,没查询到返回-1,可以接受一个或两个参数,参数1要查询的元素,参数2查询开始的索引
let arr=['A','B','C','D','E']
arr.indexOf(3)//-1
arr.indexOf('E')//4
arr.indexOf("C",1)//2从索引1开始查询元素'C' - lastIndexOf()//同indexOf用法相同不同是从数组末尾开始查询
- includes()//同indexOf用法相同返回结果为true和false
2.按断言函数搜索 - find((element, index, array)=>{})//element:当前元素 index:当前元素的索引 array:数组 查询后就不在搜索
let arrObj=[{name:"lly",sex:"man",age:"18"},{name:"ly",sex:"woman",age:"18"}]
arr.find((element, index, array)=>{
element//当前元素
index//当前元素的索引
array//数组
})//返回查询到元素 findIndex()//同find用法相同返回查询到的元素的索引
迭代方法
//以下方法不改变原数组 every()://对数组每一项都运行传入的函数,如果对每一项函数都返回 true,则这个方法返,回 true。 let arr=[2,4,6,8,10] arr.every((item,index,array)=>{return item>=2})//true filter()://对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回。 arr.filter((item,index,array)=>{return item>2)})//[4,6,8,10] forEach()://对数组每一项都运行传入的函数,没有返回值。 arr.forEach((item,index,array)=>{ console.log(item*=10)//20,40,60,80,100 }) map()://对数组每一项都运行传入的函数,返回由每次函数调用的结果构成的数组。 arr.map((item,index,array)=>{ item*=10 }) some()://对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true。
归并方法
1.reduce((prev, cur, index, array) => prev + cur)) => {});//参数说明:上一个归并值、当前项、当前项的索引和数 组本身 let arr=[0,1,2,3,4,5,6,7,8,9] arr.reduce((prev, cur, index, array) =>{ console.log(prev, cur, index, array) //过程 //0 1 1 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //1 2 2 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //3 3 3 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //6 4 4 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //10 5 5 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //15 6 6 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //21 7 7 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //28 8 8 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] //36 9 9 (10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] return prev + cur }) 2.reduceRight()//和reduce相同只是归并的方向不同此方法从数组尾部开始
定型数组
Typed Array 是ECMAScript新增的结构,为了提升向原生库传输数据的效率而产生,(一种特殊的包含数值类型的数组),定型数组的架构分为缓冲和视图两部分,缓冲通过ArrayBuffer对象来实现,视图主要用来读和写缓冲区的内容,视图主要包含TypedArray和DataView()。
TypedArray包含:- Uint8Array,Uint16Array,Uint32Array —— 用于 8 位、16 位和 32 位无符号整数。
- Uint8ClampedArray —— 用于 8 位整数,在赋值时便“固定”其值。
- Int8Array,Int16Array,Int32Array —— 用于有符号整数(可以为负数)。
- Float32Array,Float64Array —— 用于 32 位和 64 位的有符号浮点数。
注意:不要把类型化数组与正常数组混淆,因为在类型数组上调用 Array.isArray()
会返回false
。此外,并不是所有可用于正常数组的方法都能被类型化数组所支持(如 push 和 pop)
历史
由于浏览器快速发展,为了满足运行复杂的 3D 应用程序需求,浏览器厂商开发一套 JavaScript API,从而充分利用 3D 图形 API 和 GPU 加速,以便在<canvas>元素上渲染复杂的图形,后来欧朋浏览器专注于2D和3D计算机图形子集,写出WebGL,早期版本由于 JavaScript 数组与原生数组之间不匹配,所以出现了性能问题。为了解决性能问题Mozilla实现了CanvasFloatArray,最后变成了Float32Array,也就是定型数组的第一个类型
ArrayBuffer
ArrayBuffer 是所有定型数组及视图引用的基本单位。ArrayBuffer()是一个普通的 JavaScript 构造函数,可用于在内存中分配特定数量的字节空间。ArrayBuffer 一经创建就不能再调整大小,可以使用 slice()复制其全部或部分到一个新实例中
//创建
let buf=new ArrayBuffer(32)//内存中分配32个字节
buf.byteLength//32查看字节数
let buf1=buf.slice(0,16)
buf1.byteLength//16
ArrayBuffer 某种程度上类似于 C++的 malloc(),但也有几个明显的区别。
- malloc()在分配失败时会返回一个 null 指针。ArrayBuffer 在分配失败时会抛出错误。
- malloc()可以利用虚拟内存,因此最大可分配尺寸只受可寻址系统内存限制。ArrayBuffer分配的内存不能超过 Number.MAX_SAFE_INTEGER(2^53^ 1)字节。
- malloc()调用成功不会初始化实际的地址。声明 ArrayBuffer 则会将所有二进制位初始化为 0。
- 通过 malloc()分配的堆内存除非调用 free()或程序退出,否则系统不能再使用。而通过声明ArrayBuffer 分配的堆内存可以被当成垃圾回收,不用手动释放。
不能通过对ArrayBuffer的引用就能读取或写入其内容,要读取或写入需要通过视图,视图有不同的类型,但引用的都是ArrayBuffer中存储的二进制数据。
DataView
DataView读写ArrayBuffer的视图,专为文件I/O和网络I/O设计,支持对缓冲数据的高度控制,但相比于其他类型的视图性能也差一些。DataView 对缓冲内容没有任何预设,也不能迭代。
let buf=new ArrayBuffer(32);
let dv=new DataView(buf,byteOffset,byteLength);//可以接受三个参数分别是已知buf,缓冲区开始位置,ArrayBuffer的内存
dv.byteOffset // 0 ArrayBuffer缓冲开始位置
dv.byteLength// 32 ArrayBuffer的内存
dv.buffer === buf // true
DataView读取缓冲需要的组件
- 首先是要读或写的字节偏移量。可以看成 DataView 中的某种“地址”。
- DataView 应该使用 ElementType 来实现 JavaScript 的 Number 类型到缓冲内二进制格式的转换。
- 最后是内存中值的字节序。默认为大端字节序。
- ElementType
ElementType | 字 节 | 说 明 | 等价的 C 类型 | 值的范围 |
---|---|---|---|---|
Int8 | 1 | 8位有符号整数 | signed char | 128~127 |
Uint8 | 1 | 8位无符号整数 | unsigned char | 0~255 |
Int16 | 2 | 16位有符号整数 | short | 32 768~32 767 |
Uint16 | 2 | 16 位无符号整数 | unsigned short | 0~65 535 |
Int32 | 4 | 32 位有符号整数 | int | 2 147 483 648~2 147 483 647 |
Uint32 | 4 | 32 位无符号整数 | unsigned int | 0~4 294 967 295 |
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(字节偏移
量)定位要读取或写入值的位置,类型可以相互转换。
2.字节序
计算系统维护的一种字节顺序的约定,DataView 只支持两种约定:大端字节序(网络字节序)和小端字节序,大端字节序中最高有效位保存在第一个字节,而最低有效位保存在最后一个字节,小端字节序中最低有效位保存在第一个字节,最高有效位保存在最后一个字节
3.边界情形
- DataView 完成读、写操作的前提是必须有充足的缓冲区,否则就会抛出 RangeError
- DataView 在写入缓冲里会尽最大努力把一个值转换为适当的类型,后备为 0。如果无法转换,则抛出错误
定型数组
定型数组是ArrayBuffer的视图,用来读写缓存区的内容。
定型数组主要有:
对象 | 说明 |
---|---|
Uint8Array | 8 位无符号整数 |
Uint16Array | 16 位无符号整数 |
Uint32Array | 32 位无符号整数 |
Uint8ClampedArray | 8 位整数,在赋值时便“固定”其值 |
Int8Array | 有符号整数(可以为负数) |
Int16Array | 有符号整数(可以为负数) |
Int32Array | 有符号整数(可以为负数) |
Float32Array | 32 位有符号浮点数 |
Float64Array | 64 位有符号浮点数 |
定型数组方法:
1.定型数组行为(操作符、方法和属性)
定型数组行为 | 说明 |
---|---|
[] | |
copyWithin() | 浅复制数组的一部分到同一数组中的另一个位置 |
entries() | 索引/值对 |
every() | 迭代方法 |
fill() | 使用给定值,填充一个数组 |
filter() | 迭代方法 |
find() | 搜索到就不在搜索 |
findIndex() | 用法和find()相同返回搜索到元素的索引 |
forEach() | 迭代方法 |
indexOf() | 从前搜索元素 |
join() | 转换方法 |
keys() | 数组索引 |
lastIndexOf() | 从后搜索元素 |
length | 长度 |
map() | 迭代方法 |
reduce() | 归并方法上一个归并值、当前项、当前项的索引和数组本身 |
reduceRight() | 同reduce()用法相同,方向相反 |
reverse() | 反向排序 |
slice() | 截取 |
some() | 迭代方法 |
sort() | 升序排列 |
toLocaleString() | 转换方法 |
toString() | 转换方法 |
values() | 数组元素 |
2.合并、复制和修改定型数组
合并、复制和修改定型数组 | 说明 |
---|---|
concat() | 复制原来数组在此基础之上添加元素 |
pop() | 获取数组最后一个元素 |
push() | 数组后添加元素 |
shift() | 获取数组第一个元素 |
splice() | 较为全面的操作方法,删除,插入,替换 |
unshift() | 在数组的第一个元素前添加元素 |
3.下溢和上溢
定型数组中值的下溢和上溢不会影响到其他索引,但仍然需要考虑数组的元素应该是什么类型。定型数组对于可以存储的每个索引只接受一个相关位,而不考虑它们对实际数值的影响。
// 长度为 2 的有符号整数数组
// 每个索引保存一个二补数形式的有符号整数
// 范围是-128(-1 * 2^7)~127(2^7 - 1)
const ints = new Int8Array(2);
// 长度为 2 的无符号整数数组
// 每个索引保存一个无符号整数
// 范围是 0~255(2^7 - 1)
const unsignedInts = new Uint8Array(2);
// 上溢的位不会影响相邻索引
// 索引只取最低有效位上的 8 位
unsignedInts[1] = 256; // 0x100
console.log(unsignedInts); // [0, 0]
unsignedInts[1] = 511; // 0x1FF
console.log(unsignedInts); // [0, 255]
// 下溢的位会被转换为其无符号的等价值
// 0xFF 是以二补数形式表示的-1(截取到 8 位),
// 但 255 是一个无符号整数
unsignedInts[1] = -1 // 0xFF (truncated to 8 bits)
console.log(unsignedInts); // [0, 255]
// 上溢自动变成二补数形式
// 0x80 是无符号整数的 128,是二补数形式的-128
ints[1] = 128; // 0x80
console.log(ints); // [0, -128]
// 下溢自动变成二补数形式
// 0xFF 是无符号整数的 255,是二补数形式的-1
ints[1] = 255; // 0xFF
console.log(ints); // [0, -1]
//不允许任何方向溢出。超出最大值 255 的值会被向下舍入为 255,而小于最小值 0 的值会被向上舍入为 0。
const clampedInts = new Uint8ClampedArray([-1, 0, 255, 256]);
console.log(clampedInts); // [0, 0, 255, 255]
Map
映射,一种集合类型,为JavaScript提供真正的键值存储机制
使用方式:
//**Map对象可以使用JavaScript中所有的数据类型作为键
//创建
let m = new Map();
//添加
m.set("key1",1).set("key2",2)
//查询
m.has("key1")//包含true不包含false
m.get("key")//返回键值
m.size//键值对的个数
//删除
m.delete("key1")//删除指定的键值对
m.clear()//清空所有的键值对
顺序和迭代
let m=new Map([[key1,1],[key2,2],[key3,3]])
m.entries===m[Symbol.iterator]//true
m.entries()//MapIterator {"key1" => 1, "key2" => 2, "key3" => 3}
[...m]//[["key1", 1], ["key2", 2], ["key3", 3]]
//可以通过for ...of方法实现迭代
for(let i of obj){
//obj是m
console.log(i)//["key1", 1], ["key2", 2], ["key3", 3]
//obj是m.entries()
console.log(i)//[["key1", 1], ["key2", 2], ["key3", 3]]
//obj是m[Symbol.iterator]()
console.log(i)//["key1", 1], ["key2", 2], ["key3", 3]
}
m.forEach((value,key)=>{
value//1,2,3
key//"key1","key2","key3"
})
m.keys()//MapIterator {"key1", "key2", "key3"}
m.values()//MapIterator {1, 2, 3}
//键和值在迭代器遍历时是可以修改的,但映射内部的引用则无法修改
let m1=new Map([["name":"lly"]])
for(let key of m1.keys()){
key="name1"
console.log(key)//name1
console.log(m1.get("name"))//lly
}
开发中关于Object和Map使用的选择
1.内存占用
给定内存Map比Object存储的键值大50%
2.插入性能
在插入方面Map比Object稍微快些,涉及大量插入操作Map是更优选择
3.查找速度
如果存在大量查询操作选择Object是更优选择
4.删除性能
如果涉及到大量删除操作Map是最优选择
WeakMap
弱映射,一种集合类型,为JavaScript提供增强的键/值对存储机制
使用方法
//**weakMap中的键只能是 Object 或者继承自 Object 的类型
//创建
const key={id:1},key1={id:2},key2={id:3}
const wm = new WeakMap([[key,001],[key1,002],[key2,003]]);
//查询
wm.get(key)//1返回键值
wm.has(key1)//true返回boolean
//添加
const key3={id:4},key4={id:5}
wm.set(key3,4).set(key4,5)
wm//WeakMap {{…} => 5, {…} => 3, {…} => 4, {…} => 1, {…} => 2}
//删除
wm.delete(key3);//删除指定键值对
弱键
WeakMap中的弱映射的键,不属于正式的引入,不会阻止垃圾回收的处理,但是弱映射的值,只要键存在,键值对就存在映射中,并被当作对值的引用,不会被当作垃圾回收。
const wm = new WeakMap();
wm.set({}, "val");
//由于空对象被当做一个字符串的键,对象没有被指向引用,会被当作垃圾回收,之后键值对就在映射中消失成为一个空映射
const wm = new WeakMap();
const container = {
key: {}
};
wm.set(container.key, "val");
function removeReference() {
container.key = null;
}
//container对象维护着一个对弱映射键的引用,所有这个对象键不会被当作垃圾回收,只有执行 removeReference方法后键值对才会被销毁。
不可迭代键
因为 WeakMap 中的键/值对任何时候都可能被销毁,所以没必要提供迭代其键/值对的能力。
弱映射的使用场景
1.私有变量
const User = (() => {
const wm = new WeakMap();
class User {
constructor(id) {
this.idProperty = Symbol('id');
this.setId(id);
}
setPrivate(property, value) {
const privateMembers = wm.get(this) || {};
privateMembers[property] = value;
wm.set(this, privateMembers);
}
getPrivate(property) {
return wm.get(this)[property];
}
setId(id) {
this.setPrivate(this.idProperty, id);
}
getId(id) {
return this.getPrivate(this.idProperty);
}
}
return User;
})();
const user = new User(123);
user.getId(); // 123
user.setId(456);
user.getId(); // 456
DOM 节点元数据
WeakMap 实例不会妨碍垃圾回收,所以非常适合保存关联元数据。由于WeakMap的主要优点是它对对象是弱引用,所以被它们引用的对象很容易地被垃圾收集器移除。它用作“主要”对象存储之外的“辅助”数据结构。一旦将对象从主存储器中删除,如果该对象仅被用作 WeakMap 的键,那么它将被自动清除。let wm=new WeakMap(); let login=document.querySelector('#login'); m.set(login, {disabled: true}); //那么当节点从 DOM 树中被删除后,垃圾回收程序就可以立即释放其内存(假设没有其他地方引用这个对象)
Set
一种集合类型,Map的加强版
使用方法
//Set对象可以使用JavaScript中所有的数据类型作为值
//创建
let s=new Set(["value1", "value2", "value3"]);
//添加
s.add("value4").add("value5")//Set(5) {"value1", "value2", "value3", "value4", "value5"}
//查询
s.has("value1")//true 返回boolean值
s.size//5
//删除
s.delete("value1")//删除指定的值
s.clear()//删除所用的值
顺序与迭代
let s=new Set(["val1","val2","val3","val4"]);
s.values\s.keys\s[Symbol.iterator] //ƒ values() { [native code] }
s.values === s[Symbol.iterator] // true
s.keys === s[Symbol.iterator] // true
s.values()/s.keys()//SetIterator {"val1", "val2", "val3", "val4"}
//迭代
for(let item of 迭代对象){
//迭代对象:s.values()/s[Symbol.iterator]()/s
item//"val1","val2","val3","val4"
//迭代对象:s.entries()
item// ["val1", "val1"]、["val2", "val2"]、["val3", "val3"]、["val4", "val4"]
}
//集合转数组可以通过扩展操作符
[...s]// ["val1", "val2", "val3", "val4"]
定义正式集合操作
* 某些 Set 操作是有关联性的,因此最好让实现的方法能支持处理任意多个集合实例。
* Set 保留插入顺序,所有方法返回的集合必须保证顺序。
* 尽可能高效地使用内存。扩展操作符的语法很简洁,但尽可能避免集合和数组间的相互转换能够节省对象初始化成本。
* 不要修改已有的集合实例。union(a, b)或 a.union(b)应该返回包含结果的新集合实例。
WeakSet
WeakSet一种集合类型,是Set的兄弟类型。
使用方法
//!弱集合中的值只能是 Object 或者继承自 Object 的类型
//创建
const val1={id:1},val2={id:2},val3={id:3}
let ws=new WeakSet([val1,val2,val3]);
//查找
ws.has(val1)//true 返回boolean
//添加
const val4={id:4},val5={id:5}
ws.add(val4).add(val5)//
//删除
ws.delete(val5)//指定删除项
弱值
WeakSet 中的值不属于正式的引用,不会阻止垃圾回收。
const ws = new WeakSet();
ws.add({});
//新对象用作值,没有被指向引用,会被当作垃圾回收,这个值在弱集合中消除。
const ws = new WeakSet();
const container = {
val: {}
};
ws.add(container.val);
function removeReference() {
container.val = null;
}
//container 对象维护着一个对弱集合值的引用,因此这个对象值不会成为垃圾回收的目标。如果调用了 removeReference(),就会摧毁值对象的最后一个引用,垃圾回收程序就可以把这个值清理掉。
不可迭代值
WeakSet 中的值任何时候都可能被销毁,所以没必要提供迭代其值的能力,
使用弱集合
WeakSet的实例没有WeakMap用处大,
使用场景:给对象打标签
const disabledElements = new WeakSet();
const loginButton = document.querySelector('#login');
// 通过加入对应集合,给这个节点打上“禁用”标签
disabledElements.add(loginButton);
//只要 WeakSet 中任何元素从 DOM 树中被删除,垃圾回收程序就可以忽略其存在,而立即释放其内存(假设没有其他地方引用这个对象)。
迭代与扩展操作
迭代器和扩展运算符对集合引用类型(Array、定向数组、Map、Set)的之间的相互操作、复制、修改非常便利。
迭代器 :for ...of
扩展操作符 :[...]可以对可迭代对象执行浅复制时非常方便。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。