一、扩展运算符
扩展运算符(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