【06】ES6:数组的扩展

news2024/12/22 3:29:28

一、扩展运算符

扩展运算符(spread)是三个点(…)。它是 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

1、基本语法

[...array] // array表示要转换的数组

console.log(...[1, 2, 3]) // 1 2 3
console.log(1, ...[2, 3, 4], 5) // 1 2 3 4 5
[...document.querySelectorAll('div')] // [<div>, <div>, <div>]

2、用途

(1)复制数组

数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。

// 存在问题:a2 并不是 a1 的克隆,而是指向同一份数据的另一个指针。修改 a2,会直接导致a1 的变化。
const a1 = [1, 2]
const a2 = a1
a2[0] = 2
a1 // [2, 2]

// ES5 中用变通方法复制数组(浅拷贝)
const a1 = [1, 2]
const a2 = a1.concat()

// 使用扩展运算符复制数组(浅拷贝)
const a1 = [1, 2]
const a2 = [...a1]

(2)合并数组

合并数组只是进行了浅拷贝,使用的时候需要注意

const arr1 = ['a', 'b']
const arr2 = ['c']
const arr3 = ['d', 'e']

// ES5 的合并数组
arr1.concat(arr2, arr3) // [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ]

(3)与解构赋值结合

扩展运算符可以与解构赋值结合起来,用于生成数组。

const arr = [1, 2, 3]
// ES5
const first = arr[0], rest = arr.slice(1)
// ES6
const [first, ...rest] = arr

console.log(first) // 输出 1
console.log(rest) // 输出 [2, 3]

如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。

const [...butLast, last] = [1, 2, 3, 4, 5] // 报错
const [first, ...middle, last] = [1, 2, 3, 4, 5] // 报错

(4) 字符串转数组

[...'hello'] // [ "h", "e", "l", "l", "o" ]
// ES6 之前字符串转数组是通过:'hello'.split('');

(5)类数组转为数组

// arguments
function func() {
    console.log(arguments) // [Arguments] { '0': 1, '1': 2 }
    console.log([...arguments]) [ 1, 2 ]
}
func(1, 2)

// 任何定义了遍历器(Iterator)接口的对象。都可以用扩展运算符转为真正的数组。
let nodeList = document.querySelectorAll('div')
let array = [...nodeList]

(6)数组转为参数数列(函数传参)

将一个数组转为用逗号分隔的参数序列,可以方便地传递给函数。

const arr = [1, 2, 3]

function sum(a, b, c) {
	return a + b + c
}

console.log(sum(...arr)) // 输出 6

二、Array.from()

类数组不能直接使用数组的方法,需要先把类数组转化为数组。

ES6 数组的新增方法 Array.from(),该方法用于将类数组对象(array-like)和可遍历的对象(iterable)转换为真正的数组进行使用(包括 ES6 新增的数据结构 Set 和 Map)。

基本语法

Array.from(arrayLike, mapFn, thisArg)
参数描述
arrayLike想要转换成数组的类数组对象或可迭代对象
mapFn如果指定了该参数,新数组中的每个元素会执行该回调函数
thisArg可选参数,执行回调函数 mapFn 时 this 对象

arrayLike

任何有 length 属性的对象,都可以通过 Array.from() 方法转为数组。

const arrLike = {
  '0': 'a',
  '1': 'b',
  '2': 'c',
  length: 3
}

// ES5 的写法
var arr = [].slice.call(arrayLike) // ['a', 'b', 'c']
var arr = [].slice.apply(arrLike) // ['a', 'b', 'c']

// ES6 的写法
var arr = Array.from(arrLike)
// NodeList 对象
let ps = document.querySelectorAll('p')
Array.from(ps).filter(p => {
	return p.textContent.length > 100
})

// arguments 对象
function foo() {
	var args = Array.from(arguments)
	// ...
}
/* 部署了 Iterator 接口的数据结构 */
Array.from('hello') // ['h', 'e', 'l', 'l', 'o']

const namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']

const map = new Map([[1, 2], [2, 4], [4, 8]])
Array.from(map) // [[1, 2], [2, 4], [4, 8]]
// 数组对象,Array.from()会返回一个一模一样的新数组
Array.from([1, 2, 3]) // [1, 2, 3]
// 有 length 属性的对象
Array.from({ length: 3 }) // [undefined, undefined, undefined]

