关于 JS 的数组
数组的奇怪地方
-数组的空位
读懂 ECMAScript 规格 - 阮一峰的网络日志 (ruanyifeng.com)
Array.isArray(Array.prototype) // true
// Array.prototype => [constructor: ƒ, at: ƒ, concat: ƒ, copyWithin: ƒ, fill: ƒ, …]
// Array.prototype 是个数组
Array.prototype instanceof Array // false
数组的遍历方式
关于数组的详细描述看这份文档 Array - JavaScript | MDN (mozilla.org)
- for 和 forEach
let arr = [1,2, ,4,null,undefined,7]
// arr => [1, 2, empty, 4, null, undefined, 7]
for(let i = 0; i < arr.length; i++) {
if(arr[i] == 1) {
continue
}
if(arr[i] == 8) {
break
}
console.log(arr[i]) // empty 被打印成 undefined
}
arr.forEach((elem, index, array) => {
console.log(elem, index, array)
})
两者的区别:for 支持 break 和 continue ,而 forEach 都不支持;for 不会跳过 empty 值,而 forEach 会跳过;对于 null 和 undefined 两者都不会跳过。
- map
let arr = [1,2, ,4,null,undefined,7]
// arr => [1, 2, empty, 4, null, undefined, 7]
let res = arr.map((value, index, array) => {
console.log(value, index, array)
return '-' + value
})
console.log(res) // ['-1', '-2', empty, '-4', '-null', '-undefined', '-7']
map 会跳过 empty 值,对于 null 和 undefined 都不会跳过。
- filter 返回值是数组
let arr = [1,2, ,4,null,undefined,7,NaN]
// arr => [1, 2, empty, 4, null, undefined, 7, NaN]
let res = arr.filter((value, index, array) => {
console.log(value, index, array)
return !value
})
console.log(res) // [null, undefined, NaN]
filter 会跳过 empty 值
- some 返回值是 boolean
let arr = [1,2, ,null,undefined,6,NaN]
// arr => [1, 2, empty, null, undefined, 6, NaN]
let bool = arr.some((value, index, array) => {
return !value // 遇到 null ,取反为真,中断循环直接返回
})
console.log(bool) // true
some 会跳过 empty 值。 some()
方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试。它返回的是一个 Boolean 类型的值。如果用一个空数组进行测试,在任何情况下它返回的都是false
。
- every 返回值是 boolean
let arr = [1,2,3,4,5]
let bool = arr.every((value, index, array) => {
return value < 7
})
console.log(bool) // true
every 会跳过 empty 值。 every()
方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。若收到一个空数组,此方法在任何情况下都会返回 true
。
- reduce 接受一个函数作为累加器
let arr = [1,2,3,4,5]
let sum = arr.reduce((init, value, index, array) => {
return init + value
}, 0)
console.log(sum) // 15
reduce()
方法对数组中的每个元素按序执行一个由您提供的 reducer 函数,每一次运行 reducer 会将先前元素的计算结果作为参数传入,最后将其结果汇总为单个返回值。
// 使用 reduce 取最大值
let arr = [1,2,3,3,4,5]
let max = arr.reduce((prev, cur) => {
return Math.max(prev, cur)
})
console.log(max)
// 使用 reduce 去重
arr.reduce((prev, cur) => {
prev.indexOf(cur) == -1 && prev.push(cur)
return prev
}, [])
问题2:for in 能否遍历数组?
let arr = [1,2,3,3,4,5]
Array.prototype.run = function() {
console.log('run')
}
for(let index in arr) {
console.log(index, arr[index]) // 可以打印出 run 属性
}
答:可以,但是不推荐使用 for in 遍历数组。
问题2:for of 能否遍历数组?
let arr = [1,2,3,3,4,5]
Array.prototype.run = function() {
console.log('run')
}
for(let item of arr) {
console.log(item)
}
for(let item of arr.values()) { // 效果等同于上面
console.log(item)
}
for(let [index, item] of arr.entries()) { // 效果等同于上面
console.log(index, item)
}
答:可以
问题3: new Array() 参数问题
let arr = new Array(1, 2)
console.log(arr) // [1, 2]
let arr1 = new Array(2)
console.log(arr1) // [empty x 2]
let arr2 = new Array(0) // 等同于 new Array()
console.log(arr2) // []
let arr3 = new Array(null)
console.log(arr3) // [null] length: 1
let arr4 = new Array(undefined)
console.log(arr4) // [undefined] length: 1
let arr5 = new Array(NaN) // Uncaught RangeError: Invalid array length
参数只有一个时,是指数组的长度,否则就是数组内的元素
还有哪些可以遍历数组的方法:find / findIndex / keys / values / entries / flat / flatMap / findLast / findLastIndex / lastIndexOf / includes / slice / splice / fill / copyWithin
数组的扩展
关于类数组的说法:为什么 JavaScript 里函数的 arguments 只是 array-like object?
- 类数组/伪数组
let divDomList = document.getElementsByTagName('div')
console.log(divDomList) // HTMLCollection
let targets = document.querySelectorAll('.target')
console.log(targets) // NodeList
console.log(targets instanceof Array) // false
targets.push(1) // TypeError: targets.push is not a function
// -------------------------------------- //
function foo() {
console.log(arguments)
console.log(arguments instanceof Array) // false
}
foo(1,2,3) // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
如何转成数组:
let targets = document.querySelectorAll('.target')
let arr = Array.prototype.slice.call(targets)
console.log(arr)
console.log(arr instanceof Array) // true
// 使用 Array.from 方法
let arr1 = Array.from(targets)
console.log(arr1)
console.log(arr1 instanceof Array) // true
// ---------------------------------------- //
let arrayLike = {
0: 'es6',
1: 'es7',
2: 'es8',
length: 3
}
let arr2 = Array.from(arrayLike)
console.log(arr2)
console.log(arr2 instanceof Array) // true
- 如何将
new Array(3)
表现成[3]
这样?
let arr = Array.of(3)
console.log(arr) // [3]
- copyWithin 该方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
const array1 = ['a', 'b', 'c', 'd', 'e'];
console.log(array1.copyWithin(0, 3, 4));
// ["d", "b", "c", "d", "e"]
console.log(array1.copyWithin(1, 3));
// ["d", "d", "e", "d", "e"]
- fill 该方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
let arr = new Array(3).fill(6) // [6,6,6]
let arr1 = [1,2,3,4]
console.log(arr1.fill(5, 1)); // [1, 5, 5, 5]
console.log(arr1.fill(16, 1, 3)); // [1, 16, 16, 5]
console.log(arr1.fill(0)); // [0, 0, 0, 0]
- Array.prototype.includes() (ES 7 / ES 2016) 返回值是 boolean 类型, 有两个参数(第一个是搜索的目标值,第二个是开始搜索的位置) 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回
true
,否则返回false
。使用includes()
比较字符串和字符时是区分大小写的。0 的值将全部视为相等,与符号无关(即 -0 与 0 和 +0 相等),但false
不被认为与 0 相等。includes()
使用零值相等
算法来确定是否找到给定的元素。
let arr = [1, , 3, null, undefined, 6]
console.log(arr.includes(0))
如果 includes 第二个参数为负值,计算出的索引将作为开始搜索searchElement
的位置。如果计算出的索引小于 0,则整个数组都会被搜索。
let arr = ['a', 'b', 'c', NaN, 2]
console.log(arr.includes('a', -3)) // true
console.log(arr.includes('a', -1)) // false
console.log(arr.includes(NaN)) // true
console.log(arr.indexOf(NaN)) // -1
console.log(arr.includes('2')) // false
console.log(arr.indexOf('2')) // -1
// 看出来 indexOf 和 includes 对于 NaN 的区别,includes 支持 NaN 的搜索; 两者搜索目标值都采用 严格模式 方式匹配
- Array.prototype.flat() (ES 10 / ES 2019)
const arr = [ 1, [2, 3], [ [5], [6] ], 7 ]
console.log(arr.flat()) // [1,2,3, [5], [6], 7]
console.log(arr.flat(2)) // [1,2,3, 5, 6, 7]
console.log(arr.flat().flat()) // [1,2,3, 5, 6, 7]
console.log(arr.flat(Infinity)) // [1, 2, 3, 5, 6, 7]
const arr1 = ['es', 'ng', 'vue']
const res = arr1.map(item => ['__' + item])
console.log(res) // [['__es'], ['__ng'], ['__vue']]
console.log(res.flat()) // ['__es', '__ng', '__vue']
// 使用 flatMap
const res1 = arr1.flatMap(item => ['__' + item])
console.log(res1) // ['__es', '__ng', '__vue']
- Array.prototype.flatMap() (ES 10 / ES 2019) 可以理解成先拍平再 map 操作。