集合对象转数组_javascript之Array对象回顾

最近在回顾javascript的知识点,花了几天的空余时间重新学习了一下MDN中的Array对象,对Array对象有了新的认识和理解,所以决定将这些温故知新的内容大致的整理一下并记录下来,也在于鞭策自己继续学习。 d9159cc65cc3d29fa309406b52ee9fec.png

首先,我们知道Array是引用数据类型,其本质是保存在堆内存中的对象,在栈内存中保存的是对象在堆内存中的引用地址。MDN中数组的描述如下:数组是一种类列表对象。数组对象有三个属性:length、constructor和protoType。

  • length:表示数组的长度,即数组所包含的元素个数

  • constructor:表示创建数组对象的构造函数,所有数组实例的constructor都是Array

  • protoType:表示数组实例的原型对象

数组对象的方法有很多,这里笔者根据自己的理解归纳了9大类,逐一针对各方法的语法及使用做了简要的介绍。

d9159cc65cc3d29fa309406b52ee9fec.png 01 数组的创建

我们在使用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等); 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]
02 内置迭代器

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: 'red', done: false}
console.log(iter2.next());  // { value: 'yellow', done: false}
console.log(iter2.next());  // { value: 'blue', done: false}
console.log(iter2.next());  // { value: undefined, done: true}
另,Array和Set集合的默认迭代器为values(),Map集合的默认迭代器是entries(),在for...of循环中,若没有显式指定则会使用默认迭代器。 03 数组的检测与数组元素的检测

数组的检测方法有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
04 数组的重排序 数组的重排序有两种方法:sort()和reverse()
  • sort():用原地算法对数组的元素进行排序,并返回数组。默认的排序顺序是根据逐个字符的unicode位点进行排序。

    语法:arr.sort(compareFn(firstEl, secondEl))

其中,compareFn可选,用于指定按某种顺序进行排列的函数。若a和b是两个将要被比较的元素: 1. 若compareFn(a, b) < 0,那么a会排在b前面。 2. 若compareFn(a, b) = 0,那么a和b的相对位置不变。 3. 若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);
}

当然,这是chrome70以前sort()的算法实现,从chrome70开始,V8团队更新了sort()方法,使用了Time-sort算法,这里笔者未作深究。

  • reverse():将数组中的元素颠倒,并返回该数组。该方法会改变原数组。

    语法:arr.reverse()

const arr = [1,2,3,4,5];
console.log(arr.reverse()); // [1,2,3,4,5]
05数组元素的操作

针对数组的元素主要有数组元素的添加,删除和修改等操作,方法包括:

添加: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():删除数组中的最后一个元素,并返回该元素。当数组为空时,返回undifined。

    语法: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。
// 队列实现BFS
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}
06 数组元素的查找和过滤

js中数组针对数组某个元素的查找,有indexOf()、lastIndexOf()以及ES6新增的find()、findIndex()方法等;filter()可以针对数组查找符合某个条件的所有元素。

  • indexOf():返回指定元素在数组中的第一个的索引,若不存在,则返回-1。从数组的前面向后查找。

    语法:arr.indexOf(searchElment , fromIndex)

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|个元素开始查找(也可以顺数from Index + 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++       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++       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():创建一个新数组,其包含通过所提供函数的测试的所有元素

    语法:

    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
07 数组的合并与切割

js提供了数组的合并和分割的方法,可以针对一个或多个数组进行操作。合并的方法有:concat()、join();切割的方法有:slice()、splice()。

  • concat():合并两个或多个数组。此方法不改变原数组,返回一个新数组。

    语法:newArr = oldArr.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]
08数组的遍历

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():创建一个新数组,其结果是遍历数组的每个元素,执行指定的函数后的返回的值。不会改变原数组。

  • 语法:

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'
09数组扁平化 js中,可能存在这样中情况,比如数组中嵌套了数组(即数组的元素仍是数组),当我们想将所有的元素展开组成一个一维数组,即数组扁平化。那么此时就要用到flat()flatMap()方法。
  • flat():按照一个指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组。 

    语法: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几乎相同,但效率更高。

    语法:

    arr.flatMap(callback(currentValue,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]]
【参考文献】:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array

63cad393e87e971b56fd061cad91d809.gif 2d23c014d1a68605063c6125ed8d5094.gif点击上方蓝字 关注我吧你的每个赞和在看,我都喜欢! dddb334149ab27f65f053a8f1b02732e.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值