mapFn 回调函数

在 Array.from 中第二个参数是一个类似 map 函数的回调函数,该回调函数会依次接收数组中的每一项作为传入的参数,然后对传入值进行处理,最得到一个新的数组。 Array.from(obj, mapFn, thisArg) 也可以用 map 改写成这样 Array.from(obj).map(mapFn, thisArg)。

Array.from(arrayLike, x => x * x)
// 等同于
Array.from(arrayLike).map(x => x * x)

Array.from([1, 2, 3], x => x * x) // [1, 4, 9]
// 取出一组 DOM 节点的文本内容
let spans = document.querySelectorAll('span.name')

// map()
let names1 = Array.prototype.map.call(spans, s => s.textContent)

// Array.from()
let names2 = Array.from(spans, s => s.textContent)

thisArg

Array.from 中第三个参数可以对回调函数中 this 的指向进行绑定,该参数是非常有用的,我们可以将被处理的数据和处理对象分离,将各种不同的处理数据的方法封装到不同的的对象中去,处理方法采用相同的名字。

在调用 Array.from 对数据对象进行转换时,可以将不同的处理对象按实际情况进行注入,以得到不同的结果,适合解耦。

// 定义一个 obj 对象可以认作是,Array.from 回调函数中处理数据的方法集合,handle 是其中的一个方法,把 obj 作为第三个参数传给 Array.from 这样在回调函数中可以通过 this 来拿到 obj 对象。
let obj = {
  handle: function(n){
    return n + 2
  }
}

Array.from([1, 2, 3, 4, 5], function (x){
  return this.handle(x)
}, obj) // [3, 4, 5, 6, 7]

三、Array.of()

Array.of() 方法用于将一组值,转换为数组。

Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1

这个方法的主要目的,是弥补数组构造函数Array()的不足。因为参数个数的不同,会导致Array()的行为有差异。

Array() // []
Array(3) // [, , ,] 参数只有一个正整数时,实际上是指定数组的长度
Array(3, 11, 8) // [3, 11, 8] 返回由参数组成的新数组

上面代码中,Array() 方法没有参数、一个参数、三个参数时,返回的结果都不一样。只有当参数个数不少于 2 个时,Array() 才会返回由参数组成的新数组。参数只有一个正整数时,实际上是指定数组的长度。

Array.of() 基本上可以用来替代 Array() 或 new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。

Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]

Array.of() 总是返回参数值组成的数组。如果没有参数,就返回一个空数组。

Array.of() 方法可以用下面的代码模拟实现。

function ArrayOf(){
	return [].slice.call(arguments)
}

四、实例方法

1、copyWithin()

在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。该方法会改变原数组。

Array.prototype.copyWithin(target, start = 0, end = this.length)
参数描述
target必须参数。从该位置开始替换数据。如果为负值,表示倒数
start可选参数。从该位置开始读取数据,默认为0。如果为负值,表示从末尾开始计算。
end可选参数。到该位置停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。

这三个参数都应该是数值,如果不是,会自动转为数值。

// 将3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,覆盖原来的1和2。
[1, 2, 3, 4, 5].copyWithin(0, 3) // [4, 5, 3, 4, 5]

// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4) // [4, 2, 3, 4, 5]

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1) // [4, 2, 3, 4, 5]

2、find()、findIndex()、findLast()、findLastIndex()

数组的检索方法,不会改变原数组。

方法说明
find()用于找出第一个符合条件的数组成员。如果有返回该成员,如果没有符合条件的成员,则返回 undefined。
findIndex()返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1。
findLast()用于找出第一个符合条件的数组成员。如果有返回该成员,如果没有符合条件的成员,则返回 undefined。从数组的最后一个成员开始,依次向前检查。
findLastIndex()返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回 -1。从数组的最后一个成员开始,依次向前检查。

第一个参数是一个回调函数,所有数组成员依次执行该回调函数。

第二个参数,用来绑定回调函数的 this 对象。

[1, 4, -5, 10].find(n => n < 0) // -5

// 回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
[1, 5, 10, 15].find(function(value, index, arr) {
	return value > 9
}) // 10

