【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')] // [
,
,
]

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

【06】ES6:数组的扩展_第1张图片
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

你可能感兴趣的:(ES,es6,javascript,前端)