复习 Array,重学 JavaScript
1 数组与对象
在 JavaScript 中,一个对象的键只能有两种类型:string
和 symbol
。下文只考虑键为字符串的情况。
1.1 创建对象
在创建对象时,若对象的键为数字,或者由 字母+数字 组成,那么键上的引号可以省去:
var obj1 = {1: 'one', 2: 'two'} // 等同于 {'1': 'one', '2': 'two'}
var obj2 = {'one': 1, 'two': 2} // 等同于 {one: 1, two: 2}
如果对象的键由 数字+字母 组成,那么键上的引号不能省去:
var obj = {'3d': true} // √
var obj = {3d: true} // ×
1.2 访问对象
在 JavaScript 中,访问一个对象的属性有三种方式++:++
- 方括号 + 字符串:
obj['prop']
- 方括号 + 字符串变量:
obj[keyName]
- 点 + 键名
var obj = {prop: 666, 1: 'one'}
obj.prop // => 666
obj['prop'] // => 666
// 若方括号内为数字,语法解释器会将其转换为字符串类型
obj['1'] === obj[1] // => true (*)
let keyName = 'prop'
obj[keyName] // => 666
// 访问不存在的属性时,输出 undefined
obj.keyName // => undefined
obj['keyName'] // => undefined
// 若方括号内既非数字也非字符串,语法解释器会将其解释为变量
obj[variable] // 将会报错,因为变量 variable 没有定义
上面代码的 (*) 行,obj[1]
方括号内是一个数组,语法解释器会自动将其转换成字符串类型,因此 obj['1']
和 obj[1]
其实是一个东西,这样,我们就找到了 JavaScript 中的数组与对象的相通之处。
const arr = ['a', 'b', 'c']
arr[0] === arr['0'] // => true
Object.keys(arr) // => ['0', '1', '2'] 注意输出的不是 [0, 1, 2]
可以看到,虽然我们习惯通过索引获取数组的元素,但在语法内部还是通过对象的键获取对应的值来实现的。根据这一特性构造一个伪数组:
const arrayLike = {0: 'a', 1: 'b', 2: 'c', length: 3}
arrayLike[0] === arrayLike['0']
1.3 数据类型
JavaScript 的八大数据类型分别是:number
、bigint
、boolean
、string
、null
、undefined
、symbol
、object
,前 7 种称为基本数据类型,object
为唯一的复杂数据类型。
JavaScript 中有
Number
、String
、Object
......但他们是作为构造函数实际存在的。而number
、string
、object
等在 JavaScript 中并不存在,是我们用来描述数据类型的。
基本数据类型的数据都是作为一个整体存在,复制操作是直接创建数据的副本,复制以后两个基本数据变量不会相互影响。
复杂数据类型的变量存放的是一个引用,复制以后两个变量指向的都是同一个内存地址,其中一个变量对数据作出改变会影响至另一个变量。
Array
并不是 JavaScript 中八大数据类型的其中一种,它之所以存在,是因为实际应用经常会有一些数据是有序的,而数组正是一种 有序 的数据集合。
我们可以认为: 数组 = 对象 + 额外的特性,这里 额外的特性 就是指数组专有的属性和方法,如表示数组长度的 length
,可以进行数组拼接的 concat()
方法......
一切皆为对象,这是 JavaScript 世界中一句广为流传的话,也就是说,如果一个东西不属于 7 个基本数据类型,都可以认为它是基于对象扩展而来的一个新对象,也都能套用上面的公式:对象 + 额外的特性 = 新对象。
上面说过,JavaScript 中是实际存在 Object
的,它是一个构造函数。Object
有很多方法可以帮助我们了解一个新对象的特性:
Object.keys(o: object): string[]
返回对象中所有enumerable = true
(可枚举)的键Object.values(o: object): any[]
返回对象中所有可枚举的键的值Object.entries(o: object): any[]
以二维数组的格式返回键值对Object.getOwnPropertyNames(o: object): string[]
返回对象中所有的属性名(也就是键),包括enumerable = false
的属性Obejct.getOwnPropertyDescriptors(o: object): object
返回对象所有属性的描述符
获取数据类型:
typeof o
o.__proto__.constructor.name
Object.prototype.toString.call(o)
2 数组 Array
Array
表示数组的构造函数,Array === Array.prototype.constructor
将输出 true
。
据我观察,JavaScript 中首字母大写的都是构造函数.......
Object === Object.prototype.constructor // => true
Date === Date.prototype.constructor // => true
Map === Map.prototype.constructor // => true
......
上图中,将 Array
换成 Date
Map
RegExp
等 JavaScript 中的其他对象,各关系依然成立。
通过 Object.getOwnPropertyNames()
即可获取该对象下所有的属性(包括不可枚举的):
Object.getOwnPropertyNames(Array) // => ["length", "name", "prototype", "isArray", "from", "of"]
Object.getOwnPropertyNames(Date) // => ["length", "name", "prototype", "now", "parse", "UTC"]
......
2.1 Array
的属性
Array.length: number
构造函数的属性,是静态的,固定值为 1
Array.name: string
构造函数的静态属性,固定值为 "Array"
Array.prototype: any[]
数组的原型对象,可认为它是程序中所有数组实例的“母体”
[].__proto__ === Array.prototype // => true
// 可以认为 Array.prototype ≈ []
Object.prototype.toString.call(Array.prototype) // => '[object Array]'
Array.prototype.length // => 0
Array.prototype.max = function () {
let currentArr = this // “谁调用该方法,this 就是谁”
let max = -Infinity // 无穷小
for (let i = 0; i < currentArr.length; ++i) {
max = currentArr[i] > max ? currentArr[i] : max
}
return max
}
[6, 9, 7].max() // => 9
2.2 Array
的方法
Array.isArray(param: any): boolean
判断传入的参数是否为数组
// 下面均返回 true
Array.isArray([1])
Array.isArray([]) // 构造空数组
Array.isArray(Array()) // 构造空数组
Array.isArray(new Array()) // 构造空数组
Array.isArray(Array.prototype) // 数组的原型就是数组!
Array.from(arrayLike any, callback?: Function, thisArg?: any): any[]
根据传入的 arrayLike
创建一个数组,并将其返回。arrayLike
应该是下面两种对象的某一种:
// 伪数组对象
Array.from({0: 'a', 1: 'b', length: 2}) // => Array ["a", "b"]
// 可迭代对象
Array.from(['a', 'b'].keys()) // => Array [0, 1]
若指定了 callback
和 thisArg
,那么 Array.from(arrayLike, callback, thisArg)
等同于 Array.from(arrayLike).map(callback, this)
,即让生成的数组调用一次 map()
方法,实现对数组中元素的“再加工”。
Array.from([1, 2, 3], el => el * 2) // [2, 4, 6]
Array.of(...items): any[]
返回一个数组,数组元素由传入的参数构成
Array.of(66, null, {}, "ok") // => [ 66, null, {}, "ok" ]
Array.of(8).length // => 1
Array(8).length // => 8
3 数组实例
一个 JavaScript 程序中,可以有很多数组实例,这些数组实例的原型只有一个:Array.prototype
[].__proto__ === Array.prototype // => true
[1, 2, 3].__proto__ === Array.prototype // => true
在浏览器控制台中输入 Array.prototype
,即可查看当前环境支持的数组全部方法和属性,数组实例都可使用这些方法和属性
可以看到输入 Array.prototype
首先返回的是 []
(绝大部分场景下,Array.prototype
是可以用 []
代替的);其中有两个属性的属性名为 symbol
类型:
Array.prototype[Symbol.iterator] === Array.prototype.values // => true
Array.prototype[Symbol.unscopables]
/* =>
{
copyWithin: true,
entries: true,
fill: true,
find: true,
findIndex: true,
flat: true,
flatMap: true,
includes: true,
keys: true,
values: true
} */
属性值为原始数据类型的只有一个 length
,剩下的就是数组成员方法了。
3.1 创建数组实例
4 种方式,创建数组 [1, 2, 3]
[1, 2, 3]
new Array(1, 2, 3)
Array(1, 2, 3)
Array.prototype.constructor(1, 2, 3)
4 种方式,创建长度为 3 的空数组(数组中 3 个元素均为 undefined
)
new Array(3)
Array(3)
Array.prototype.constructor(3)
var arr = []
arr.length = 3
3.2 数组实例的属性
再次说明:下方的 Array.prototype
都能用 []
替换
Array.prototype.length: number
通过 length
属性,可以获取、设定一个数组的长度
设定一个比当前 length
更大的数值,可以实现对数组的扩充(扩充的元素为 undefined
)
const arr = ['a', 'b', 'c']
arr.length = 4
arr[3] // => undefined
arr // => ["a", "b", "c", empty];即相当于 {0: 'a', 1: 'b', 2: 'c', length: 4}
通过设定一个比当前 length
更小的数值,可以实现对数组的裁剪
const arr = ['a', 'b', 'c']
arr.length = 1
arr[1] // => undefined
arr // => ["a"]
3.3 数组实例的方法
下列方法中,如果方法的参数为索引,一般都可以是负索引,如 -1 是倒数第一个元素的索引,-2 是倒数第二个元素的索引......
3.3.1 修改器方法
若数组调用了修改器方法,那么该数组将会 发生变化。常用的修改器方法有如下几个:
copyWithin()
fill()
pop()
push()
shift()
unshift()
reverse()
sort()
splice()
Array.prototype.copyWithin(target: number, start?: number, end?:number): any[]
复制数组 start
(默认为 0)到 end
(默认为数组长度) 位置上的元素,再从 target
位置开始粘贴;返回改变后的数组。
let arr = ['a', 'b', 'c', 'd', 'e']
// 复制 arr[3], arr[4], 从 arr[2] 开始进行粘贴
arr.copyWithin(2, 3, 5) // => ["a", "b", "d", "e", "e"]
Array.prototype.fill(value: any, start?: number, end?: number): any[]
用 value
填充数组,填充的位置是从 start
(默认为0) 到 end
(默认为数组长度);返回改变后的数组。
let arr = ['a', 'b', 'c', 'd', 'e']
arr.fill([], 3, 5) // => ["a", "b", "c", [], []]
arr.fill('哈') // => ["哈", "哈", "哈", "哈", "哈"]
Array.prototype.pop(): any
删除数组最后一个元素;数组长度将减 1;返回被删除的元素。
let arr = ['a', 'b', 'c']
arr.pop() // => "c"
arr // => ["a", "b"]
Array.prototype.push(...items): number
将参数中的值追加到数组后面;返回数组长度。
let arr = ['a','b']
arr.push('c','d') // => 4
arr // => ["a", "b", "c", "d"]
Array.prototype.shift(): any
删除数组实例的第一个元素;数组长度将减 1;返回被删除的元素
let arr = ['a', 'b']
arr.shift() // => 'a'
arr // Outpus: ['b']
Array.prototype.unshift(...items): number
将传入的参数添加到数组的头部;返回改变后的数组长度
let arr = ['a', 'b']
arr.unshift('A', 'B') // => 4
arr // => ["A", "B", "a", "b"]
Array.prototype.reverse(): any[]
翻转数组实例中元素排列的顺序;返回翻转后的数组
let arr = ['a', 'b', 'c']
arr.reverse() // => ["c", "b", "a"]
arr // => ["c", "b", "a"]
Array.prototype.sort(compare?: Function): any[]
对数组中的元素进行排序;返回排序后的数组
const arr = ['b', 'c', 'a']
arr.sort() // => ["a", "b", "c"]
arr // => 同上
sort()
会试图将数组元素转换为字符串类型,再以字符串的标准进行排序
[2, 31, 111].sort() // [111, 2, 31]
['2', '31', '111'].sort() // ["111", "2", "31"]
可以自定义比较函数,当比较函数返回负数时,排序会发生变化
// arr.reverse() <==> arr.sort(() => -1)
['哈哈', '嘿嘿', '嘻嘻'].sort(() => -1) // => ["嘻嘻", "嘿嘿", "哈哈"]
[1, 2, 3, 4].sort((a, b) => {
// 第一轮比较时,b = 1, a = 2,后面也是按照这种先后顺序
return b - a
}) // => [4, 3, 2, 1]
Array.prototype.splice(start: number, deleteCount?: number, ...items): any[]
删除从 start
开始的 deleteCount
个元素,再将 items
插入此处;返回被删除的所有元素
const arr1 = ['a', 'b', 'c', 'd', 'e']
arr1.splice(2) // => ["c", "d", "e"]
arr1 // => ["a", "b"]
const arr2 = ['a', 'b', 'c', 'd', 'e']
arr2.splice(2, 2) // => ["c", "d"]
arr2 // => ["a", "b", "e"]
const arr3 = ['a', 'b', 'c', 'd', 'e']
arr3.splice(-2, 1, 'dd') // => ["d"]
arr3 // => ["a", "b", "c", "dd", "e"]
3.3.2 访问方法
数组调用访问方法不会改变自身,它会返回访问方法期望的值。常用的访问方法有:concat()
includes()
join()
slice()
toString()
indexOf()
lastIndexOf()
Array.prototype.concat(...items): any[]
将参数拼接到数组末尾;返回拼接后的数组
const arr = [1, 2]
arr.concat(3, 4) // => [1, 2, 3]
arr // => [1, 2]
如果传入的参数是个数组,那么会将该数组展开再拼接,但至多展开一层
[].concat('a', ['b', 'c'], [1, [2,2]]) // => ["a", "b", "c", 1, [2, 2]]
实现数组的扁平化:
function flat(arr) {
let rst = []
for (let i = 0; i < arr.length; ++i) {
if (arr[i].__proto__.constructor.name == 'Array') {
rst = rst.concat(flat(arr[i]))
} else {
rst = rst.concat(arr[i])
}
}
return rst
}
flat(['a', ['b', 'c'], [1, [2,2]]]) // => [ "a", "b", "c", 1, 2, 2 ]
Array.prototype.includes(searchElement: any, fromIndex?: number): boolean
从 fromIndex
(默认为 0)开始,判断一个数组是否含有 searchElement
const arr = ['a', 'b', 'c']
arr.includes('b') // => true
arr.includes('b', -1) // => false
Array.prototype.indexOf(searchElement: any, fromIndex?: number): number
从 fromIndex
(默认为 0)开始,在数组中寻找 searchElemnt
, 若找到了则返回该元素所在的索引,没找到则返回 -1
const subArr = [3]
const arr = [1, [2], subArr]
arr.indexOf(1) // => 0
arr.indexOf([2]) // => -1
arr.indexOf(subArr) // => 2
Array.prototype.lastIndexOf(searchElement: any, fromIndex?: number): number
indexOf()
是从前往后寻找,lastIndexOf()
则是从后往前寻找
Array.prototype.slice(begin?: number, end?: number): any[]
提取数组区间从 begin
(默认为 0) 到 end
(默认为数组长度) 之间的元素;返回提取的元素组成的数组
const arr = ['a', 'b', 'c', 'd', 'e']
arr.slice(2, 4) // => ["c", "d"]
arr.slice(-2) // => ["d", "e"]
arr // => ["a", "b", "c", "d", "e"]
Array.prototype.join(separator?: string): string
将数组中的元素用分隔符 separator
(默认为 ,
) 拼接;返回拼接后的字符串
const arr = ['a', 'b', 'c']
arr.join(',') // => 'a,b,c'
arr.join() // => 同上
arr // => ['a', 'b', 'c']
Array.prototype.toString(): string
用 ','
将数组元素拼接成字符串,因此 arr.toString()
、arr.join
和 arr.join(',')
这三者效果是相同的。
如果一个数组被当作字符串进行字符串拼接时,将隐式调用 toString()
方法
const arr = [1, 2]
'number: ' + arr // => "number: 1,2"
3.3.3 迭代方法
迭代方法会接收一个回调函数作为参数,数组中的元素会按照从前往后的顺序,依次作为参数传入回调函数中。常用的迭代方法有:forEach()
every()
some()
filter()
find()
findIndex()
map()
reduce()
reduceRight()
。
回调函数一般都会有如下三个参数:
element
当前传入回调函数中的数组元素index
当前传入回调函数中的元素对应的索引array
调用迭代方法的数组实例
除了回调函数,迭代方法一般还会接收另一个参数 thisArg: any
,它可作为回调函数中 this
的指向。
迭代方法不会改变数组,为了代码的可读性,请不要在回调函数中对数组进行修改操作。
Array.prototype.forEach(callback: Function, thisArg?: any): undefined
该方法是经典 for
循环的简便写法
let arr = [1]
arr.forEach(function () {
console.log(this === arr)
}) // => false
arr.forEach(function () {
console.log(this === arr)
}, arr) // => true
Array.prototype.every(callback: Function, thisArg?: any): boolean
判断数组中的每一个元素,是否都能通过回调函数的测试;若每次回调函数返回的都是 truthy,那么该方法将返回 true
,否则返回 false
[3, 5, 1].every(el => el > 0) // => true
只要有一次回调函数返回的是 falsy,那么 every()
方法将立即结束,返回 false
[1, 2, 3].every(el => {
console.log(el) // 分别打印 1 和 2
return el != 2
}) // => false
Array.prototype.some(callback: Function, thisArg?: any): boolean
判断数组是否存在能通过回调函数的测试的元素;只要有一次回调函数返回的是 truthy,那么方法立即结束,返回 true
;若所有回调函数返回的都是 falsy,那么方法返回 false
['a', 'b', 'c'].some(el => el === 'b') // => true
[false, 0, '', null, undefined, NaN].some(el => el) // => false
Array.prototype.filter(callback: Function, thisArg?: any): any[]
根据测试条件过滤元素;如果回调函数返回 truthy,那么暂存当前传入回调函数的数组元素,方法结束后,这些暂存的元素将组成一个新的数组,作为方法的返回值
[1, 2, 3, 4, 5].filter(el => el % 2 === 0) // => [2, 4]
Array.prototype.map(callback: Function, thisArg?: any): any[]
方法返回一个数组,数组元素由回调函数每次返回的结果组成
[1, 2, 3].map(el => el * 2) // => [2, 4, 6]
Array.prototype.find(callback: Function, thisArg?: any): any | undefined
寻找符合测试条件的元素;在回调函数 第一次 返回 truthy 时,方法结束,返回此时轮到的数组元素;若回调函数始终不返回 truthy,方法将返回 undefined
[2, 8, 11, 5].find(el => el > 10) // => 11
Array.prototype.findIndex(callback: Function, thisArg?: any): number
寻找符合测试条件的元素的索引;在回调函数 第一次 返回 truthy 时,返回此时轮到的数组元素的索引;若回调函数始终不返回 truthy,方法将返回 -1
[2, 8, 11, 5].findIndex(el => el > 10) // => 2
Array.prototype.reduce(callback: Function, firstValue?: any): any
通过回调函数中的运算规则,计算 array[i]
和 array[i + 1]
,计算的结果作为下一轮回调函数的参数,与 array[i + 2]
再进行计算……返回最后一次回调函数计算的结果。
在之前的迭代方法中,回调函数接收 3 个参数:callback(element?: any, index?: number, array?: any[]){}
,reduce()
中的回调函数将会接收四个参数:callback(accumulator?: any, element?: any, index?: number, array?: any[]) {}
。可以看出 reduce()
多出一个 accumulator
参数,其译为 累积器,它的值是上一轮回调函数返回的值。
['x', 'y', 'z'].reduce((accumulator, element) => {
console.log(accumulator, element) // 将依次输出:1. x y 2. xy z
return accumulator + element
}) // => "xyz"
从上面的例子中还能看出:
accumulator
初始值为数组第一个元素值- 一个长度为 N 的数组,调用
reduce()
方法,回调函数执行 N -1 次
因此,单元素的数组调用 reduce()
方法,将始终返回第一个元素值
['first'].reduce(() => 'end') // => 'first'
['first'].reduce(a => a + ' end') // => 同上
reduce()
返回最后一次回调函数的结果
[[1, 1], [2, 2]].reduce((accumulator, element, index, arr) => {
accumulator = accumulator.concat(element)
return accumulator
}) // => [1, 1, 2, 2]
[[1, 1], [2, 2]].reduce((accumulator, element, index, arr) => {
accumulator = accumulator.concat(element)
return index === arr.length - 1 ? 'end' : accumulator
}) // => 'end'
若指定了 reduce()
的第二个参数 firstValue: any
,那么 accumulator
的初始值就是 firstValue
,执行回调函数的次数也将多 1 次
[1, 2].reduce((accumulator, element) => {return accumulator + element}) // => 3
[1, 2].reduce((accumulator, element) => {return accumulator + element}, 97) // => 100;不是 99!
实现数组去重:
function deDuplication(arr) {
return arr.reduce((accumulator, el) => {
return accumulator.includes(el) ? accumulator : accumulator.concat(el)
}, [])
}
deDuplication([1,1,2,3,2,3,3]) // => [1, 2, 3]
Array.prototype.reduceRight(callback: Function, firstValue?: any): any
reduce()
是从前往后运算,reduceRight()
是从后往前运算;其他方面一致
3.3.4 获得迭代器对象的方法
keys()
返回一个迭代器对象,其中包含所有元素的键values()
返回一个迭代器对象,其中包含所有元素的值entries()
返回一个迭代对象,包含所有数组元素及索引
let arr = ['a', 'b', 'c']
let keyIterator = arr.keys()
keyIterator.next() // => {value: 0, done: false}
keyIterator.next() // => {value: 1, done: false}
keyIterator.next() // => {value: 2, done: false}
keyIterator.next() // => {value: undefined, done: true}
for (let value of arr.values()) {
console.log(value)
}
// =>
// 'a'
// 'b'
// 'c'
console.log(...arr.entries()) // => [0, 'a'] [1, 'b'] [2, 'c']
3.3.5 数组实例的方法小结
- 如果要查找符合某条件的元素,使用
find()
- 如果要查找符合某件的一组元素,使用
filter()
- 如果要确定符合某条件的元素的索引,使用
findIndex()
- 如果要确定某元素的索引,使用
indexOf()
或lastIndexOf()
- 如果要判断数组是否包含某个元素,使用
includes()
- 如果要判断数组元素是否全都符合某条件,使用
every()
- 如果要判断数组是否有符合某条件的元素,使用
some()
- 具备拷贝功能的方法:
concat()
、slice()
以及所有的迭代方法 - 尾操作
push()
和pop()
比头操作shift()
和unshift()
效率更高
4 通用性
数组的很多方法被设计成是通用的,也就是说非数组对象也可以调用数组的方法。下面是几个示例:
fill()
的通用用法:
let length = 3
Array.prototype.fill.call({length}, 'x') // => {0: "x", 1: "x", 2: "x", length: 3}
[].fill.apply({'length': 3}, ['x', 0, 3]) // 输出同上
map()
的通用用法:
// 输出字符串中每个字符的代码
[].map.call('ABC', el => el.charCodeAt()) // => [65, 66, 67]
includes()
的通用用法:
// 判断函数输入的参数中是否有某个值
function hasParam(arguments_, param) {
// 每个函数的 arguments 虽然是数组的外形,但它的类型不是 Array,因此不能直接调用数组的方法 includes
return [].includes.call(arguments_, param)
}
function func() {
if (hasParam(arguments, 'GET')) {
console.log('输入中包含 GET')
} else {
console.log('输入中不包含 GET')
}
}
push()
的通用用法:
let obj = {}
Array.prototype.push.apply(obj, ['a', 'b']) // => 2(push() 返回数组长度)
obj // => {0: 'a', 1: 'b', length: 2}; 若对象中没有 length 属性将自动加上
5 数组和函数
声明函数时,可以通过 ...args
将待传入的参数包裹在一个数组中。其他地方使用 ...
可以展开一个数组或对象。
// 延时 ms 毫秒执行函数
Function.prototype.delay = function(ms) {
let that = this
return function(...args) {
setTimeout(() => {
that.apply(this, args)
// 或:that.call(that, ...args)
}, ms)
}
}
function f(a, b) {
console.log( a + b );
}
f.delay(1000)(1, 2) // 1000ms 后输出 3
6 Array、Set、Map、WeakMap、WeakSet
Array
用来表示实际应用中的一组 有序 的数据
Set
用来表示实际应用中一组 不会重复 的数据,即集合中个元素唯一
Map
可以视为 Object
的扩展,因为 Object
只允许键为 string
或 symbol
类型,而 Map
的键可以是 任意类型
6.1 Map
实例的方法和属性
new Map(...items)
创建实例Map.prototype.set(key: any, value: any): Map
存入键值对Map.prototype.get(key: any): any
获取key
对应的值,若不存在返回undefined
Map.prototype.has(key: any): boolean
判断是否存在相应的键Map.prototype.delete(key): boolean
根据键删除键值对,若不存在键key
,将返回false
,否则返回true
Map.prototype.clear()
清空实例的元素Map.prototype.size: number
返回当前元素个数Map.prototype.forEach((key, value, map) => {...})
遍历实例中的元素- map 也有
map.keys()
map.values()
map.entries()
通过 new Map(Object.entries(o))
和 Object.fromEntires(map)
可以实现 Map
和 Object
的相互转换
6.2 Set
实例的方法和属性
new Set(source: Iterable)
Set.prototype.add(value: any): Set
Set.prototype.delete(value: any): boolean
Set.prototype.has(value: any): boolean
Set.prototype.clear()
Set.prototype.size: number
6.3 WeakMap
和 WeakSet
现有如下代码:
let obj = {1: 'one'}
let map = new Map()
// 将 obj 作为 map 的一个键
map.set(obj, '')
// 覆盖引用
obj = null
map.keys() // => [{value: {1: 'one'}}]
将 null
赋值给引用 obj
,我们预期的是对象 {1: 'one'}
能够随之被“垃圾回收”清理掉,然而因为它存在于 map
中,因此会被阻止清理——我们仍然可以通过 map.keys()
获取到 {1: 'one'}
。也就是说 {1: 'one'}
将始终存在内存中,这不是我们预期的,在大型项目当中,这会导致大量不再用到的对象始终存在内存中。
WeakMap
和 WeakSet
可以解决这一问题。
WeakMap
的实例只有以下方法:
WeakMap.prototype.get(key: object): any
WeakMap.prototype.set(key: object, value: any): WeakMap
WeakMap.prototype.has(key: object): boolean
WeakMap.prototype.delete(key: object): boolean
WeakSet
的实例只有以下方法:
WeakSet.prototype.add(value: object): WeakSet
WeakSet.prototype.delete(value: object): boolean
WeakSet.prototype.has(value: object): boolean
WeakMap
只允许对象作为键,WeakSet
只允许对象作为集合元素;当该对象在其他地方不能被访问时,WeakMap
和 WeakSet
中的对象将会随之被清理掉。
// 该代码直接在控制台中执行看不到效果
// 需要放在文件中执行,才能看到对象自动被清理
let obj = {}
let weakMap = new WeakMap()
weakMap.set(obj, '')
console.log(weakMap) // => [[{}: '']]
obj = null
console.log(weakMap) // => []
复习 Array,重学 JavaScript的更多相关文章
- 重学JavaScript - 数组
作者:狐狸家的鱼 GitHub:surRimn 整理自MDN文档 数组 数组是一种类列表对象,长度和元素类型不固定. 描述 访问数组 JavaScript数组的索引是从0开始的,第一个元素的索引为0, ...
- 2.重学javascript 对象和数组
什么是对象,其实就是一种类型,即引用类型. 一.创建Object类型有两种. ①使用new运算符 <script type="text/javascript"> var ...
- 重学JavaScript - 映射与集合
作者:狐狸家的鱼 GitHub:surRimn 整理自MDN文档 带键的集合 映射 Map对象 一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of 循环在每次迭代后会返 ...
- 1.重学javascript (一)
一.script标签解析 <script>xxx</script>这组标签,是用于在html 页面中插入js的主要方法.它主要有以下 几个属性: 1.charset:可选.表示 ...
- 重学JavaScript之面向对象的程序设计(继承)
1. 继承 ES 中只支持实现继承,而且其实现继承主要依靠原型链来实现的. 2. 原型链 ES中 描述了 原型链的概念,并将原型链作为实现继承的主要方法.其基本思想是利用原型让一个引用类型继承另一个引 ...
- 重学JavaScript之匿名函数
1. 什么是匿名函数? 匿名函数就是没有名字的函数,有时候也称为< 拉姆达函数>.匿名函数是一种强大的令人难以置信的工具.如下: function a(a1, a2, a3) { // 函 ...
- 重学js之JavaScript 面向对象的程序设计(创建对象)
注意: 本文章为 <重学js之JavaScript高级程序设计>系列第五章[JavaScript引用类型]. 关于<重学js之JavaScript高级程序设计>是重新回顾js基 ...
- 从头开始学JavaScript (十二)——Array类型
原文:从头开始学JavaScript (十二)--Array类型 一.数组的创建 注:ECMAscript数组的每一项都可以保存任何类型的数据 1.1Array构造函数 var colors = ne ...
- 从头开始学JavaScript (八)——变量
原文:从头开始学JavaScript (八)--变量 一.变量分类: 基本类型值:null.undefined.number.string.Boolean: 引用类型值:保存在内存中的对象,如:Obj ...
随机推荐
- 使用SQL语句进行特定值排序
使用SQL语句进行查询时,对数据进行排序,排序要求为排序的一个字段中特定值为顶部呈现: select * from TableName order by case TableFieldName whe ...
- Jexl表达式引擎-根据字符串动态执行JAVA.md
Table of Contents generated with DocToc 一.使用场景 二.市面上表达式引擎比较 2.1 Aviator 2.2 Jexl 一.使用场景 在做某些项目的时候,有时 ...
- IDEA添加注释常用的快捷键
1.行注释Ctrl+/ 2.块注释Ctrl+Shift+/ 3.生成类注释 输入/**,然后按回车 (idea上没有生成类注释快捷键的,可以看这里 :idea生成类注释和方法注释的正确方法 ) 4.生 ...
- 深入了解PHP的生成器
在驾驶方面,速度并不会决定一切.但是在网络上,速度至关重要.你的应用程序越快,用户体验就越好.好吧,这时候有人就奇怪了,本文是关于PHP 生成器的,那么为什么我们要谈论速度呢?很快你就会发现,生成器在 ...
- 部署一套完整的Kubernetes高可用集群(二进制,v1.18版)
一.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadm Kubeadm是一个K8s部署工具,提供kubeadm ...
- 遍历map的6种方式
1,平时开发中对map的使用很多,然后发现了很多map可能存在的各种问题:如HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize ...
- iframe子页面取父页面的变量问题
iframe包含的子页面,想获取父页面的变量,不能直接获取到. 但是子页面可以访问父页面的方法 window.parent.parentFunctionName(); 利用这一点,可以将父页面的变 ...
- jmeter零散知识点
- ES数据库 常用 java api
一.获取类api get API允许根据其id从索引中获取类型化的JSON文档. 以下示例从名为twitter的索引中获取一个JSON文档,该索引名为tweet,id值为1: GetResponse ...
- JVM详解之:汇编角度理解本地变量的生命周期
目录 简介 本地变量的生命周期 举例说明 优化的原因 总结 简介 java方法中定义的变量,它的生命周期是什么样的呢?是不是一定要等到方法结束,这个创建的对象才会被回收呢? 带着这个问题我们来看一下今 ...