[1, 5, 10, 15].findIndex(function(value, index, arr) {
	return value > 9
}) // 2

// 绑定回调函数的this对象。
function f(v){
	return v > this.age
}
let person = {name: 'John', age: 20}
[10, 12, 26, 15].find(f, person) // 26
const array = [
	{ value: 1 },
	{ value: 2 },
	{ value: 3 },
	{ value: 4 }
]

array.findLast(n => n.value % 2 === 1) // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1) // 2

这四个方法都可以发现 NaN,弥补了数组的 indexOf() 方法的不足。

array.indexOf(item, start = 0) 。 返回数组中某个指定的元素位置,如果在数组中没找到指定元素则返回 -1。

[NaN].indexOf(NaN) // -1

// 借助 Object.is() 方法
[NaN].findIndex(y => Object.is(NaN, y)) // 0

3、filter()

过滤数组成员,满足条件的成员组成一个新数组返回。该方法不会改变原数组。

第一个参数是一个回调函数,所有数组成员依次执行该回调函数,返回结果为 true的成员组成一个新数组返回。

第二个参数,用来绑定回调函数的 this 对象。

[1, 2, 3, 4, 5].filter(function (elem) {
	return (elem > 3)
}) // [4, 5]

[0, 1, 'a', false].filter(Boolean) // [1, 'a']
// 回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
	return index % 2 === 0
}) // [1, 3, 5]

// 绑定回调函数内部的this变量。
const obj = { MAX: 3 }
const myFilter = function (item) {
	if (item > this.MAX) return true
}
[2, 8, 3, 4, 1, 3, 2, 9].filter(myFilter, obj) // [8, 4, 9]

4、map()

将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。不会改变原数组。

第一个参数是一个回调函数,所有数组成员依次执行该回调函数,运行结果组成一个新数组返回。

第二个参数,用来绑定回调函数的 this 对象。

[1, 2, 3].map(function (n) {
	return n + 1
}) // [2, 3, 4]
// 回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
[1, 2, 3].map(function(elem, index, arr) {
	return elem * index
}) // [0, 2, 6]


// 绑定回调函数内部的this变量。
const arr = ['a', 'b', 'c']
[1, 2].map(function (e) {
	return this[e]
}, arr) // ['b', 'c']

5、forEach()

数组循环。调用数组的每个元素,并将元素传递给回调函数。没有返回结果,返回值为 undefined。修改数组元素会改变原数组。

第一个参数是一个回调函数,所有数组成员依次执行该回调函数,循环一遍。

第二个参数,用来绑定回调函数的 this 对象。

// 回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。
const arr = ['a', 'b', 'c']

arr.forEach(function(currentValue, index, arr) {
	console.log(currentValue)
})
// 'a' 0 ['a', 'b', 'c']
// 'b' 1 ['a', 'b', 'c']
// 'c' 2 ['a', 'b', 'c']

中断循环

(1)使用 for 循环或者 for in 循环代替。break 结束整个循环, continue 退出本次循环。

let list = [1, 2, 3, 4]
for (let i = 0; i < list.length; i++) {
	if (i == 1) {
		continue // 退出本次循环
	}
	if (i == 2) {
		break // 结束整个循环
	}
}
// break 只会结束最里层的 for 循环,继续外面的 for 循环
for (var i = 1; i < 4; i++) {
	for(var j = 1; j < 6; j++) {
		console.log(i * j)
		if(j > 2) break
	}
} // 1 2 3 2 4 6 3 6 9
for 循环中 return 语句:跳出 for 循环,且不执行 for 循环之外的语句,直接跳出当前函数,返回 return 后的值。

因为 js 中 for 是没有局部作用域的概念的,所以只有把 for 循环放在函数中时,才可以在for循环中使用 return 语句。

(2)return、break、continue 的方式都不能终止 forEach 循环,return 在 forEach 里相当于 for 循环里的 continue,能够退出本次循环。

// return 3 的元素跳过
var arr = [1, 2, 3, 4, 5]

arr.forEach(function (item) {
    if (item === 3) {
        return
    }
    console.log(item)
})
// 1
// 2
// 4
// 5

(3)使用 try…catch,throw 抛出异常,终止 forEach 循环。

