前端常见的数组问题|数组全家桶
开始之前,我们先创建一个数组
var fruits = ['Apple', 'Banana']; console.log(fruits.length); // 2 复制代码
一,数组的方法
1.添加元素到数组的末尾 push()
var newLength = fruits.push('Orange'); // newLength:3; fruits: ["Apple", "Banana", "Orange"] 复制代码
2.删除数组末尾的元素 pop()
var last = fruits.pop(); // remove Orange (from the end) // last: "Orange"; fruits: ["Apple", "Banana"]; 复制代码
3.删除数组最前面(头部)的元素 shift()
var first = fruits.shift(); // remove Apple from the front // first: "Apple"; fruits: ["Banana"]; 复制代码
4.添加元素到数组的头部 unshift()
var newLength = fruits.unshift('Strawberry') // add to the front // ["Strawberry", "Banana"]; 复制代码
5.找出某个元素在数组中的索引 indexOf()
注意点:
当某个元素不存在数组中的时候会返回-1,所以可以用于过滤数组
如果某个元素在数组中存在多个,则只会返回第一次出现的索引
arr.indexOf(searchElement[, fromIndex]),接受两个参数,第二个参数表示开始查找的位置
采用的是严格等于(===)来查找
fruits.push('Mango'); // ["Strawberry", "Banana", "Mango"] var pos = fruits.indexOf('Banana'); // 1 复制代码
6.通过索引删除或添加元素 splice()
这个函数我们经常用来删除元素,其实还可以用来添加元素 注意点:
array.splice(start[, deleteCount[, item1[, item2[, ...]]]])。接受三个参数,第一个是要开始删除的元素的索引,第二参数是要删除的元素数量,如果不指定,则 splice() 将删除start所在元素之后所有元素, 第三个参数是数组形式,是要添加进数组的元素,从start 位置开始。如果不指定,则 splice() 将只删除数组元素。
此方法会改变原数组,并且返回值是被删除的元素
var vegetables = ['Cabbage', 'Turnip', 'Radish', 'Carrot']; console.log(vegetables); // ["Cabbage", "Turnip", "Radish", "Carrot"] var pos = 1, n = 2; var removedItems = vegetables.splice(pos, n); // this is how to remove items, n defines the number of items to be removed, // from that position(pos) onward to the end of array. console.log(vegetables); // ["Cabbage", "Carrot"] (the original array is changed) console.log(removedItems); // ["Turnip", "Radish"] 复制代码
7.复制一个数组 slice()
这个函数确实没怎么用过,不过有一个地方挺重要的,而且我们很常用的,就是类数组对象,就跟这个有关:
function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] 复制代码
var shallowCopy = fruits.slice(); // this is how to make a copy // ["Strawberry", "Mango"] 复制代码
除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程。
var unboundSlice = Array.prototype.slice; var slice = Function.prototype.call.bind(unboundSlice); function list() { return slice(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] 复制代码
注意点:
arr.slice([begin[, end]])slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。
如果传入的参数是负值将会倒数取值复制。
8.Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例
可以把string,set,map,等一些对象转化为数组的形式。 后面也可以使用箭头函数进行元素的操作。
console.log(Array.from('foo')); // expected output: Array ["f", "o", "o"] console.log(Array.from([1, 2, 3], x => x + x)); // expected output: Array [2, 4, 6] 复制代码
9.Array.of()
Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
Array.of(7); // [7] Array.of(1, 2, 3); // [1, 2, 3] Array(7); // [ , , , , , , ] Array(1, 2, 3); // [1, 2, 3] 复制代码
10.合并数组: concat()
该方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
const array1 = ['a', 'b', 'c']; const array2 = ['d', 'e', 'f']; const array3 = array1.concat(array2); console.log(array3); // expected output: Array ["a", "b", "c", "d", "e", "f"] 复制代码
11.copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
const array1 = ['a', 'b', 'c', 'd', 'e']; // copy to index 0 the element at index 3 console.log(array1.copyWithin(0, 3, 4)); // expected output: Array ["d", "b", "c", "d", "e"] // copy to index 1 all elements from index 3 to the end console.log(array1.copyWithin(1, 3)); // expected output: Array ["d", "d", "e", "d", "e"] 复制代码
12.entries() 方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对。
const array1 = ['a', 'b', 'c']; const iterator1 = array1.entries(); console.log(iterator1.next().value); // expected output: Array [0, "a"] console.log(iterator1.next().value); // expected output: Array [1, "b"] 复制代码
13.every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
const isBelowThreshold = (currentValue) => currentValue < 40; const array1 = [1, 30, 39, 29, 10, 13]; console.log(array1.every(isBelowThreshold)); // expected output: true 复制代码
14.fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。
const array1 = [1, 2, 3, 4]; // fill with 0 from position 2 until position 4 console.log(array1.fill(0, 2, 4)); // expected output: [1, 2, 0, 0] // fill with 5 from position 1 console.log(array1.fill(5, 1)); // expected output: [1, 5, 5, 5] console.log(array1.fill(6)); // expected output: [6, 6, 6, 6] 复制代码
15.filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; const result = words.filter(word => word.length > 6); console.log(result); // expected output: Array ["exuberant", "destruction", "present"] 复制代码
16.flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
const arr1 = [0, 1, 2, [3, 4]]; console.log(arr1.flat()); // expected output: [0, 1, 2, 3, 4] const arr2 = [0, 1, 2, [[[3, 4]]]]; console.log(arr2.flat(2)); // expected output: [0, 1, 2, [3, 4]] 复制代码
17.join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
const elements = ['Fire', 'Air', 'Water']; console.log(elements.join()); // expected output: "Fire,Air,Water" console.log(elements.join('')); // expected output: "FireAirWater" console.log(elements.join('-')); // expected output: "Fire-Air-Water" 复制代码
18. map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
const array1 = [1, 4, 9, 16]; // pass a function to map const map1 = array1.map(x => x * 2); console.log(map1); // expected output: Array [2, 8, 18, 32] 复制代码
19. reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。
这个也算是经常用到,具体可以看here
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // expected output: 10 // 5 + 1 + 2 + 3 + 4 console.log(array1.reduce(reducer, 5)); // expected output: 15 复制代码
20. reverse() 方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。
const array1 = ['one', 'two', 'three']; console.log('array1:', array1); // expected output: "array1:" Array ["one", "two", "three"] const reversed = array1.reverse(); console.log('reversed:', reversed); // expected output: "reversed:" Array ["three", "two", "one"] // Careful: reverse is destructive -- it changes the original array. console.log('array1:', array1); // expected output: "array1:" Array ["three", "two", "one"] 复制代码
21. some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
const array = [1, 2, 3, 4, 5]; // checks whether an element is even const even = (element) => element % 2 === 0; console.log(array.some(even)); // expected output: true 复制代码
22. sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的.
const months = ['March', 'Jan', 'Feb', 'Dec']; months.sort(); console.log(months); // expected output: Array ["Dec", "Feb", "Jan", "March"] const array1 = [1, 30, 4, 21, 100000]; array1.sort(); console.log(array1); // expected output: Array [1, 100000, 21, 30, 4] 复制代码
二,怎么判断是否是数组
1.Array.isArray()
当检测Array实例时, Array.isArray 优于 instanceof,因为Array.isArray能检测iframes.
// 下面的函数调用都返回 true Array.isArray([]); Array.isArray([1]); Array.isArray(new Array()); Array.isArray(new Array('a', 'b', 'c', 'd')) // 鲜为人知的事实:其实 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 }); 复制代码
2.instance of
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。 同样可以检测是否是数组。 但是为什么instance of不能检测到iframes呢?
在浏览器中,我们的脚本可能需要在多个窗口之间进行交互。多个窗口意味着多个全局环境,不同的全局环境拥有不同的全局对象,从而拥有不同的内置类型构造函数。这可能会引发一些问题。比如,表达式 [] instanceof window.frames[0].Array 会返回 false,因为 Array.prototype !== window.frames[0].Array.prototype,并且数组从前者继承。
起初,你会认为这样并没有意义,但是当你在你的脚本中开始处理多个 frame 或多个 window 以及通过函数将对象从一个窗口传到另一个窗口时,这就是一个有效而强大的话题。比如,实际上你可以通过使用Array.isArray(myObj) 或者Object.prototype.toString.call(myObj) === "[object Array]" 来安全的检测传过来的对象是否是一个数组。
比如检测一个 Nodes 在另一个窗口中是不是 SVGElement,你可以使用myNode instanceof myNode.ownerDocument.defaultView.SVGElement
3.Object.prototype.toString.call(myObj) === "[object Array]"
4.原型链(constructor)
一般情况下,除了 undefined 和 null,其它都能使用 constructor 判断类型。
var arr = [1, 2, 3] console.log(arr.__proto__.constructor === Array) // true console.log(arr.constructor === Array) // true // 注意:arr.__proto__ === Array.prototype 为 true。 复制代码
但是某些情况下,判断是不准确的,比如:
// 构造函数 function Fn() {} // 修改原型对象 Fn.prototype = new Array() // 实例化对象 var fn = new Fn() console.log(fn.constructor === Fn) // false console.log(fn.constructor === Array) // true // 此时的 fn 应该是一个普通对象,而非数组,所以此时使用 constructor 判断是不合适的。 复制代码
三, 数组遍历的几种方式
1.forEach()
arr.forEach(function (item, index, array) { console.log(item, index); }); 复制代码
2.for 循环
3.for ...in
一般会使用for-in来遍历对象的属性的,不过属性需要 enumerable,才能被读取到. for-in 循环只遍历可枚举属性。一般常用来遍历对象,包括非整数类型的名称和继承的那些原型链上面的属性也能被遍历。像 Array和 Object使用内置构造函数所创建的对象都会继承自Object.prototype和String.prototype的不可枚举属性就不能遍历了.
4.for ...of
大家一定好奇,这么多循环的方式,到底用哪一种呢。我就找来了网上大佬的测试图,来看看各种循环的速度吧。 这里可以看到最快的还是我们经典的for循环,最慢的是for-in,因为他需要遍历原型链上的属性的原因。而且他也最常用在遍历对象上。
for > for-of > forEach > filter > map > for-in
四, 扁平化数组方法
1.flat()
首先就是api里的这个方法啦,可以传入参数指定要提取嵌套数组的结构深度,默认值为 1。
2. 使用 reduce 与 concat
var arr = [1, 2, [3, 4]]; // 展开一层数组 arr.flat(); // 等效于 arr.reduce((acc, val) => acc.concat(val), []); // [1, 2, 3, 4] // 使用扩展运算符 ... const flattened = arr => [].concat(...arr); 复制代码
3. reduce + concat + isArray + recursivity
// 使用 reduce、concat 和递归展开无限多层嵌套的数组 var arr1 = [1,2,3,[1,2,3,4, [2,3,4]]]; function flatDeep(arr, d = 1) { return d>0?arr.reduce((acc,val)=> arr.concat(Array.isArray(val)?flatDeep(val,d-1):val),[ ] ):arr.slice(); }; flatDeep(arr1, Infinity); // [1, 2, 3, 1, 2, 3, 4, 2, 3, 4] 复制代码
4.forEach+isArray+push+recursivity
其实就是循环递归
// forEach 遍历数组会自动跳过空元素 const eachFlat = (arr = [], depth = 1) => { const result = []; // 缓存递归结果 // 开始递归 (function flat(arr, depth) { // forEach 会自动去除数组空位 arr.forEach((item) => { // 控制递归深度 if (Array.isArray(item) && depth > 0) { // 递归数组 flat(item, depth - 1) } else { // 缓存元素 result.push(item) } }) })(arr, depth) // 返回递归结果 return result; } // for of 循环不能去除数组空位,需要手动去除 const forFlat = (arr = [], depth = 1) => { const result = []; (function flat(arr, depth) { for (let item of arr) { if (Array.isArray(item) && depth > 0) { flat(item, depth - 1) } else { // 去除空元素,添加非undefined元素 item !== void 0 && result.push(item); } } })(arr, depth) return result; } 复制代码
五,数组去重
这个问题算是很常见了,解决的办法有很多如下:
1.排序
最简单粗暴的就是用排序了,排完序比较下
const unique = (array) => { array.sort((a, b) => a - b); let pre = 0; const result = []; for (let i = 0; i < array.length; i++) { if (!i || array[i] != array[pre]) { result.push(array[i]); } pre = i; } return result; } 复制代码
2. indexOf + filter
过滤留下重复元素出现的情况
const unique =arr =>{arr.filter((elem,index)=> arr.indexOf(elem)==index)} 复制代码
3.Object
开辟一个外部存储空间用于标示元素是否出现过。
const unique = function(arr){ var container = {}; return arr.filter((elem,index)=>container.hasOwnProperty(elem)?false: (container[item] = true)) } 复制代码
4. set
const unique = arr => Array.from(new Set(arr)); const unique = arr => [...new Set(arr)];
作者:仙儿仙儿
链接:https://juejin.cn/post/7019132559636299813