最近在回顾javascript
的知识点,花了几天的空余时间重新学习了一下MDN中的Array
对象,对Array
对象有了新的认识和理解,所以决定将这些温故知新的内容大致的整理一下并记录下来,也在于鞭策自己继续学习。
首先,我们知道Array
是引用数据类型,其本质是保存在堆内存中的对象,在栈内存中保存的是对象在堆内存中的引用地址。MDN中数组的描述如下:数组是一种类列表对象。数组对象有三个属性:length
、constructor
和protoType
。
length
:表示数组的长度,即数组所包含的元素个数constructor
:表示创建数组对象的构造函数,所有数组实例的constructor
都是Array
protoType
:表示数组实例的原型对象
数组对象的方法有很多,这里笔者根据自己的理解归纳了9大类,逐一针对各方法的语法及使用做了简要的介绍。
1.数组的创建
我们在使用js创建数组时,通常会通过数组直接量形式或构造函数Array
来创建,而ES6新增了两种创建数组的方法:Array.of
和Array.from
。
-
数组直接量形式创建
const arr = []; // 空数组 const arr1 = [1,2,3,4,5];
-
构造函数Array实例化
// 1.直接实例化 const arr = new Array(1,2,3,4,5); console.log(arr); // [1,2,3,4,5] // 2.实例化后赋值 const arr1 = new Array(); arr1[0] = 1; arr1[1] = 2; arr1[2] = 3; arr1[3] = 4; arr1[4] = 5; console.log(arr1); // [1,2,3,4,5]
使用Array
构造函数创建数组时,会存在一个比较奇怪的现象:构造函数在传入单个参数为整数时,所创建的数组为空数组,且数组的长度为该整数,若传入的单个参数为字符串时,所创建的数组才是包含该参数的长度为1的数组。
// 构造函数Array传入单个整数5时,此时创建的数组为[],且length为5
const arr = new Array(5);
console.log(arr) // [,,,,,] 这里是指5个空位的空数组
console.log(arr[0]) // undefined
// 构造函数Array传入单个参数为'5',此时创建的数组为['5'];
const arr1 = new Array('5');
console.log(arr1) // ['5']
为了避免Array
构造函数创建数组时产生的歧义,ES6新增了Array.of()
方法来创建数组
-
Array.of()方法创建
语法:
Array.of(...args)
,任意个参数按顺序成为创建数组的元素Array.of()
方法可以创建可变参数的数组实例,其不必考虑参数的数量或类型,所创建的数组元素即为所传入的参数。const arr = Array.of(5); console.log(arr) // [5] const arr1 = Array.of(1,2,3,4,5); console.log(arr1) // [1,2,3,4,5] const arr2 = Array.of(undefined); console.log(arr2) // [undefined]
ES6还新增了Array.from()
方法,可以从类数组或可迭代对象创建一个新的,浅拷贝的数组实例。
-
Array.from()方法创建
语法:
Array.from(arrayLike,mapFn,thisArg)
其中,
arrayLike
为类数组对象(拥有length属性和若干索引属性的对象)或可迭代对象(Set
,Map
,arguments
等),mapFn
为可选参数,创建的新数组的每个元素都会执行该回调函数,thisArg
可选参数,指定执行mapFn
时this
对象。// 1.String生成数组 const str = 'string'; const arr = Array.from(str); console.log(arr); // ['s','t','r','i','n','g'] // 2.Set生成数组 const set = new Set(['s','e','t']); const arr1 = Array.from(set); console.log(arr1); // ['s','e','t'] // 3.Map生成数组 const map = new Map([[1,'m'],[2,'a'],[3,'p']]); const arr2 = Array.from(map); const arr3 = Array.from(map.values()); const arr4 = Array.from(map.keys()); console.log(arr2); // [[1,'m'],[2,'a'],[3,'p']] console.log(arr3); // ['m','a','p'] console.log(arr4); // [1,2,3]; // 4.类数组对象arguments生成数组 function f() { return Array.from(arguments); } console.log(f(1,2,3,4,5)); // [1,2,3,4,5] // 5.可迭代对象生成数组 const numbers = { *[Symbol.iterator](){ yield 1; yield 2; yield 3; } } const arr5 = Array.from(numbers,value => value +1); console.log(arr5); // [2,3,4] // 6.指定回调函数 const arr6 = Array.from([1,2,3,4,5], item => item * 2); console.log(arr6); // [2,4,6,8,10] // 7.指定回调函数及执行回调函数的this对象 const helper = { diff:1, add(value) { return value + this.diff; } } const arr7 = Array.from([1,2,3,4,5],helper.add,helper); console.log(arr7); // [2,3,4,5,6] // 8.应用:利用Array.from和Set实现数组去重 function unique(arr) { return Array.from(new Set(arr)); } console.log(unique([1,1,2,2,3,3,4,4,5,5])); // [1,2,3,4,5]
2.内置迭代器
ES6中有3种类型的集合对象:Array
、Map
集合和Set
集合,这三种集合对象包括其它可迭代对象都内置了3种迭代器:
entries()
: 返回一个迭代器,其值为多个键值对keys()
:返回一个迭代器,其值为集合的所有键名values()
:返回一个迭代器,其值为集合的值
了解迭代器的概念就会知道,迭代器对象都有一个next()
方法,每次调用都返回一个结果对象。结果对象有两个属性:一个是value
,表示下一个将要返回的值;另一个是done
,一个布尔值,当没有可返回数据时返回true
。
-
entries()迭代器
调用
entries()
返回的迭代器每次调用next()
方法时,都返回一个结果对象,对象中的value
为一个数组,数组中的元素为集合中每个元素的键和值。若遍历的对象是数组,则第一个元素是数字类型的索引。若为Set
集合,则第一个元素和第二个元素都是值。若为Map
集合,第一个元素为键名。这里只介绍数组的示例。const colors = ['red','yellow','blue']; const iter = colors.entries(); console.log(iter.next()); // { value: [0,'red'], done: false} console.log(iter.next()); // { value: [1,'yellow'], done: false} console.log(iter.next()); // { value: [2,'blue'], done: false} console.log(iter.next()); // { value: undefined, done: true}
-
keys()迭代器
调用
keys()
返回的迭代器每次调用next()
方法,返回的结果对象中的value
为集合中存在的元素的键名。const colors = ['red','yellow','blue']; const iter1 = colors.keys(); console.log(iter1.next()); // { value: 0, done: false} console.log(iter1.next()); // { value: 1, done: false} console.log(iter1.next()); // { value: 2, done: false} console.log(iter1.next()); // { value: undefined, done: true}
-
values()迭代器
调用
values()
返回的迭代器每次调用next()
方法,返回的结果对象中的value
为集合中所存元素的值。const colors = ['red','yellow','blue']; const iter2 = colors.values(); console.log(iter2.next()); // { value: 0, done: false} console.log(iter2.next()); // { value: 1, done: false} console.log(iter2.next()); // { value: 2, done: false} console.log(iter2.next()); // { value: undefined, done: true}
另,Array
和Set
集合的默认迭代器为values()
,Map
集合的默认迭代器是entries()
,在 for...of
循环中,若没有显式指定则会使用默认迭代器。
3.数组的检测与数组元素的检测
数组的检测方法有Array.isArray
,数组元素的检测方法有every()
、some()
-
Array.isArray():用于检测一个对象是否是Array对象
语法:
Array.isArray(obj)
若传入的对象是
Array
,则返回true
,否则返回false
。console.log(Array.isArray([])); // true console.log(Array.isArray([1])); // true console.log(Array.isArray(new Array())); // true console.log(Array.isArray({})); // false console.log(Array.isArray(null)); // false console.log(Array.isArray(undefined)); // false
-
every():检测数组的所有元素是否都能通过指定函数的测试,返回一个
boolean
值语法:
arr.every(callback(element, index, array), thisArg)
其中,参数
element
为用于检测的当前值;index
可选,为当前值的索引;array
可选,为调用every
的当前数组;thisArg
为执行callback
时使用的this
对象[18,32,4,120,60].every(x => x > 10); // false [18,32,14,120,60].every(x => x > 10); // true
-
some():检测数组中是否有一个元素能通过指定函数的测试,返回一个
boolean
值语法:
arr.some(callback(element, index, array), thisArg)
[2,1,4,8,7].some(x => x > 10); // false [2,1,14,8,7].some(x => x > 10); // true
4.数组的重排序
数组的重排序有两种方法:sort()
和reverse()
-
sort():用原地算法对数组的元素进行排序,并返回数组。默认的排序顺序是将元素转换成字符串,然后根据逐个字符的
unicode
位点进行排序,该方法会改变原数组。语法:
arr.sort(compareFn(firstEl, secondEl))
其中,
compareFn
可选,用于指定按某种顺序进行排列的函数。若a和b是两个将要被比较的元素- 若`compareFn(a, b) < 0,那么a会排在b前面
- 若compareFn(a, b) = 0,那么a和b的相对位置不变
- 若compareFn(a, b) > 0,那么b会排在a前面
// 数组的升序、降序排序 const arr = [1,3,2,5,4]; console.log(arr.sort((a, b) => a - b)); // [1,2,3,4,5] console.log(arr.sort((a, b) => b - a)); // [5,4,3,2,1]
我们知道,常见的有八大排序算法:
稳定的:冒泡排序 O(n2)、插入排序 O(n2)、基数排序 O(logRB)、归并排序 O(nlogn)
不稳定的:选择排序 O(n2)、希尔排序 O(nlogn)、快速排序 O(nlogn)、堆排序 O(nlogn)
这里笔者比较好奇sort()
排序的底层究竟做了啥,它的算法时间复杂度又是多少呢?
不同浏览器的sort()
方法的实现不同,这里主要研究了V8引擎下的sort()
方法的源码,发现对于长度<=10
的数组使用的是插入排序,>10
的数组使用的是插入排序+快速排序。这里截取了源码的一段:
// Insertion sort is faster for short arrays.
if (to - from <= 10) {
InsertionSort(a, from, to);
return;
}
if (to - from > 1000) {
third_index = GetThirdIndex(a, from, to);
} else {
third_index = from + ((to - from) >> 1);
}
当然,这是chrome 70以前sort()
的算法实现,从chrome 70开始,V8团队更新了sort()
方法,使用了Timesort
算法,这里笔者未作深究。
-
reverse():将数组中的元素颠倒,并返回该数组。该方法会改变原数组
语法:
arr.reverse()
const arr = [1,2,3,4,5]; console.log(arr.reverse()); // [1,2,3,4,5]
5.数组元素的操作
针对数组的元素主要有数组元素的添加,删除和修改等操作,方法包括:
添加:push()
、unshift()
删除:pop()
、shift()
修改:fill()
、copyWithin()
-
push():将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
语法:
arr.push(element1,...,elementN)
push
方法具有通用性,可以和call()
或apply()
一起使用,应用在类数组对象上。// 添加元素到数组 const arr = [1,2,3,4,5]; const total = arr.push(6,7,8,9,10); console.log(arr); // [1,2,3,4,5,6,7,8,9,10] console.log(total); // 10 total为数组的新长度值 // 利用apply()合并两个数组 const arr1 = [1,2,3,4,5]; const arr2 = [6,7,8,9,10]; Array.prototype.push.apply(arr1,arr2); console.log(arr1); // [1,2,3,4,5,6,7,8,9,10] // 类数组对象的应用 const obj = { length: 0, addElem: function(elem){ [].push.call(this, elem) } }; obj.addElem({}); obj.addElem({}); console.log(obj); // { 0: {}, 1: {}, length: 2, addElem: ƒ}
-
pop():删除数组中的最后一个元素,并返回该元素。当数组为空时,返回
undefined
。语法:
arr.pop()
pop
方法也具有通用性,可以和call()
或apply()
一起使用,应用在类数组对象上。// 删除数组的最后一个元素 const arr = [1,2,3,4,5]; const elem = arr.pop(); console.log(arr); // [1,2,3,4] console.log(elem); // 5
-
unshift():将一个或多个元素添加到数组的开头,并返回该数组的新长度。
语法:
arr.unshift(element1,...,elementN)
unshift
同样可以通过call()
或apply()
方法作用于类数组对象上。// 添加元素到数组 const arr = [1,2,3,4,5]; const total = arr.unshift(6,7,8,9,10); console.log(arr); // [6,7,8,9,10,1,2,3,4,5] console.log(total); // 10 // 合并数组 const arr1 = [1,2,3,4,5]; const arr2 = [6,7,8,9,10]; Array.prototype.unshift.apply(arr1,arr2); console.log(arr1); // [6,7,8,9,10,1,2,3,4,5];
-
shift():删除数组中的第一个元素,并返回该元素。当数组为空时,返回
undefined
语法:
arr.shift()
shift
也同样可以通过call()
或apply()
方法作用于类数组对象上。const arr = [1,2,3,4,5]; const elem = arr.shift(); console.log(arr); // [2,3,4,5] console.log(elem); // 1
我们知道,栈的特点是后进先出,队列的特点是后进后出。因此,我们可以利用数组的push()
、pop()
实现栈结构,push()
、shift()
实现队列结构。
// 栈结构 后进先出
const Stack = function() {
this.data = [];
this.insert = push;
this.delete = pop;
this.clear = clear;
this.length = length;
}
const push = function(element) {
this.data.push(element);
return this.data.length;
}
const pop = function() {
return this.data.pop();
}
const clear = function() {
this.data.length = 0;
}
const length = function() {
return this.data.length;
}
const s = new Stack();
s.insert('first');
s.insert('second');
console.log(s.data); // ['first','second']
s.delete();
console.log(s.data); // ['first']
s.clear();
console.log(s.data); // []
// 队列结构 后进后出
const Queue = function() {
this.data = [];
this.insert = push;
this.delete = shift;
this.clear = clear;
this.length = length;
}
const push = function(element) {
return this.data.push(element);
}
const shift = function() {
return this.data.shift();
}
const clear = function() {
this.data.length = 0;
}
const length = function() {
return this.data.length;
}
const q = new Queue();
q.insert('first');
q.insert('second');
console.log(q.data); // ['first','second']
q.delete();
console.log(q.data); // ['second']
q.clear();
console.log(q.data); // []
再进一步思考,我们可以通过栈来实现DFS
:首先将根节点入栈,然后出栈后检查此节点是否有子节点,有子节点就逆序推入栈中,再出栈,子节点逆序入栈,依次循环;通过队列实现BFS
:首先将根节点入队列,然后出队列检查此节点是否有子节点,有子节点就顺序入队列,再出队列子,节点顺序入队列,依次循环
// 栈实现DFS
function deepTraversal(node) {
const nodelist = [];
if(node) {
const stack = [];
stack.push(node); // 根节点入栈
while(stack.length !== 0) {
const childItem = stack.pop(); // 出栈(数组的最后一个元素)
nodelist.push(childItem);
const childrenList = childItem.children;// 获取出栈的节点的子节点,保证子节点先出栈
for(let i = childrenList.length-1;i >= 0;i--) {
stack.push(childrenList[i]); // 子节点逆序入栈
}
}
return nodelist;
}
}
// 队列实现BFS
function wideTraversal(node) {
const nodelist = [];
if(node) {
const queue = [];
queue.push(node); // 根节点入队列
while(queue.length !== 0) {
const childItem = queue.shift(); // 出队列(数组第一个元素)
nodelist.push(childItem);
const childrenList = childItem.children; // 获取出队列的节点的子节点
for(let i = 0;i <= childrenList.length -1;i++) {
queue.push(childrenList[i]); // 子节点顺序入队列,保证父节点先出队列
}
}
return nodelist;
}
}
-
fill():用一个固定值填充一 个数组从起始索引到终止索引(即替换数组的指定位置的元素),不包括终止索引。返回修改后的数组。
语法:
arr.fill(value, start, end)
value
为用来填充数组元素的值。start
可选,为起始索引,默认值为0,当start
为负数时,开始索引为length+start
。end
可选,为终止索引,默认值为length
,当end
为负数时,终止索引为length+end
。fill
为通用方法,类数组对象可通过call()
或apply()
来调用fill
。const arr1 = [1,2,3,4,5].fill(4); const arr2 = [1,2,3,4,5].fill(4,1,2); const arr3 = [1,2,3,4,5].fill(4,4,4); const arr4 = [1,2,3,4,5].fill(4,-2); const arr5 = [1,2,3,4,5].fill(4,-4,-1); const arr6 = Array(5).fill(4); const arr7 = [].fill.call({length: 5},4); console.log(arr1); // [4,4,4,4,4] console.log(arr2); // [1,4,3,4,5] console.log(arr3); // [1,2,3,4,5] console.log(arr4); // [1,2,3,4,4] console.log(arr5); // [1,4,4,4,5] console.log(arr6); // [4,4,4,4,4] console.log(arr7); // {0: 4, 1: 4, 2: 4, 3: 4, 4: 4, length: 5}
-
copyWithin():浅拷贝数组的一部分到同数组中的另一个位置,并返回它,不会改变原数组的长度。返回改变后的数组。
语法:
arr.copyWithin(target, start, end)
target
为复制序列到该索引的位置。当target
为负数时,复制到的位置索引为length+target
,当target
大于length
时,不发生拷贝,当target
在start
之后,复制的序列将被修改以符合arr.length
。start
为开始复制元素的起始索引。默认为0,当start
为负数时,开始的索引为length+start
。end
为开始复制元素的终止索引,不包括该位置的元素。默认为arr.length
,当end
为负数时,终止的索引为length+end
。const arr1 = [1,2,3,4,5].copyWithin(-2); const arr2 = [1,2,3,4,5].copyWithin(0,3); const arr3 = [1,2,3,4,5].copyWithin(0,3,4); const arr4 = [1,2,3,4,5].copyWithin(3,1,4); const arr5 = [1,2,3,4,5].copyWithin(1,-3,-1); const arr6 = [1,2,3,4,5].copyWithin(1,-3,-4); const arr7 = [].copyWithin.call({length: 5, 3: 1},0,3); console.log(arr1); // [1,2,3,1,2] console.log(arr2); // [4,5,3,4,5] console.log(arr3); // [4,2,3,4,5] console.log(arr4); // [1,2,3,2,3] console.log(arr5); // [1,3,4,4,5] console.log(arr6); // [1,2,3,4,5] console.log(arr7); // {0: 1, 3: 1, length: 5}
6.数组元素的查找和过滤
js中数组针对数组某个元素的查找,主要有indexOf()
、lastIndexOf()
以及ES6新增的includes()
、find()
、findIndex()
方法;filter()
可以针对数组查找符合某个条件的所有元素。
-
indexOf():返回指定元素在数组中的第一个的索引,若不存在,则返回-1。从数组的前面向后查找
语法:
arr.indexOf(searchElment , fromIndex:option)
其中,
searchElement
为要查找的元素,fromIndex
可选,为开始查找的位置,fromIndex
为负数时,表示从倒数第|fromIndex|
个元素开始查找(也可以顺数fromIndex+arr.length
),查找顺序仍然是从前向后。// 查找元素在数组中的位置 const arr = [1,2,3,4,5]; console.log(arr.indexOf(2)); // 1 console.log(arr.indexOf(6)); // -1 console.log(arr.indexOf(3,3)); // -1 console.log(arr.indexOf(2,-1)); // -1 console.log(arr.indexOf(4,-3)); // 3
-
lastIndexOf():返回指定元素在数组中的最后一个的索引,若不存在,则返回-1。从数组的后面向前查找。
语法:
arr.lastIndexOf(searchElement, fromIndex)
searchElement
为要查找的元素,fromIndex
可选,为从此位置开始逆序查找,当fromIndex
为负数时,表示从数组倒数第|fromIndex|
个元素开始查找(也可以顺数fromIndex+arr.length
),查找顺序仍然是从后往前。const arr = [1,2,3,4,5,3,1]; console.log(arr.lastIndexOf(3)); // 5 console.log(arr.lastIndexOf(6)); // -1 console.log(arr.lastIndexOf(3,3)); // 2 console.log(arr.lastIndexOf(3,-3)); // 2 console.log(arr.lastIndexOf(3,-1)); // 5
-
includes():查找一个数组是否包含指定元素,若包含则返回
true
,否则返回false
。语法:
arr.incluedes(searchElement, fromIndex)
searchElment
为要查找的元素,fromIndex
可选,从fromIndex
索引处开始查找,当fromIndex
为负数时,则从arr.length+fromIndex
的索引开始查找(或倒数第|fromIndex|
个元素开始),若计算出来的索引仍然小于0,则整个数组都会搜索,查找顺序仍然时从前向后。arr = [1,2,3,4,5]; console.log(arr.includes(1)); // true console.log(arr.includes(3,2)); // true console.log(arr.includes(3,-2)); // false console.log(arr.includes(5,-10)); // true
-
find():返回数组中满足提供测试的测试函数的第一个元素的值,若没有则返回
undefined
。语法:
arr.find(callback(element, index, array),thisArg)
其中,
callback
为在数组每一项上执行的函数,有3个参数:element为当前遍历的元素;index
可选,为当前遍历的索引;array
也是可选,为调用findIndex
的数组。thisArg
可选,为执行回调时的this
对象。// 用对象的属性查找数组里的某个对象 const inventory = [ {name: 'apples', quantity: 2}, {name: 'bananas', quantity: 0}, {name: 'cherries', quantity: 5} ] console.log(inventory.find(fruit => fruit.name === 'cherries')); // {name: 'cherres', quantity: 5} // 寻找数组中的首个质数 function isPrime(element) { let start = 2; while(start <= Math.sqrt(element)) { if(element % start++ < 1) { return false; } } return element > 1; } console.log([4,6,8,12].find(isPrime)); // undefined console.log([4,5,8,12].find(isPrime)); // 5 console.log([3,4,5,7,8,12].find(isPrime)); // 3
-
findIndex():返回数组中满足提供的测试函数的第一个元素的索引。若没有则返回-1。
语法:
arr.findIndex(callback(element, index,array),thisArg)
callback
为在数组每一项上执行的函数,有3个参数:element
为当前遍历的元素;index
可选,为当前遍历的索引;array
可选,为调用findIndex
的数组。thisArg
可选,为执行callback
时的this
对象。// 查找数组中首个质数元素的索引 function isPrime(element) { let start = 2; while(start <= Math.sqrt(element)) { if(element % start++ < 1) { return false; } } return element > 1; } console.log([4,6,8,12].findIndex(isPrime)); // -1 console.log([4,6,7,12].findIndex(isPrime)); // 2 console.log([4,5,6,7,12].findIndex(isPrime)); // 1
-
filter():创建一个新数组,其包含通过所提供函数的测试的所有元素。
语法:
const newArray = arr.filter(callback(element,index,array),thisArg)
其中,
callback
为测试数组的每个元素的函数。返回true
表示该元素通过测试,保留该元素,false
则不保留。有3个参数:element
为当前元素;index
为当前索引;array
为调用filter
的数组。thisArg
为执行callback
时的this
对象。// 筛选满足条件的元素 const arr = [12,5,10,24,48]; const arr1 = arr.filter(element => element > 10); const arr2 = arr.filter(element => element <= 10); console.log(arr1); // [12,24,48] console.log(arr2); // [5,10] // 过滤json中的无效条目 const arr = [ { id: 15 }, { id: -1 }, { id: 0 }, { id: 3 }, { id: 12.2 }, { }, { id: null}, { id: NaN }, { id: 'undefiend' } ]; let invalidEntries = 0; function filterByID(item) { if(item.id !== undefined && typeof(item.id) === 'number' && !isNaN(item.id) && item.id !== 0) { return true; } invalidEntries++; return false; } const newArr = arr.filter(filterByID); console.log(newArr); // [{id: 15},{id: -1},{id: 3},{id: 12.2}] console.log(invalidEntries); // 5
7.数组的合并与切割
js提供了数组的合并和分割的方法,可以针对一个或多个数组进行操作。
合并的方法有:concat()
、join()
切割的方法有:slice()
、splice()
-
concat():合并两个或多个数组。此方法不改变原数组,返回一个新数组。
语法:
const newArray = oldArray.concat(Array1,...,ArrayN)
若
concat
的参数为空,则返回调用此方法的现有数组的一个浅拷贝(即oldArray
的浅拷贝)。// 合并多个数组 const arr1 = [1,2,3]; const arr2 = [4,5,6]; const arr3 = ['a','b','c']; const arr = arr1.concat(arr2,arr3); console.log(arr); // [1,2,3,4,5,6,'a','b','c'] // 返回原数组的浅拷贝 const oldArray = [ { person: { name: 'zhang', age: 28 }, city: 'wuhan' }, { person: { name: 'wang', age: 29 }, city: 'shenzheng' } ] const newArray = oldArray.concat(); oldArray[0].person.age = 27; console.log(newArray); // [{person: {name:'zhang',age:27},city:'武汉'},{person:{name:'wang',age:29},city:'深圳'}]
-
join():按指定分隔符合并数组或类数组对象的所有元素,连接成一个字符串并返回。若数组只有一个项目,那么返回时不使用分隔符。
语法:
arr.join(separator)
separator
可选,为指定连接数组每个元素的分隔符。若缺省该值,数组元素以逗号(,)分隔,若separator
为空字符串(''),则数组元素之间没有任何字符。如果一个元素为undifined
或null
,它会被转换为空字符串。// 连接数组 const arr = ['Winter',undefined,'Is',null,'Comming']; const str = arr.join(); const str1 = arr.join(','); const str2 = arr.join('_'); const str3 = arr.join(''); console.log(str); // Winter,,Is,,Comming console.log(str1); // Winter,,Is,,Comming console.log(str2); // Winter__Is__Comming console.log(str3); // WinterIsComming // 连接类数组对象 function f(a,b,c) { const s = Array.prototype.join.call(arguments); console.log(s); } f(1,'cold',true); // 1,cold,true
-
slice():返回一个新的数组对象,这个对象是由
begin
和end
决定的原数组的浅拷贝(包含begin
,不包含end
)。原数组不改变。语法:
arr.slice(begin, end)
begin
可选,指从该索引开始切割原数组元素。若省略begin
,则slice
从索引0开始,若begin
为负数,则表示从倒数第几个元素开始切割(或顺数索引arr.length+begin
开始)。end
可选,指在该索引处结束切割原数组元素。若end
省略,则slice
提取到原数组的末尾,若end
为负数,则表示在原数组的倒数第几个元素结束(或顺数索引arr.length+end
结束),若end
大于数组的长度,slice
也会提取到原数组的末尾。// 返回现有数组的一部分 const arr = ['Banana','Orange','Lemon','Apple','Mango']; const fruit = arr.slice(); const fruit1 = arr.slice(1,3); const fruit2 = arr.slice(-1,3); const fruit3 = arr.slice(3,-1); const fruit4 = arr.slice(-1,-3); const fruit5 = arr.slice(-3,-1); console.log(fruit); // ['Banana','Orange','Lemon','Apple','Mango'] console.log(fruit1); // ['Orange','Lemon'] console.log(fruit2); // [] console.log(fruit3); // ['Apple'] console.log(fruit4); // [] console.log(fruit5); // ['Lemon','Apple'] // 类数组对象调用 function list() { return [].slice.call(arguments); } list(1,2,3,4,5); // [1,2,3,4,5]
-
splice():通过删除或替换现有数组的元素或原地添加新的元素来修改数组,并以数组的形式返回被修改的内容。此方法会改变原数组。返回值为被删除的元素组成的数组。
语法:
arr.splice(start, deleteCount, item1,item2,...)
其中,
start
为指定修改的开始位置(包括该位置的元素),若超过了数组的长度,则从数组末尾开始添加内容;如果是负值,则表示从数组倒数第几个元素开始,若负数的绝对值大于数组的长度,则表示开始的位置为第0位。deleteCount
可选,表示要移除的数组元素的个数。当deleteCount
大于start
之后的元素总数,则start
后面的元素都被删除,若deleteCount
省略或大于等于length-start
,那么start
后面的元素都被删除,当deleteCount
为0或负数,则不移除元素,这种情况至少应该添加一个新元素。item1,item2,...
表示要添加进数组的元素,从start
位置开始,如果不指定,则splice
只删除元素。const arr1, arr2, arr3, arr4, arr5 = [1,2,3,4,5]; console.log(arr1.splice(2)); // [3,4,5] console.log(arr1); // [1,2] console.log(arr2.splice(1,5)); // [2,3,4,5] console.log(arr2); // [1] console.log(arr3.splice(-1,3,4,5)); // [5] console.log(arr3); // [1,2,3,4,4,5] console.log(arr4.splice(-1,-1,4,5)); // [] console.log(arr4); // [1,2,3,4,5,4,5] console.log(arr5.splice(6,2,4,5)); // [] console.log(arr5); // [1,2,3,4,5,4,5]
8.数组的遍历
js中遍历数组的方法有:forEach()
、map()
、reduce()
、reduceRight()
。
-
forEach():遍历数组的每个元素,执行一次给定的函数。
forEach
除非抛出异常,否则无法跳出循环,且不对未初始化的值进行任何操作。返回值为undefined
。语法:
arr.forEach(callback(currentValue, index, array), thisArg)
其中,
callback
为遍历每个元素执行的函数,该函数有三个参数:currentValue
为当前遍历的元素;index
可选,为当前遍历的索引;array
可选,为执行forEach
的数组。thisArg
可选,为执行回调函数时的this
对象。// 遍历稀疏数组 const arrSparse = [1,3,,7]; let numCallbackRuns = 0; arrSparse.forEach(function(element) { console.log(element);numCallbackRuns++; }); console.log('numCallbackRuns: ',numCallbackRuns); // 1 // 3 // 7 // numCallbackRuns: 3
-
map():创建一个新数组,其结果是遍历数组的每个元素,执行指定的函数后的返回的值。不会改变原数组。
语法:
const newArray = arr.map(callback(currentValue, index, array),thisArg)
callback
为生成新数组元素的函数,该函数有三个参数:currentValue
为当前遍历的元素;index
可选,为当前遍历的索引;array
可选,为调用map
的数组。thisArg
为执行callback
函数的this
对象。// 求数组中每个元素的平方 const arr = [1,2,3,4,5]; const newArr = arr.map(item => Math.pow(item,2)); console.log(newArr); // [1,4,9,16,25] // map格式化数组中的对象 const arr = [ {key: 1, value: 10}, {key: 2, value: 20}, {key: 3, value: 30} ]; const newArr = arr.map(function(obj) { const rObj = {}; rObj[obj.key] = obj.value; return rObj; }) console.log(newArr); // [{1: 10},{2: 20},{3: 30}]
-
reduce():对数组中的每个元素执行一个指定的
reducer
函数(升序执行),将其结果汇总为单个返回值。语法:
arr.reduce(callback(accumulator, currentValue, index, array), initialValue)
其中,
callback
为遍历每个元素执行的函数,有四个参数:accumulator
为累计器累计回调的返回值,即上一次调用时返回的累积值。currentValue
为当前遍历的元素;index
可选,为当前遍历的索引;array
可选,为调用reduce()
的数组。initialValue
可选,作为第一次调用callback
函数时的第一个参数的值,若没有提供,则将使用数组的第一个元素,且遍历跳过该元素。返回值为函数累积处理的结果。// 累加数组里的所有值 const arr = [1,2,3,4,5]; const total = arr.reduce((acc,cur) => acc + cur, 0); console.log(total); // 15 // 将二维数组转化为一维数组 const arr1 = [[1,2],[3,4],[5,6]]; const flattened = arr1.reduce((acc, cur) => acc.concat(cur), []); console.log(flattened); // [1,2,3,4,5,6] // 计算数组中每个元素出现的次数 const names = ['Alice', 'Bob', 'Tiff','Bruce', 'Alice']; const countNames = names.reduce(function(allNames, name){ if(name in allNames){ allNames[name]++; } else { allNames[name] = 1; } return allNames },{}); console.log(countNames); // {'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1} // 数组去重 const arr2 = [1,3,5,7,5,3,1]; const newArr = arr2.reduce((acc, cur) => { if(!acc.includes(cur)) { acc.push(cur) } return acc; }, []); console.log(newArr); // [1,3,5,6]
-
reduceRight():接受一个函数作为累加器,从右到左遍历数组的元素,将其结果汇总为单个值。
语法:
arr.reduceRight(callback(accumulator, currentValue, index, array), initialValue)
其中,
callback
为遍历每个元素执行的回调函数,有四个参数:accumulator
为累加器累计回调的返回值,即上一次调用回调函数的返回值。currentValue
为当前遍历的元素;index
可选,为当前遍历的索引;array
为调用callback
的数组。initialValue
可选,为首次调用callback
函数时,累加器accumulator
的值。若没提供初始值,则将使用数组的最后一个元素作为初始值,且遍历跳过该元素。返回值为累计执行的返回值。// 计算数组元素的和 const arr = [0,1,2,3,4]; const sum = arr.reduceRight((acc, cur) => acc + cur, 0); console.log(sum); // 10 // 扁平化一个二维数组 const arr2 = [[0,1],[2,3],[4,5]]; const flattened = arr2.reduceRight((acc, cur) => acc.concat(cur)); console.log(flattened); // [4,5,2,3,0,1] // reduce与reduceRight的区别 const arr3 = ['1','2','3','4','5']; const arr4 = arr3.reduce((acc, cur) => acc + cur); const arr5 = arr3.reduceRight((acc, cur) => acc + cur); console.log(arr4); // '12345' console.log(arr5); // '54321'
9.数组扁平化
js中,可能存在这样中情况,比如数组中嵌套了数组(即数组的元素仍是数组),当我们想将所有的元素展开组成一个一维数组,即数组扁平化。那么此时就要用到flat()
或flatMap()
方法。
-
flat():按照一个指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
语法:
const newArr = arr,flat(depth)
depth
可选,指定要提取嵌套数组的嵌套深度,默认值为1。返回值为扁平化后的新数组。// 扁平化嵌套数组 const arr1 = [1,2,[3,4]]; const arr2 = arr1.flat(); console.log(arr2); // [1,2,3,4] const arr3 = [1,2,[3,4,[5,6]]]; const arr4 = arr3.flat(); const arr5 = arr3.flat(2); console.log(arr4); // [1,2,3,4,[5,6]] console.log(arr5); // [1,2,3,4,5,6] // 使用Infinity可展开任意深度的嵌套数组 const arr6 = [1,2,[3,4,[5,6,[7,8,[9,10]]]]]; const arr7 = arr6.flat(Infinity); console.log(arr7); // [1,2,3,4,5,6,7,8,9,10] // 扁平化会移除数组空项 const arr8 = [1,2,,4,5]; const arr9 = arr8.flat(); console.log(arr9); // [1,2,4,5]
当然,我们也可以通过递归 +
isArray
来扁平化嵌套数组:const arr = [1,2,3,[1,2,3,[1,2,3,[1,2,3]]]]; function flatten(arr) { return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur)?flatten(cur):cur),[]) } console.log(flatten(arr)); // [1,2,3,1,2,3,1,2,3,1,2,3]
-
flatMap():首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与
map
连着深度值为1的flat
几乎相同,但效率更高。语法:
const newArr = arr.flatMap(callbakc(cuttrentValue, index, array),thisArg)
其中,
callback
为映射每个元素的映射函数,有三个参数:currentValue
为当前处理的元素;index
可选,为当前元素的索引;array
可选,为调用flatMap
的数组。thisArg
可选,为执行callback
函数时的this
对象。// map()与flatMap()的区别 const arr = [1,2,3,4,5]; const arr1 = arr.map(x => [x * 2]); const arr2 = arr.flatMap(x => [x * 2]); const arr3 = arr.flatMap(x => [[x * 2]]); console.log(arr1); // [[2],[4],[6],[8],[10]] console.log(arr2); // [2,4,6,8,10] console.log(arr3); // [[2],[4],[6],[8],[10]]
【参考文献】:
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!