try {
	[1, 2, 3].forEach(item => {
		if (item === 2) throw('哈哈哈')
		console.log(item)
	})
} catch(e) {
	console.log(e, 'e')
}

// 1
// '哈哈哈' e

(4)通过 some 和 every 来实现。every 在碰到 return false 的时候,中止循环。some 在碰到 return ture 的时候,中止循环。

// 使用 Array.some()
arr.some(item => {
	console.log(item) 
	return item === 2 // 当有数组有一项满足条件时结束并返回 true
})
 
// 使用 Array.ervey()
arr.every(item => {
	console.log(item)
	return item !== 2 // 检查数字中是否每一项都满足条件,如果有一项不满足就结束循环并返回 false
})

6、some()、every()

这两个方法类似“断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件。不会改变原数组。

第一个参数是一个回调函数,所有数组成员依次执行该回调函数,该函数接受三个参数:当前成员、当前位置和整个数组,然后返回一个布尔值。

第二个参数,用来绑定回调函数的 this 对象。

[1, 2, 3, 4, 5].some(function (elem, index, arr) {
	return elem >= 3
}) // true

[1, 2, 3, 4, 5].every(function (elem, index, arr) {
	return elem >= 3
}) // false

arr.some() 只要有一项满足条件, 就会返回 true,否则返回 false;arr.every() 只有所有项都满足条件,才会返回 true,否则返回 false。

7、reduce()

reduce() 接受一个函数作为累加器,从数组第一个值开始缩减,直到最后一个值缩减完成。最终计算为一个值。不会改变原数组。

参数说明
(prev, cur, index, arr) => {}必需,回调函数
prev:必需。初始值 initialValue,或者计算结束后的返回值。
cur:必需。arr 数组的当前累加项。
index:可选。当前累加项的索引。
arr:可选。要累加的 arr 非空数组。
initialValue可选,初始值。当没有初始值时,回调函数第一次累加的 prev 是 arr 的第一项,cur 是 arr 的第二项,index 的值为 1。
[1, 2, 3, 4, 5].reduce(function (a, b) {
	console.log(a, b) // 1 2 | 3 3 | 6 4 | 10 5 
	return a + b
}) // 15

[1, 2, 3, 4, 5].reduce(function (a, b) {
	return a + b
}, 10) // 25

8、fill()

arr.fill(value, start = 0, end = arr.length) 用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。 起始索引,默认值为 0。 终止索引,默认值为 this.length。

['a', 'b', 'c'].fill(7) // [7, 7, 7]

new Array(3).fill(7) // [7, 7, 7]

['a', 'b', 'c'].fill(7, 1, 2) // ['a', 7, 'c']

注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。

let arr = new Array(3).fill({name: 'Mike'})
arr[0].name = 'Ben'
arr // [{name: 'Ben'}, {name: 'Ben'}, {name: 'Ben'}]

let arr = new Array(3).fill([])
arr[0].push(5)
arr // [[5], [5], [5]]

9、entries()、keys() 和 values()

用于遍历数组。它们都返回一个遍历器对象,可以用 for…of 循环进行遍历,唯一的区别是 keys() 是对键名的遍历、values() 是对键值的遍历,entries() 是对键值对的遍历。

for (let index of ['a', 'b'].keys()) {
	console.log(index)
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem)
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem)
}
// 0 'a'
// 1 'b'

10、includes()

Array.prototype.includes 方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的 includes 方法类似。

[1, 2, 3].includes(2)	// true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true

该方法的第二个参数表示搜索的起始位置,默认为 0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为 -4,但数组长度为 3),则会重置为从 0开始。

[1, 2, 3].includes(3, 3) // false
[1, 2, 3].includes(3, -1) // true

没有该方法之前,我们通常使用数组的 indexOf 方法,检查是否包含某个值。

indexOf 方法有两个缺点:

一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于 -1,表达起来不够直观。

二是,它内部使用严格相等运算符(===)进行判断,这会导致对 NaN 的误判。includes使用的是不一样的判断算法,就没有这个问题。

if (arr.indexOf(el) !== -1) {
	// ...
}

[NaN].indexOf(NaN) // -1
[NaN].includes(NaN) // true

11、flat()、flatMap()

Array.prototype.flat()

用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,不会改变原数组。

