数组基础
简介
数组是应用最广泛的数据存储结构。它被植入到大部分编程语言中。在 ECMAScript 中数组是非常常用的引用类型。
属性
length
length属性表示数组的长度,即其中元素的个数。
JavaScript数组的length属性是可变的,当length属性被设置得更大时,整个数组的状态事实上不会发生变化,仅仅是length属性变大;当length属性被设置得比原来小时,则原先数组中索引大于或等于length的元素的值全部被丢失。
prototype
返回对象类型原型的引用。prototype 属性是 object 共有的。
一般用来给数组实例添加方法。
constructor
表示创建对象的函数。
说明:constructor 属性是所有具有 prototype 的对象的成员。constructor 属性保存了对构造特定对象实例的函数的引用。
基本操作
创建数组
|
|
检测数组
|
|
数组方法 isArray, from, of
Array.isArray(obj)
检测对象是否 Array ,是则返回true,否则为false。
Array.from(arrayLike, mapFn, thisArg)
该方法从一个类似数组或可迭代对象创建一个新的数组实例。参数 arrayLike 是想要转换成真实数组的类数组对象或可遍历对象。mapFn 是可选参数,如果指定了该参数,则最后生成的数组会经过该函数的加工处理后再返回。thisArg是可选参数,为执行 mapFn 函数时 this 的值。
所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from方法转为数组。
实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的 arguments 对象。
|
|
Array.of(item…)
该方法用于创建数组实例。该方法用于替代 Array() 或 new Array()。Array.of() 和 Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个包含 7 个 undefined 元素的数组。
数组实例方法
转换方法 join
arr.join(separator)
把数组构构造成字符串,它先把数组中的每个元素转换成字符串,然后再用 separator 分隔符把它们链接在一起,separator 分隔符默认是逗号 “,”,要想做到无间隔链接,可以使用空字符串作为 separator:
|
|
栈方法 push, pop
arr.push(item…)
将一个或多个新元素添加到数组结尾,并返回数组新长度。
arr.pop()
移除最后一个元素并返回该元素值。
队列方法 unshift, shift
arr.unshift(item…)
将一个或多个新元素添加到数组开始,数组中的元素自动后移,返回数组新长度。
arr.shift()
移除最前一个元素并返回该元素值,数组中元素自动前移.如果这个数组是空的,它会返回 undefined。shift 通常比 pop 慢的多。
重排序方法 reverse, sort
arr.reverse()
反转数组的顺序。
arr.sort(comparefn)
给数组排序,默认升序。
注意 sort 默认会将数组内容视为字符串来排序,所以对数字排序时默认的排序规则会错的离谱。一般我们给sort带入个比较函数来替代原来的默认的比较方法,比较方法接受两个参数,如果两个参数相等则返回0,如果第一个参数应该排在前面则返回一个负数,如果第二个参数应该排在前面则返回一个正数:
操作方法 concat, slice, splice, copyWithin, fill
arr.concat(item…)
该方法产生一份 arr 的潜复制,并将多个数组(也可以是字符串,或者是数组和字符串的混合)附加在其后连接为一个数组,返回连接好的新的数组。
arr.slice(start, end)
该方法对数组中的一段做浅复制,首先复制数组 arr[start] 至 arr[end] 的部分,注意不包括 end 对应的元素,如果省略 end 将复制 start 之后的所有元素(或者理解成 end 的默认值为 arr.length)。字符串也有个同名方法 string.slice。
arr.splice(start, deleteCount, item…)
该方法从 arr 中移除一个或多个元素,并将新的 item 插入至移除元素的开始位置, 参数 start 是移除元素的开始位置,deleteCount 是要移除的元素的个数,item 是要被插入的元素。它返回一个包含被移除元素的数组。
arr.copyWithin(target, start, end)
该方法复制数组的一部分到同一数组中的另一个位置(会覆盖原成员),并返回修改后的数组。使用这个方法,会修改当前数组。参数 target 为开始替换数据的位置,若 target 大于等于 arr.length,将会不发生拷贝。start 是可选参数,为开始读取数据的位置,默认为0。end 是可选参数,为停止读取数据的位置,默认为 arr.length。
上面代码表示将从3号位直到数组结束的成员(4和5),复制到从0号位开始的位置,结果覆盖了原来的1和2。
arr.fill(value, start, end)
该方法使用给定值填充一个数组,参数 value 是用来填充数组的值。start 是可选参数,为填充开始位置,默认为 0。end 是可选参数,为填充的结束位置,默认为 arr.length。
fill 方法是个可变方法, 它会改变调用它的 this 对象本身, 然后返回它, 而并不是返回一个副本。
位置方法 indexOf, lastIndexOf, includes
arr.indexOf(searchElement, start)
该方法返回要查找的项在数组中的位置,如果没找到返回 -1。接受两个参数,searchElement 是要查找的项,start 是查找起始位置的索引,默认是0。
arr.lastIndexOf(searchElement, start)
从 start 位置开始向前查找,start 默认值为 arr.length – 1。
注意该方法在比较查找项与数组中每一项时,会使用全等操作符,也就是要求查找的项必须严格相等。
arr.includes(searchElement, fromIndex)
该方法用来判断当前数组是否包含某指定的值,如果是,则返回 true,否则返回 false。参数 searchElement 为需要查找的元素值。参数 fromIndex 是可选参数,从该索引处开始查找 searchElement,如果为负值,则按升序从 array.length + fromIndex 的索引开始搜索,默认为 0。 如果fromIndex 大于等于数组长度 ,则返回 false 。该数组不会被搜索。
该方法属于ES7,但Babel转码器已经支持。
|
|
没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。
indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相当运算符(===)进行判断,这会导致对NaN的误判。includes使用的是不一样的判断算法,就没有这个问题。
|
|
includes() 方法有意设计为通用方法。它不要求this值是数组对象,所以它可以被用于其他类型的对象 (比如类数组对象)。
|
|
迭代方法 every, some, filter, forEach, map, find, findIndex, entries, keys, values
arr.every(callback,thisArg)
对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回 true。callback 被调用时传入三个参数:元素值,元素的索引,原数组。thisArg 为可选参数,指定执行 callback 时使用的 this 值。
arr.some(callback, thisArg)
对数组中的每一项运行给定函数,如果该函数对任意一项返回 true,则返回 true。
arr.filter(callback, thisArg)
对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
arr.forEach(callback)
对数组中的每一项运行给定函数,这个方法没有返回值。本质上与使用 for 循环迭代数组一样。
arr.map(callback)
对数组中的每一项运行给定函数,返回每次函数调用组成的数组。
arr.find(callback, thisArg)
该方法对数组所有成员依次执行 callback 函数,直到找出第一个返回值为 true 的成员,然后返回该成员。如果没有符合条件的成员,则返回 undefined。
|
|
find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组
arr.findIndex(callback, thisArg)
该方法与 arr.find() 类似,对数组中的成员依次执行 callback 函数,直至照吃第一个返回值为 true 的成员,然后返回该成员的索引。如果没有符合条件的成员,则返回 -1。
arr.entries()
arr.keys()
arr.values()
这三个方法都返回一个新的Array Iterator对象,可以用for…of循环进行遍历,区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
归并方法 reduce, reduceRight
arr.reduce(callback, initialValue)
arr.reduceRight(callback, initialValue)
这个两个方法都会迭代数组所有的项,然后返回一个最终值。reduce()方法从数组的第一项开始,逐个遍历到最后,而reduceRight()则从数组的最后一项开始,向前遍历到第一项。它们接受两个参数,callback 每一项上调用的函数,callback 被调用时传入四个参数:上一次调用回调返回的值、正在处理的元素、正在处理的元素的索引值(如果提供了 initialValue ,从0开始;否则从1开始)、原数组。initialValue 是可选项,作为归并基础的初始值,其值用于第一次调用 callback 的第一个参数。
数组常见问题练习
数组合并
多个一维数组合并
如有两个一维数组,要将他们合并成一个数组:
Array.prototype.concat()方法
|
|
for循环和Array.prototype.push()
|
|
reduce()或reduceRight()方法
|
|
多维(复合)数组合并成一维数组
例如有这么一组多维数组,要把它拍平成1维数组:
|
|
Array.prototype.push()方法
|
|
Array.prototype.concat()方法
|
|
可以通过Function.prototype.apply()方法,让其变得更简单:
还可以将上面方法做个精简:
在 ES6 中,还可以这样写:
如果要处理三维或更多维数组,则需要加一些判断:
使用join()和split()
利用数组实例的join()方法和字符串实例的split()方法,也可以将多维数组拍平:
|
|
这种方法的缺点在于返回的数组内全是字符串,如果不需要校验内容类型的话,可以使用这种方法。
数组去重
双重循环去重
思路:
- 构建一个空数组来存放去重后的数组
- 对原数组做循环遍历,每次从数组中取出一个元素与结果数组做对比
- 如果原数组取出的元素与结果数组元素相同,则跳出循环;反之则将其存放到结果数组
|
|
这种方法也可采用forEach()方法和indexOf()方法模拟实现:
排序遍历去重
思路:
- 构建一个空数组来存放去重后的数组
- 用sort()方法对原数组做一个排序,排完序后对数组做遍历,检查数组中第 i 个元素与结果数组中最后一个元素是否相同,如果不同,则放到结果数组中。
|
|
这种方法有两个特色:
- 去重后的数组会做排序,主要是因为原数在去重前做了排序
- 去重后的数组,与数字相同的数字字符无法区分,比如‘1’和1
对象键值对法
思路:
- 创建一个 js 对象及新数组
- 遍历原数组,每次取出一个元素与 js 对象的键作对比
- 如果不包含,将存入对象的元素的值推入到结果数组中,并将 object 对象中该属性名的值设为112345678910111213function unique(arr) {var result = []var object = {}for (var i = 0; i < arr.length; i++) {if (!object[typeof(arr[i]) + arr[i]]) { //键名里加入typeof(arr[i])是为了区别不同类型的值,如1和`1`result.push(arr[i])object[typeof(arr[i]) + arr[i]] = 1}}return result}unique([1, 2, 3, 4, 3, 2, '1', 'a', 'b', 'a']);//[1, 2, 3, 4, "a", "b"]
这种方法比较耗内存,但运行下来耗时最少,是较为优秀的方案。
#### 使用 ES6 的 Set 和 Map 方法
数组随机排序
递归的方法
思路:
- 不断从原数组中随机取一个元素放进新数组,同时删除原数组中该值,递归重复至全部取出。123456789101112131415function randomSort(arr, newArr) {var newArr = newArr || []if (arr.length == 1) {newArr.push(arr[0])return newArr; // 相当于递归退出}var random = Math.ceil(Math.random() * arr.length) - 1newArr.push(arr[random])arr.splice(random, 1)return randomSort(arr, newArr)}randomSort([1, 2, 3, 4, 5, 6, 7]); //[2, 3, 1, 5, 6, 7, 4]randomSort([1, 2, 3, 4, 5, 6, 7]); //[3, 4, 2, 5, 1, 6, 7]
使用数组 sort 方法对数组元素随机排序(效果不佳)
思路:
- 使用srot()每次比较时返回一个随机值,让数组内元素依次前后随机排序123456789Array.prototype.shuffle = function(n) {var len = this.length, num = n ? Math.min(n, len) : len, arr = this.slice(0)arr.sort(function(a, b){return Math.random() - 0.5})return arr.slice(0, num)}[1, 2, 3, 4, 5, 6, 7].shuffle()//[1, 4, 2, 5, 6, 3, 7]
这个方法虽然实现了随机排序,但由于sort()方法是依次比较的,所以每个元素被随机到原位置后面的概率会越来越低。
随机交换数组内的元素 (原理from underscore.js)
思路:
- 遍历数组,每次从i位置后元素随机挑一个放到i位置,将原i位置元素放至被挑元素原位置123456789101112Array.prototype.shuffle = function() {var len = this.length,arr = this.slice(0),temp, index;for (var i = 0; i < len; i++) {index = i + Math.floor(Math.random() * (len - i))temp = arr[i]arr[i] = arr[index]arr[index] = temp}return arr.slice(0)}
随机从原数组抽取一个元素,加入到新数组
思路:
- 遍历数组,每次从数组中随机挑一个元素(随机数最大值为原数组剩余长度),将该元素拿出来放入新数组。1234567891011121314Array.prototype.shuffle = function() {var len = this.length,arr = this.slice(0),result = [],index;for (var i = 0; i < len; i++) {index = Math.floor(Math.random() * (len - i))console.log(index)result.push(arr.splice(index, 1)[0])}return result}[1, 2, 3, 4, 5, 6, 7].shuffle()//[5, 3, 2, 1, 7, 4, 6]
取数组中最大值最小值
遍历比较方法
思路:
- 设一个变量存放最大值,将数组中第一个值赋值给该变量
- 遍历数组与最大值变量比较,如果大于最大值,则将该值赋值最大值变量
- 遍历结束后,变量里储存的就是数组里的最大值12345678Array.prototype.max = function() {var max = this[0]this.forEach(function(v) {if (v > max) {max = v}})return max}[1,45,23,3,6,2,7,234,56].max() // 234
归并比较方法
思路:
- 使用数组实例的 reduce(function(prev, curv, index, arr)) 方法
- 依次比较回调函数中参数 prev 与 curv 的大小,返回大的那个123456Array.prototype.max = function() {return this.reduce(function(prev, curv) {return prev > curv ? prev : curv})}[1,45,23,3,6,2,7,234,56].max() // 234
利用 Math.max() 与 Math.min() 方法
思路:
- 将数组转换成参数传进Math.max()或Math.min()。1234Array.prototype.max = function() {return Math.max.apply({},this) //或 return Math.max(...this)}[1,45,23,3,6,2,7,234,56].max() // 234
将一个数按大小顺序插入数组并返回索引值
任务:
构建一个函数where(arr, num),函数接受两个参数,一个是数组arr,一个是要插进数组的数字num,函数将num按大小顺序插进arr,并返回num的索引值。例如:
|
|
使用 push + sort + indexOf 方法
思路:
- 通过push()或unshift()方法将 num 插入 arr
- 使用sort()对 arr 进行排序
- 使用indexOf()找出 num 在 arr 中的 index(或使用for、forEach、some循环)
- 返回 index1234567891011121314151617181920212223242526272829function where(arr, num) {arr.push(num)arr.sort(function(a,b){return a - b})return arr.indexOf(num)}var a = [1,2,3,4]where(a, 1,5) //1, a = [1,1.5,2,3,4]//或者function where(arr, num) {arr.push(num)arr.sort(function(a,b){return a - b})var index;arr.some(function(v,i) {if(v === num) {index = ireturn true}})return index}var a = [1,2,3,4]where(a, 1,5) //1, a = [1,1.5,2,3,4]
使用 sort + findIndex + splice 方法
思路:
- 使用sort()对 arr 进行排序
- 使用 findIndex() 找出 arr 中第一个大于 num 的元素的位置
- 使用 splice() 将 num 插入该元素前123456789function where(arr, num) {arr.sort((a,b) => a-b)const index = arr.findIndex(v => v > num)arr.splice(index,0,num)return index}var a = [1,2,3,4]where(a, 1,5) //1, a = [1,1.5,2,3,4]
从数组中寻找元素并删除元素
任务:
构建一个函数destroyer(arr, item…),传入数组与要删除的指定值,返回删除指定值后的数组,例如:
arguments + indexOf + filter 遍历判断法
思路:
- 通过 arguements 对象分别获取到数组 arr 和指定要删除的元素,将指定元素转换成数组 removeArgs
- 使用filter()筛选出 arr 内与 removeArgs 内不相同的元素,筛选规则通过indexOf()方法判断12345678910111213141516function destroyer(para) {var arr = arguments[0]var removeArgs = []for(var i = 1; i < arguments.length; i++) {removeArgs.push(arguments[i])}var isFalse = function(v) {return removeArgs.indexOf(v) === -1}return arr.filter(isFalse)}destroyer([1,2,3,4,1,3,2],1,2) //[3,4,3]
slice(或解构运算符) + es6 set + filter 方法
思路:
- 利用call()对 arguments 使用数组的slice()分别获取到原数组和指定值
- 将指定值传进 new Set 对象
- 使用filter()筛选出 arr 内与 set 对象内不相同的元素,筛选规则通过 set 对象的has()方法判断12345function destroyer(para) {var removeArgs = new Set(Array.prototype.slice.call(arguments, 1))return para.filter(v => !removeArgs.has(v))}destroyer([1,2,3,4,1,3,2],1,2) //[3,4,3]
也可以写成:
文章内容转载处
感谢: 前端记录