【06】ES6:数组的扩展

一、扩展运算符

扩展运算符(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] = arrconsole.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
// 1for (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.mzph.cn/news/191047.shtml

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

相关文章

Python操作合并单元格

如何使用python操作Excel实现对合并单元格的一系列操作 01、准备工作&#xff08;使用镜像下载&#xff09; pip install openpyx -i https://pypi.tuna.tsinghua.edu.cn/simple 02、简单示例 简单创建一个工作簿进行示范&#xff1a; from openpyxl import Workbook from o…

波奇学C++:智能指针(二):auto_ptr, unique_ptr, shared_ptr,weak_ptr

C98到C11&#xff1a;智能指针分为auto_ptr, unique_ptr, shared_ptr&#xff0c;weak_ptr,这几种智能都是为了解决指针拷贝构造和赋值的问题 auto_ptr&#xff1a;允许拷贝&#xff0c;但只保留一个指向空间的指针。 管理权转移&#xff0c;把拷贝对象的资源管理权转移给拷贝…

vue中实现纯数字键盘

一、完整 代码展示 <template><div class"login"><div class"login-content"><img class"img" src"../../assets/image/loginPhone.png" /><el-card class"box-card"><div slot"hea…

阵列信号处理---频率-波数响应和波束方向图

波束延迟求和器 阵列是由一组全向阵元组成&#xff0c;阵元的位置为 p n p_n pn​&#xff0c;如下图所示&#xff1a; 阵元分别在对应的位置对信号进行空域采样&#xff0c;这样就产生了一组信号信号为 f ( t , p ) f(t,p) f(t,p),具体表示如下&#xff1a; f ( t , p ) [ f…

C++入门篇(零) C++入门篇概述

目录 一、C概述 1. 什么是C 2. C的发展史 3. C的工作领域 4. C关键字(C98) 二、C入门篇导论 一、C概述 1. 什么是C C是基于C语言而产生的计算机程序设计语言&#xff0c;支持多重编程模式&#xff0c;包括过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计和设计模式…

SQL Server 2016(创建数据库)

1、实验环境。 某公司有一台已经安装了SQL Server 2016的服务器&#xff0c;现在需要新建数据库。 2、需求描述。 创建一个名为"db_class"的数据库&#xff0c;数据文件和日志文件初始大小设置为10MB&#xff0c;启用自动增长&#xff0c;数据库文件存放路径为C:\db…

Ubuntu系统CLion安装与Ubuntu下菜单启动图标设置

Ubuntu系统CLion安装 pycharm 同理。 参考官网安装过程&#xff1a;官网安装过程 下载linux tar.gz包 # 解压 sudo tar -xzvf CLion-*.tar.gz -C /opt/ sh /opt/clion-*/bin/clion.sh其中第二个命令是启动CLion命令 clion安装完以后&#xff0c;不会在桌面或者菜单栏建立图…

大学里学编程,为什么这么难?

在大学学习计算机专业&#xff0c;为何很多同学觉得编程学得不顺心呢&#xff1f;许多同学会有这种感觉&#xff0c;在上大学里的计算机专业课程时&#xff0c;听得头都大了&#xff0c;但是真正要写代码&#xff0c;却不知道从哪里开始&#xff0c;或是觉得&#xff0c;大学里…

05:2440----代码重定义

目录 一&#xff1a;引入 1&#xff1a;基本概念 2&#xff1a;NAND启动 3&#xff1a;NOR启动 4:变量 5&#xff1a;实验证明 A:代码makefile B:NOR启动 C:NAND启动 D:内存空间 二&#xff1a;链接脚本 1:NOR 2:NAND 3:解决方法 A:尝试解决 B:方法一解决 A:简…

【SparkSQL】SparkSQL的运行流程 Spark On Hive 分布式SQL执行引擎

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍、SparkSQL的运行流程、 SparkSQL的自动优化、Catalyst优化器、SparkSQL的执行流程、Spark On Hive原理配置、分布式SQL执行引擎概念、代码JDBC连接。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文…