flat() 默认只会“拉平”一层,如果想要“拉平”多层的嵌套数组,可以将 flat() 方法的参数写成一个整数,表示想要拉平的层数,默认为 1。参数值为 Infinity 时,不管有多少层嵌套,都会转成一维数组。

[1, 2, [3, 4]].flat() // [1, 2, 3, 4] 拉平一层

[1, 2, [3, [4, 5]]].flat() // [1, 2, 3, [4, 5]] 拉平一层

[1, 2, [3, [4, 5]]].flat(2) // [1, 2, 3, 4, 5] 拉平两层

[1, [2, [3]]].flat(Infinity) // [1, 2, 3] 全部拉平

[1, 2, , 4, 5].flat() // [1, 2, 4, 5] 跳过空位

Array.prototype.flatMap()

对原数组的每个成员执行一个函数(相当于执行 Array.prototype.map() ),然后对返回值组成的数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。

flatMap() 只能展开一层数组。

第一个参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。

第二个参数,用来绑定遍历函数里面的 this。

// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2]) // [2, 4, 3, 6, 4, 8]

// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]]) // [[2], [4], [6], [8]]

arr.flatMap(function callback(currentValue[, index[, array]]) {
	// ...
}[, thisArg])

12、at()

长久以来,JavaScript 不支持数组的负索引,如果要引用数组的最后一个成员,不能写成 arr[-1],只能使用 arr[arr.length - 1]。

这是因为方括号运算符 [] 在 JavaScript 语言里面,不仅用于数组,还用于对象。对于对象来说,方括号里面就是键名,比如 obj[1] 引用的是键名为字符串 1 的键,同理 obj[-1] 引用的是键名为字符串-1的键。由于 JavaScript 的数组是特殊的对象,所以方括号里面的负数无法再有其他语义了,也就是说,不可能添加新语法来支持负索引。

为了解决这个问题,ES2022 为数组实例增加了 at() 方法,接受一个整数作为参数,返回对应位置的成员,并支持负索引。这个方法不仅可用于数组,也可用于字符串和类型数组(TypedArray)。

const arr = [5, 12, 8, 130, 44]
arr.at(2) // 8
arr.at(-2) // 130

// 如果参数位置超出了数组范围,at()返回undefined。
const sentence = 'This is a sample sentence';
sentence.at(0) // 'T'
sentence.at(-1) // 'e'
sentence.at(-100) // undefined
sentence.at(100) // undefined

13、toReversed()、toSorted()、toSpliced()、with()

很多数组的传统方法会改变原数组,比如 push()、pop()、unshift()、shift() 等等。数组只要调用了这些方法,它的值就变了。现在有一个提案,允许对数组进行操作时,不改变原数组,而返回一个原数组的拷贝。

这样的方法一共有四个。

  • Array.prototype.toReversed() -> Array
  • Array.prototype.toSorted(compareFn) -> Array
  • Array.prototype.toSpliced(start, deleteCount, ...items) -> Array
  • Array.prototype.with(index, value) -> Array

它们分别对应数组的原有方法。

这四个新方法对应的原有方法,含义和用法完全一样,唯一不同的是不会改变原数组,而是返回原数组操作后的拷贝。

  • toReversed() 对应 reverse(),用来颠倒数组成员的位置。
  • toSorted() 对应 sort(),用来对数组成员排序。
  • toSpliced() 对应 splice(),用来在指定位置,删除指定数量的成员,并插入新成员。
  • with(index, value) 对应 splice(index, 1, value),用来将指定位置的成员替换为新的值。
const sequence = [1, 2, 3]
sequence.toReversed() // [3, 2, 1]
sequence // [1, 2, 3]

const outOfOrder = [3, 1, 2]
outOfOrder.toSorted() // [1, 2, 3]
outOfOrder // [3, 1, 2]

const array = [1, 2, 3, 4]
array.toSpliced(1, 2, 5, 6, 7) // [1, 5, 6, 7, 4]
array // [1, 2, 3, 4]

const correctionNeeded = [1, 1, 3]
correctionNeeded.with(1, 2) // [1, 2, 3]
correctionNeeded // [1, 1, 3]

14、isArray()

判断 JS 对象,如果值是 Array,则为 true,否则为 false。

