前言
这里继续分享一下与数组的知识点,虽然不是足够详细,但是了解七八成应该问题不大。
数组是ECMAScript中最常用的类型之一了,ECMAScript数组是一组有序的数据,每个数据元素可以是不同的任意类型,当然,数组也是有长度的,长度会随着数组元素的变化而变化
一、数组的创建
数组的创建一般有两种创建的方法,一种是字面量的方式创建,一种是以构造函数的方式创建
1、构造函数创建
构造函数的使用:
js
// 使用Array构造函数创建数组
let colors = new Array()
// 传递一个数值给Array构造函数当做参数,这个数值会被当做数组的length长度
let colors = new Array(2) // 这个数组的长度为2
// 传递非数值数值给Array构造函数当做参数,传递的参数会被数组当做它的元素来初始化
let colors = new Array('haha') // 创建一个 ['haha'] 的数组
let colors = new Array('haha', 1) // 创建一个 ['haha', 1] 的数组
在使用Array构造函数进行实例化操作的时候,可以省略new关键字:
let colors = new Array('haha') // 创建一个 ['haha'] 的数组
构造函数新增的方法: ES6对Array构造函数有两个新增的静态方法:Array.from(用于将具有可迭代的结构或者是具有length属性的结构转换为数组)
、Array.of(用于把一组参数转换为数组)
js
// Array.from举例:
// 字符串被拆分成数组
Array.from('mata') // ['m', 'a', 't', 'a']
// Map结构的转化:
const m = new Map().set(1, 2).set(3, 4)
Array.from(m) // [ [1,2], [3, 4] ]
// set结构的转化:
const s = new Set().add(1).add(2)
Array.from(s) // [1, 2]
// 浅拷贝:
const a = [1, 2, 3]
const b = Array.from(a)
console.log(b) // [1, 2, 3]
console.log(a === b) // false
// 可迭代的对象:
const iter = {
*[Symbol.iterator]() {
yield* 1
yield* 2
}
}
Array.from(iter) // [1, 2]
// arguments对象转换为数组:
function getArray() {
return Array.from(arguments)
}
getArray(1, 2, 3) // [1, 2, 3]
// 转化具有length属性的类数组结构:
const o = {
0: 1,
1: 2,
length: 2
}
Array.from(o) // [1, 2]
Array.from()的第二个参数是一个函数,用来改变创建后的数组中元素的值
Array.from()的第三个参数是一个可选参数,用于指定第二个函数中this的值
js
// Array.of的使用:
Array.of(1, 2, 3) // [1, 2, 3]
2.字面量的创建
说明: 字面量的创建的话主要是通过[]
来将元素包裹起来,每个元素之间以,
相分隔,当然,元素的类型可以是任意类型
js
// 举例:
let a = [ ] // 创建一个空数组
let b = [ 1 ] // 创建一个 [ 1 ] 的数组
这里两种方式的创建是独立的,也就是使用字面量创建的话就不会调用构造函数来创建
二、数组的空位
说明: 当你不想初始化数组某个位置的元素的时候,可以用空位将这个位置占住,一般在初始化的时候这个位置不写或者使用undefined将其占位都可以,不过最好使用undefined进行占位,因为不同的方法面对空位的行为是不一致的
js
// 举例:
const arr = [ 1, , 2 ] // 这里元素1和2之间存在一个空的位置
const arr = [ 1, undefined, 2 ] // 这样的写法与上面的写法是一样的(推荐这样写)
三、数组的索引
说明: 索引是用来取数组内储存的元素的(格式:数组名[索引]
),这些元素都存在编号(可以将索引理解成编号),编号是从最左边从0开始的(需要存在元素才存在编号),从左到右依次增加,
然后最右边的编号是一个定值,这个值是length - 1(之前说到过length表示数组中元素的个数,元素的个数是从1开始计数的,但是编号却是从0开始计数的)
js
// 举例:
let arr = [1, 2, 3]
// 数组length的长度
arr.length // 3
// 取数组的每个元素
arr[0] // 1
arr[1] // 2
arr[2] // 3
// 修改数组的元素
arr[0] = 0
arr // [0, 2, 3]
数组的length属性还可以用来删除元素,这个删除是从数组的右边开始删除的,是不可逆的
js
// 举例:
let arr = [1, 2, 3]
// 删除数组的元素
arr.length = 2 // 此时让数组arr只存在两个元素,那么元素3就没有了
arr // [1, 2]
索引 与 length - 1 的关系:
索引值 < length <--> 可以正常的进行取值和修改
索引值 = length <--> 取值取undefined,赋值这一项为数组最后一项
索引值 > length <--> 取值取undefined,这一项与length - 2之间的项是undefined
js
// 索引值 < length:
let arr = [1, 2, 3]
// 取值
arr[2] // 3
// 赋值
arr[2] = 0
arr // [1, 2, 0]
js
// 索引值 = length:
let arr = [1, 2, 3]
// 取值
arr[3] // undefined
// 赋值
arr[3] = 0
arr // [1, 2, 3, 0]
js
// 索引值 > length:
let arr = [1, 2, 3]
// 取值
arr[4] // undefined
// 赋值
arr[4] = 0
arr // [1, 2, 3, undefined, 0]
数组最多存在4294,967,295个元素,如果尝试添加更多项,则会报错,但是如果以这个最大值作为初始值来创建数组的话,可能会存在js运行时间过长而报错
检测数组的方法:Array.isArray(),其返回值是一个布尔值
四、数组相关的方法
1.迭代器方法
说明: 在ES6中,Array的原型上面新增了三个查找数组内容的方法:keys()用于返回数组的索引
、value()用于返回数组的元素
、entries()以[索引, 值]的形式返回数组内容
js
const a = ['a', 'b']
// 由于这三个方法都会返回迭代器,那么可以使用Array.from的方法将其都转换成数组
const aKeys = Array.from(a.keys())
const aValues = Array.from(a.values())
const aEntries = Array.from(a.entries())
console.log(aKeys) // [0, 1]
console.log(aValues) // ['a', 'b']
console.log(aEntries) // [[0, 'a'], [1, 'b']]
这几个方法并不会修改原数组的内容
2.填充的方法
说明: 填充的方法是fill
,它有两个参数,一个是填充的值value
和两个可选的参数填充值的起始位置start
和终止位置end
,它可以向一个存在的数组中插入一个或者多个一样的值,如果只传递了一个参数,那么就会将这个参数的值将数组填满,其次索引可以存在±
,如果索引是-
值的话表示索引从数组末尾开始取。
js
const arr = [0, 0, 0]
// 只传递一个参数:value
arr.fill(1) // [1, 1, 1]
arr.fill(0) // [0, 0, 0]
// 传递两个参数:value和start
arr.fill(1, 1) // [0, 1, 1] --> 可以理解为>=start
// 传递三个参数:value和start和end
arr.fill(1, 1, 1) // [0, 1, 0] --> 可以理解为>=start,<=end
// 传递-值:
arr.fill(1, -1) // [1, 1, 0] --> 可以理解-号表示从数组末尾开始数
注意: 如果索引的取值不合理,fill方法是会忽略这些操作的
js
// 传递的索引过大
arr.fill(1, 10) // [0, 0, 0] --> 数组arr不存在11个元素
// 传递的索引过小
arr.fill(1, -10) // [0, 0, 0] --> 数组arr不存在11个元素
// 传递的索引需要存在范围
arr.fill(1, 4, 2) // [0, 0, 0] --> 数组不存在索引大于4而小于2的地方
// 赋值只会赋给合理的元素
arr.fill(1, 2, 10) // [0, 0, 1] --> 只有满足条件才会被赋值
需要注意的是,
fill()
方法会修改原始数组,并且无法返回一个新的数组。
3.复制的方法
说明: ES6新增复制的方法是copyWithin
,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组,它存在三个参数,分别是目标位置target
,复制的起始位置start(默认值是0)
和终止位置end(默认值是数组的length)
,后两个参数是可选的
js
// 传递一个参数:会将当前索引到数组末尾之间的元素会按顺序替换成数组的内容,
// 直到全部替换完毕
const arr = [1, 2, 3, 4, 5]
arr.copyWithin(2) // [1, 2, 3, 1, 2]
// 传递两个参数:如果第二个参数给出的范围只能够取到一个值的话,那么只会替换
// 一个元素,如果能够取到多个,就会替换掉多个
const arr = [1, 2, 3, 4, 5]
console.log(arr.copyWithin(2, 4)) // [1, 2, 5, 4, 5]
console.log(arr.copyWithin(2, 3)) // [1, 2, 4, 5, 5]
// 传递三个参数:得到的取值区间存在几个值,就会替换掉几个值
const arr = [1, 2, 3, 4, 5]
console.log(arr.copyWithin(2, 4, 5)) // [1, 2, 5, 4, 5]
console.log(arr.copyWithin(2, 3, 4)) // [1, 2, 4, 4, 5]
需要注意的是,
copyWithin()
方法会修改原始数组,并且返回当前数组,并且取值的时候是包含索引start
的元素,不包含索引end
的元素,同时,注意事项跟fill
方法是一样的
4.转换的方法
说明: 这里方法有四个,分别是toLocaleString
、toString
这两个都会返回由数组中的每个值的等效字符串拼接而成的一个以逗号分隔的字符串、valueof
则会返回数组的原始值、join
的参数表示变成字符串的连接符。
js
const arr = [1, 2, 3];
console.log(arr.toLocaleString()); // 输出: "1,2,3"
console.log(arr.toString()); // 输出: "1,2,3"
console.log(arr.valueOf()); // 输出: [1,2,3]
toLocaleString()
: 该方法返回一个表示数组元素的本地化字符串。它会遍历数组中的每个元素,并调用每个元素的toLocaleString()
方法。这意味着每个元素都可以根据本地化设置返回不同的字符串表示形式。默认情况下,toLocaleString()
方法使用逗号进行分隔。toString()
: 该方法将数组转换为一个字符串。它会遍历数组中的每个元素,并调用每个元素的toString()
方法。然后,它使用逗号作为分隔符将这些字符串拼接在一起,最终返回一个字符串。valueOf()
: 该方法返回数组的原始值。对于数组对象来说,valueOf()
方法会直接返回该对象本身,因为数组本身就是一个对象。
js
const arr = [1, 2, 3];
console.log(arr.join('-')); // 输出: "1-2-3"
如果不给
join
方法提供参数,或者提供undefined
,则会使用默认的参数,也就是,
,并且,如果数组中的某一项是null
或者是undefined
,则返回的结果会用空字符串
表示
同时,上面这四个方法并不会修改原数组的内容
5.栈方法
说明: ECMAScript
给数组提供了几个方法,让其变成一种可以限制插入和删除项的数据结构,也就是跟栈类似,栈是一种后进先出的结构,数据项的插入和删除只在栈顶发生,对应的方法分别是push
和pop
,push
方法接受任意数量的参数,将它们添加到数组的末尾并返回最新的长度,而pop
方法则会删除数组的最后一项,同时减少length
的值,并返回被删除的项
js
const arr = [1, 2, 3, 4, 5]
arr.push(1) // [ 1, 2, 3, 4, 5, 1 ]
arr.pop() // [ 1, 2, 3, 4, 5]
let arr1 = arr.push(1) // 6
let arr2 = arr.pop() // 1 --> 这个可以理解为取数组第一项的值,不过是破坏性的
上面这两个方法是直接操作原始数组的,所以这两个方法会修改原始数组的内容
6.队列方法
说明: ECMAScript
给数组提供了几个方法让其可以跟队列的数据结构类似,队列是一种先进先出的数据结构,实现这种操作的方法有两个,分别是shift
和unshift
,shift
的话会删除数组的第一项并且返回,同时减少数组的length
值,unshift
则是往数组的开头添加一项或者多项数据,并返回数组的length
长度
js
const arr = [1, 2, 3, 4, 5]
arr.unshift(1) // [1, 1, 2, 3, 4, 5]
arr.shift() // [1, 2, 3, 4, 5]
let arr1 = arr.unshift(1) // 6
let arr2 = arr.shift() // 1 --> 这个可以理解为取数组第一项的值,不过是破坏性的
上面这两个方法是直接操作原始数组的,所以这两个方法会修改原始数组的内容
7.排序方法
说明: 数组的排序方法有两个,一个是reverse
,一个是sort
,reverse
就是将原数组反向排列,其灵活性不如sort
,sort
如果不传递参数,那么它会将数组中的每一个元素调用String的转型函数,将其变成字符串,通过比较其Unicode码值来决定其大小,这在多数情况下会出现bug
,所以在使用的时候会传递一个排序函数来进行排序
js
const arr = [1, 2, 3, 4, 5]
arr.reverse() // [ 5, 4, 3, 2, 1 ]
arr.reverse() // [ 1, 2, 3, 4, 5 ]
js
// sort不传参数的问题:
const arr = [0, 1, 5, 10, 15]
arr.sort() // [ 0, 1, 10, 15, 5 ]
js
const arr = [0, 1, 5, 10, 15]
// sort的升序函数:
function compare(a, b) {
if(a < b) {
return -1
} else if (a = b) {
return 0
} else {
return 1
}
}
// 升序函数简写:
function compare(a, b) {
return a - b;
}
// sort的降序函数:
function compare(a, b) {
if(a > b) {
return -1
} else if (a = b) {
return 0
} else {
return 1
}
}
// 降序函数简写:
function compare(a, b) {
return b - a;
}
arr.sort(compare) // [ 0, 1, 5, 10, 15 ]
一般
sort
方法的排序函数都会使用简写的形式,通常会使用箭头函数,因为很简洁,其次,关于怎么记忆排序函数是升序还是降序,可以看返回值,因为参数都是a
和b
,表示前一个值和后一个值,由于返回值都是两数之差,都理解成差为负数,如果返回值是a-b
,它是负数,那么表示b
比a
要大,也就是前一个值比后一个值要大,那么也就是升序排列了,那么b-a
也是同理。
这两个方法都是对原数组进行操作,那么都会修改原数组并将排序后的数组返回,但是,返回的数组与原始数组的引用是一致的,也就是用===来判断的话,其结果是true
8.操作方法
说明: concat
方法会先创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回新构建的数组,如果传入的是一个或者多个数组,那默认情况它就会把数组的每一项取出来然后添加到副本数组中去,如果参数不是数组,它就会直接将他们添加到数组的末尾,这种默认行为称为打平数组参数
,如果需要保留参数的格式的话需要设置数组上面的一个符号Symbol.isConcatSpreadable
将其设置为false(默认值是true)
js
let arr = ['1', '2', '3']
let arr2 = arr.concat('4', ['5', '6'])
console.log(arr) // [ '1', '2', '3' ]
console.log(arr2) // [ '1', '2', '3', '4', '5', '6' ]
js
let arr = ['1', '2', '3']
let arr1 = [1, 2, 3]
let arr2 = {
[Symbol.isConcatSpreadable]: true,
length: 2,
0: 1,
1: 2
}
arr1[Symbol.isConcatSpreadable] = false
// 强制不打平数组
let arr3 = arr.concat('haha',arr1)
// 强制打平类数组
let arr4 = arr.concat('heheh', arr2)
console.log(arr) // ['1', '2', '3']
console.log(arr3) // ['1', '2', '3', 'haha', [1, 2, 3]]
console.log(arr4) // 0['1', '2', '3', 'heheh', 1, 2]
concat方法并不会修改原始数组的内容
说明: slice
方法会创建一个包含原始数组中的一个或者多个元素的新数组,它接受两个参数,开始的索引start
和结束的索引end
,如果只传递了一个参数,slice
会返回从当前索引到数组末尾的所有元素,如果传递了两个参数那么就会返回这两个索引之间的所有元素,但是不包括end索引位置的元素
js
let arr = ['1', '2', '3']
arr.slice(1) // '2', '3'
arr.slice(1, 2) // '2' --> 不包含end索引的元素
slice方法同样不会修改原始数组的内容,当然索引也会存在负数的情况,一样,如果出现负数就表示从数组末尾开始,如果两个索引之间取值取不到的话那么就会返回空数组
说明: splice
一般用来删除元素,它有三个参数或者多个参数,第一个参数表示起始位置start
,第二个参数表示删除的数量number
,从第三个参数开始表示需要在删除元素的后面需要插入的元素
js
let arr = ['apple', 'banana', 'kiwi'];
// 传递两个参数理解为删除操作
let removed = arr.splice(2, 2); // ['orange', 'strawberry']
// 传递第二参数为0理解为插入的操作
arr.splice(1, 0, 'orange', 'strawberry'); // ['apple', 'orange', 'strawberry', 'banana', 'kiwi']
// 传递第二个参数不为0理解为替换操作
arr.splice(1, 1, 'orange'); // ['apple', 'orange', 'kiwi']
splice方法会修改原始数组的内容,并且它始终会返回数组中被删除的元素
9.搜索和位置方法
说明: 按严格相等搜索
的方法有三个,分别是indexOf
、lastIndexOf
、includes
,前面两个方法在所有的版本之中都可以使用,后面的那个是ECMAScript
新增的,这些方法都接收两个参数:要查找的元素value
和搜索的起始位置start
,indexOf
和includes
都是从数组的第一项开始找,而lastIndexOf
则是从数组的最后一项开始查找,indexOf
、lastIndexOf
的返回值是元素在数组中的位置,也就是返回索引,如果没找到就会返回-1
,但是includes
只会返回布尔值,表示数组中是否存在查找的这个元素
js
let arr = ['apple', 'banana', 'kiwi', 'orange'];
console.log(arr.indexOf('banana')); // 输出:1
console.log(arr.indexOf('strawberry')); // 输出:-1
let arr = ['apple', 'banana', 'kiwi', 'orange', 'banana'];
console.log(arr.lastIndexOf('banana')); // 输出:4
console.log(arr.lastIndexOf('strawberry')); // 输出:-1
let arr = ['apple', 'banana', 'kiwi'];
console.log(arr.includes('banana')); // 输出:true
console.log(arr.includes('strawberry')); // 输出:false
上面的三个方法都不会修改原始数组的内容
说明: 按断言函数搜索
的方法有两个,find
和findindex
,使用断言函数搜索数组的时候每个索引都会调用这个函数,其返回值决定了相应索引的元素是否匹配,断言函数接受三个参数,element
当前搜索的元素、index
当前元素的索引、array
正在搜索的数组,其返回值如果为真,就表示匹配,find
和findindex
的第一个参数是断言函数,第二参数是改变断言函数内部this的指向
js
let arr = [10, 20, 30, 40, 50];
let result = arr.find(element => element > 25); // 30
let arr = [10, 20, 30, 40, 50];
let index = arr.findIndex(element => element > 25); // 2
find()
方法会返回满足条件的元素,而findIndex()
方法则返回满足条件的元素的索引,需要注意的是,find()
和findIndex()
方法遵循从左到右的顺序,在找到满足条件的元素后会停止搜索。如果数组中有多个元素满足条件,只返回第一个满足条件的元素或索引,并且这两个方法不会修改原始数组中的内容
10.迭代方法
说明: ECMAScript
为数组定义了5个迭代的方法,每个方法接收两个参数,第一个参数是一个函数,用来处理数组中的每一项,这个函数的参数有三个,element
数组的元素,index
元素的索引,array
数组本身,第二个参数用来改变this的指向,方法的返回值会根据方法的不同而不同
forEach()
: 对数组中的每一项都运行传入函数,没有返回值
js
let arr = [1, 2, 3, 4, 5];
arr.forEach(element => {
console.log(element);
// 1
// 2
// 3
// 4
// 5
});
map()
: 对数组中的每一项都运行传入函数,返回值是每次函数调用得到的结果构成的数组
js
let arr = [1, 2, 3, 4, 5];
let newArray = arr.map(element => element * 2); // [2, 4, 6, 8, 10]
filter()
: 对数组中的每一项都运行传入函数,创建一个新数组,包含所有满足指定函数返回 true
的元素,起过滤的作用。
js
let arr = [1, 2, 3, 4, 5];
let newArray = arr.filter(element => element % 2 === 0); // [2, 4]
every()
: 对数组中的每一项都运行传入函数,检测数组中的所有元素是否都满足指定的测试函数,有一个不满足就会返回false
。
js
let arr = [1, 2, 3, 4, 5];
let allEven = arr.every(element => element % 2 === 0); // false
some()
: 对数组中的每一项都运行传入函数,检测数组中是否至少有一个元素满足指定的测试函数,有一个满足就会返回true
。
js
let arr = [1, 2, 3, 4, 5];
let hasEven = arr.some(element => element % 2 === 0); // true
11.归并方法
说明: ECMASript
为数组提供了两个归并的方法,一个是reduce
、一个是reduceRight
,这两个的区别在于遍历的方向不同,前者是从数组的第一项开始遍历,后者是从数组的最后一项开始遍历,这两个方法都接受两个参数,一个处理函数和一个开始归并的初始值,处理函数接受4个参数: prev
上一个归并值、currentValue
当前值 、currentIndex
当前索引 、 array
原始数组。函数的返回值将被用作下一次迭代的初始值。
js
let arr = [1, 2, 3, 4, 5];
let sum = arr.reduce((prev, currentValue)
=> accumulator + currentValue, 0); // 15