Echarts大屏可视化_05 折线图的定制开发

继续跟着pink老师学习Echarts相关内容&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 折线图1 1.引入 折线图选取示例地址 标题没有用到就给他删了 直接引入 注意这里是line下面的chart 获取dom元素一定不…

吉他初学者学习网站搭建系列(4)——如何查询和弦图

文章目录 背景实现ChordDbvexchords 背景 作为吉他初学者&#xff0c;如何根据和弦名快速查到和弦图是一个必不可少的功能。以往也许你会去翻和弦的书籍查询&#xff0c;像查新华字典那样&#xff0c;但是有了互联网后我们不必那样&#xff0c;只需要在网页上输入和弦名&#…

POSTGRESQL中如何利用SQL语句快速的进行同环比?

1. 引言 在数据驱动的时代&#xff0c;了解销售、收入或任何业务指标的同比和环比情况对企业决策至关重要。本文将深入介绍如何利用 PostgreSQL 和 SQL 语句快速、准确地进行这两种重要分析。 2. 数据准备 为了演示&#xff0c;假设我们有一张 sales 表&#xff0c;存储了销…

【PyTorch】线性回归

文章目录 1. 代码实现1.1 一元线性回归模型的训练 2. 代码解读2.1. tensorboardX2.1.1. tensorboardX的安装2.1.2. tensorboardX的使用 1. 代码实现 波士顿房价数据集下载 1.1 一元线性回归模型的训练 import numpy as np import torch import torch.nn as nn from torch.ut…

深度学习:什么是知识蒸馏(Knowledge Distillation)

1 概况 1.1 定义 知识蒸馏&#xff08;Knowledge Distillation&#xff09;是一种深度学习技术&#xff0c;旨在将一个复杂模型&#xff08;通常称为“教师模型”&#xff09;的知识转移到一个更简单、更小的模型&#xff08;称为“学生模型”&#xff09;中。这一技术由Hint…

二叉树遍历及应用

文章目录 前言构建二叉树前序遍历中序遍历后序遍历二叉树的结点个数二叉树的叶节点个数二叉树的高度二叉树第K层结点个数 前言 二叉树的遍历及应用主要是运用了递归、分治的思想。在这一篇文章&#xff0c;小编将介绍二叉树的前序遍历、中序遍历、后序遍历&#xff0c;求二叉树…

Fiddler抓包工具之fiddler设置手机端抓包

fiddler设置手机端抓包 安卓手机抓包 第一步&#xff1a;配置电脑和安卓的相关设置 1、手机和fiddler位于同一个局域网内&#xff1b;首先从fiddler处获取到ip地址和端口号&#xff1a; &#xff0c;点击online&#xff0c;最后一行就是ip地址 2、路径&#xff1a;Tools》O…

【ASP.NET CORE】数据迁移 codefirst

已经写好实体类&#xff0c;使用add-migration生成数据迁移语句&#xff0c;注意如果项目中有多个dbcontext需要使用 -context 名称&#xff0c;指定下需要使用的dbcontext add-Migration Address -context mvcsqlcontext运行后会生成两个文件 2. 使用Update-Database语句更…

Web自动化测试详解

做测试的同学们都了解&#xff0c;做Web自动化&#xff0c;我们主要用Selenium或者是QTP。 有的人可能就会说&#xff0c;我没这个Java基础&#xff0c;没有Selenium基础&#xff0c;能行吗&#xff1f;测试虽然属于计算机行业&#xff0c;但其实并不需要太深入的编程知识&…

C++学习之路(十六)C++ 用Qt5实现一个工具箱(为屏幕颜色提取功能增加一个点击复制的功能)- 示例代码拆分讲解

上篇文章&#xff0c;我们用 Qt5 实现了在小工具箱中添加了《颜色代码转换和屏幕颜色提取功能》功能。今天我们把屏幕颜色提取的功能再扩展一下&#xff0c;让它可以点击复制吧。下面我们就来看看如何来规划开发这样的小功能并且添加到我们的工具箱中吧。 老规矩&#xff0c;先…