Array.isArray(obj)
// 下面的函数调用都返回 true
Array.isArray([])
Array.isArray([10])
Array.isArray(new Array())
Array.isArray(new Array('a', 'b', 'c'))
// 鲜为人知的事实:其实 Array.prototype 也是一个数组。
Array.isArray(Array.prototype)

// 下面的函数调用都返回 false
Array.isArray()
Array.isArray({})
Array.isArray(null)
Array.isArray(undefined)
Array.isArray(17)
Array.isArray('Array')
Array.isArray(true)
Array.isArray(false)
Array.isArray(new Uint8Array(32))
Array.isArray({ __proto__: Array.prototype })

ES5 中判断数组的方法

(1)typeof 类型检测

对于基本类型的判断没有问题,但是判断数组时,返回 object。( string、number、undefined、boolean、symbol、object、function)

// 基本类型
typeof 123 // number
typeof '123' // string
typeof true // boolean

// 引用类型
typeof [1, 2, 3] // object

(2)通过 instanceof 判断

instanceof 运算符用于判断当前对象是否是某个构造函数的实例。

var arr = ['a', 'b', 'c']
console.log(arr instanceof Array) // true 
console.log(arr.constructor === Array) // true

在这里插入图片描述
数组实例的原型链指向的是 Array.prototype 属性,instanceof 运算符就是用来检测 Array.prototype 属性是否存在于数组的原型链上,上面代码中的 arr 变量就是一个数组,所有拥有 Array.prototype 属性,返回值 true,这样就很好的判断数组类型了。

但是,需要注意的是,prototype 属性是可以修改的,所以并不是最初判断为 true 就一定永远为真。

(3)通过 constructor 判断

constructor 是构造函数的原型对象(prototype)上的属性,指向了当前的构造函数。

new 出来的实例对象上的原型对象有 constructor 属性指向构造函数 Array,由此我们可以判断一个数组类型。

var arr = new Array('a', 'b', 'c')
arr.constructor === Array //true

在这里插入图片描述
constructor 是可以被重写,所以不能确保一定是数组。(常见原型继承)

var str = 'abc'
str.constructor = Array
str.constructor === Array // true

(4)Object.prototype.toString

对象的 toString 方法,返回 [object type] ,type 为当前的数据类型。

 Object.prototype.toString.call(arr) === '[object Array]'

五、数组中的空位

数组的空位指的是,数组的某一个位置没有任何值。

空位不是 undefined,某一个位置的值等于 undefined,依然是有值的。空位是没有任何值。

由于空位的处理规则非常不统一,所以建议避免出现空位。

forEach()、filter()、reduce()、every() 和 some() 都会跳过空位。

map() 会跳过空位,但会保留这个值

join() 和 toString() 会将空位视为 undefined,而 undefined 和 null 会被处理成空字符串。

// forEach 方法
[, 'a'].forEach((x, i) => console.log(i)) // 1

// filter 方法
['a', , 'b'].filter(x => true) // ['a', 'b']

// every 方法
[, 'a'].every(x => x === 'a') // true

// reduce 方法
[1, , 2].reduce((x, y) => x + y) // 3

// some 方法
[, 'a'].some(x => x !== 'a') // false

// map 方法
[, 'a'].map(x => 1) // [, 1]

// join方法
[, 'a', undefined, null].join('#') // '#a##'

// toString 方法
[, 'a', undefined, null].toString() // ',a,,'

Array.from() 方法会将数组的空位,转为 undefined,也就是说,这个方法不会忽略空位。

Array.from(['a', , 'b']) // ['a', undefined, 'b']

扩展运算符(…)也会将空位转为 undefined。

[...['a', , 'b']] // ['a', undefined, 'b']

copyWithin() 会连空位一起拷贝。

[, 'a', 'b', ,].copyWithin(2, 0) // [, 'a', , 'a']

fill() 会将空位视为正常的数组位置。

new Array(3).fill('a') // ['a', 'a', 'a']

for…of 循环也会遍历空位。

let arr = [, ,]
for (let i of arr) {
	console.log(1)
}
// 1
// 1

entries()、keys()、values()、find() 和 findIndex() 会将空位处理成 undefined。

// entries()
[...[, 'a'].entries()] // [[0, undefined], [1, 'a']]

// keys()
[...[, 'a'].keys()] // [0, 1]

// values()
[...[, 'a'].values()] // [undefined, 'a']

// find()
[, 'a'].find(x => true) // undefined

// findIndex()
[, 'a'].findIndex(x => true) // 0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1259191.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1233:单词倒置(C语言)

题目描述 最近birdfly收到了女友的几份信件&#xff0c;为了只要他俩知道信件的秘密&#xff0c;女友把信件里的每个单词都倒置了。这样只有birdfly将它们倒置过来才能明白女友的心思了。为此birdfly还特意请你编写程序帮他解决一下这个问题。 简单起见假定每封信只包含英文单词…

PC行内编辑

点击编辑&#xff0c;行内编辑输入框出现&#xff0c;给列表的每条数据定义编辑标记&#xff0c;最后一定记得 v-model双向绑定&#xff0c;使数据回显。 步骤&#xff1a; 1、给行数据定义编辑标记 2、点击行编辑标记&#xff08;isedit&#xff09; 3、插槽根据标记渲染表单 …

【ZYNQ】SD 卡读写及文件扫描实验

SD 卡控制器&#xff08;SD/SDIO Controller&#xff09; ZYNQ 中的 SD 卡控制器符合 SD2.0 协议规范&#xff0c;接口兼容 eMMC、MMC3.31、SDIO2.0、SD2.0、SPI&#xff0c;支持 SDHC、SDHS 器件。SD 卡控制器支持 SDMA&#xff08;单操作 DMA&#xff09;、ADMA1&#xff08…

【腾讯云云上实验室】用向量数据库——实现高效文本检索功能

文章目录 前言Tencent Cloud VectorDB 简介Tencent Cloud VectorDB 使用实战申请腾讯云向量数据库腾讯云向量数据库使用步骤腾讯云向量数据库实现文本检索 结论和建议 前言 想必各位开发者一定使用过关系型数据库MySQL去存储我们的项目的数据&#xff0c;也有部分人使用过非关…

vue3安装eslint和prettier,最简单的步骤

第1步&#xff1a; 安装eslint yarn add eslint -D 第2步&#xff1a; 在根文件夹中&#xff0c;创建.eslintrc.js文件 第3步&#xff1a; 在package.json文件中新增命令 "lint": "eslint --fix --ext .ts,.tsx,.vue src --quiet","prettier"…

编程学习及常见的技术难题

文章目录 编程学习及常见的技术难题引言如何学习编程学习参考开发工具推荐编程中常见的技术难题 编程学习及常见的技术难题 引言 学习编程是一件有趣也有挑战的事情&#xff0c;它可以让你创造出各种有用的软件&#xff0c;解决各种复杂的问题&#xff0c;甚至改变世界。 编程中…

数据结构与算法编程题30

层次遍历二叉树(队列&#xff1a;先进先出) #define _CRT_SECURE_NO_WARNINGS#include <iostream> using namespace std;typedef char ElemType; #define ERROR 0 #define OK 1 #define Maxsize 100 #define STR_SIZE 1024typedef struct BiTNode {ElemType data;BiTNode…

西南科技大学数字电子技术实验一(数字信号基本参数与逻辑门电路功能测试及FPGA 实现)FPGA部分

一、 实验目的 1、掌握基于 Verilog 语言的 diamond 工具设计全流程。 2、熟悉、应用 Verilog HDL 描述数字电路。 3、掌握 Verilog HDL 的组合和时序逻辑电路的设计方法。 4、掌握“小脚丫”开发板的使用方法。 二、 实验原理 与门逻辑表达式:Y=AB 原理仿真图: 2 输入…

Flask SocketIO 实现动态绘图

Flask-SocketIO 是基于 Flask 的一个扩展&#xff0c;用于简化在 Flask 应用中集成 WebSocket 功能。WebSocket 是一种在客户端和服务器之间实现实时双向通信的协议&#xff0c;常用于实现实时性要求较高的应用&#xff0c;如聊天应用、实时通知等&#xff0c;使得开发者可以更…

FreeRTOS源码阅读笔记6--event_groups.c

通常用的事件标志组是一个32位的变量uxEventBits&#xff0c;可设置的位有24位&#xff0c;一共就是24 种事件。 事件组的结构体类型&#xff1a; 6.1创建事件组xEventGroupCreate() 6.1.1函数原型 返回值&#xff1a;事件组句柄&#xff0c;指向事件组。 6.1.2函数框架 ①…

【牛客网】SQL必知必会题解

SQL 必知必会题解 地址&#xff1a;牛客网在线编程_SQL篇_SQL必知必会 (nowcoder.com) 检索数据 从 Customers 表中检索所有的 ID 现有表 Customers 如下&#xff1a; cust_idABC 【问题】编写 SQL 语句&#xff0c;从 Customers 表中检索所有的 cust_id 答案&#xff1a;…

HUAWEI WATCH 4系列腕上星球,智慧生活触手可得

腕上星球&#xff0c;智慧生活触手可得。HUAWEI WATCH 4系列星球表盘引领风潮&#xff0c;万能卡片轻松便捷开启应用&#xff0c;腕上微信聊天抬腕即回&#xff0c;更有超级快充助力畅玩。全能型智能手表&#xff0c;生活更出彩&#xff01;

JAVA将PDF转图片

前言 当今时代&#xff0c;PDF 文件已经成为了常用的文档格式。然而&#xff0c;在某些情况下&#xff0c;我们可能需要将 PDF 文件转换为图片格式&#xff0c;以便更方便地分享和使用。这时&#xff0c;我们可以使用 Java 编程语言来实现这个功能。Java 提供了许多库和工具&a…

MacOS 14 系统 XCode15、 Flutter 开发 IOS

Flutter 系列文章目录 MacOS14 Sonoma 安装 Flutter 开发环境 MacOS 系统 Flutter开发Android 环境配置MacOS 系统 Flutter开发IOS 环境配置​​​​​​​ 前言 前面我们已经在MacOS14 M3芯片上安装好 Flutter环境&#xff0c;包括开发工具 VsCode 、Android Stuiod,那么fl…

用函数初始化数组

将数组全部初始化为相同值 对于一般情况 一般是用函数&#xff0c;传什么数就初始化为什么数 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> void init(int arr[], int len, int num) {int i;for (i 0; i < len; i){arr[i] num;} } int main() {int arr[…

数据库第十第十一章 恢复和并发简答题

数据库第一章 概论简答题 数据库第二章 关系数据库简答题 数据库第三章 SQL简答题 数据库第四第五章 安全性和完整性简答题 数据库第七章 数据库设计简答题 数据库第九章 查询处理和优化简答题 1.什么是数据库中的事务&#xff1f;它有哪些特性&#xff1f;这些特性的含义是什么…

微服务--01--简介、服务拆分原则

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 微服务微服务架构&#xff0c;是服务化思想指导下的一套最佳实践架构方案。服务化&#xff0c;就是把单体架构中的功能模块拆分为多个独立项目。 单体架构微服务架构…

vscode Markdown 预览样式美化多方案推荐

优雅的使用 vscode写 Markdown&#xff0c;预览样式美化 1 介绍 我已经习惯使用 vscode 写 markdown。不是很喜欢他的 markdown 样式&#xff0c;尤其是代码块高亮的样式。当然用 vscode 大家基本上都会选择安装一个Markdown-preview-enhanced的插件&#xff0c;这个插件的确…

数字IC芯片验证流程及验证工具推荐?收藏专用

验证其实是一个“证伪”的过程&#xff0c;从流程到工具&#xff0c;验证工程师的终极目的都只有一个&#xff1a; 发现所有BUG&#xff0c;或者证明没有BUG&#xff0c;以保证芯片功能性能的正确性和可靠性。 验证环节对于一颗芯片的重要性也是不言而喻的&#xff1a; 从项…

甘草书店记:2023年10月10日 星期二 晴 「甘草书店从何而来」

甘草书店记&#xff1a;2023年10月10日 星期二 晴 「甘草书店从何而来」 甘草书店的落地&#xff0c;差不多是“一瞬间”实现的。 麦田医学和公司新址园区沟通办公室租赁细节时&#xff0c;园区负责人谈到了一楼大厅的咖啡馆计划&#xff0c;而我适时地提出了一些建议和我对于…