js 数组方法大全总结(自己整理)

1.构造数组

字面量方式:
let a = []; //最常用的,简洁方便
a.length = 3; // [undefined × 3]

使用构造器: 
let b = new Array(); //[]
let c = Array(3); //[undefined × 3]
let d = Array(1,2,3); //[1,2,3]

数组长度最大为Math.pow(2,32)-1 ,即 4294967295 位

两种方式性能PK?
new Array()  <  []  

2.Array.of

Array.of用于将参数依次转化为数组中的一项,然后返回这个新数组,而不管这个参数是数字还是其它。它基本上与Array构造器功能一致,唯一的区别就在单个数字参数的处理上。如下:
Array.of(3); // [3]
Array(3); // [undefined × 3]

参数为多个,或单个参数不是数字时,Array.of 与 Array构造器等同:
Array.of(1, 2); // [1, 2]
Array(1, 2); // [1, 2]
Array.of('8'); // ["8"]
Array('8'); // ["8"]

即使其他版本浏览器不支持也不必担心,由于Array.of与Array构造器的这种高度相似性,实现一个polyfill十分简单。如下:
if (!Array.of){
  Array.of = function(){
    return Array.prototype.slice.call(arguments);
    //return [].slice.call(arguments);
  };
}

3.Array.from 语法:Array.from(arrayLike, processingFn, thisArg)

Array.from的设计初衷是快速便捷的基于其他对象创建新数组,准确来说就是从一个类似数组的可迭代对象创建一个新的数组实例    

从语法上看,Array.from拥有3个形参,第一个为类似数组的对象,必选。第二个为加工函数,新生成的数组会经过该函数的加工再返回。第三个为this作用域,表示加工函数执行时this的值,后两个参数都是可选的。

var obj = {0: 'a', 1: 'b', 2:'c', length: 3};
Array.from(obj, (value) => value.repeat(2), obj);  //["aa","bb","cc"]

生成一个从0到指定数字的新数组,Array.from就可以轻易的做到:
Array.from({length: 10}, (v, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

4.Array.isArray 用来判断一个变量是否数组类型

Array.isArray([]); // true
Array.isArray({0: 'a', length: 1}); // false

除了这个方法还有Object.prototype.toString 判断是否为数组

5.方法 unshift、shift、push、pop、splice、reverse、sort,以及两个ES6新增的方法flat、flatMap、copyWithinfill, 以上方法会改变数组本身。

unshift 语法:arr.unshift(element1, …, elementN)

unshift() 方法用于在数组开始处插入一些元素(就像是栈底插入),并返回数组新的长度。

let array = ["red", "green", "blue"];
let length = array.unshift("yellow");
console.log(array); // ["yellow", "red", "green", "blue"]
console.log(length); // 4

shift 语法:arr.shift()

shift()方法删除数组的第一个元素,并返回这个元素。

let array = [1,2,3,4,5];
let item = array.shift();
console.log(array); // [2,3,4,5]
console.log(item); // 1

push 语法:arr.push(element1, …, elementN)

push()方法添加一个或者多个元素到数组末尾,并且返回数组新的长度。

let array = ["football", "basketball", "volleyball"];
let i = array.push("Table tennis");
console.log(array); // ["football", "basketball", "volleyball", "Table tennis"]
console.log(i); // 4

pop 语法:arr.pop()

pop() 方法删除一个数组中的最后的一个元素,并且返回这个元素。

let array = ["cat", "dog", "cow", "chicken", "mouse"];
let item = array.pop();
console.log(array); // ["cat", "dog", "cow", "chicken"]
console.log(item); // mouse

splice 语法:arr.splice(start,deleteCount,[item1, item2, …])

splice()方法用新元素替换旧元素的方式来修改数组。

start 为开始位置,如果超过了数组长度,则从数组末尾开始添加内容;如果是负值,则其指定的索引位置等同于 length+start (length为数组的长度),表示从数组末尾开始的第 -start 位。
deleteCount 指定要删除的元素个数,若等于0,则不删除。这种情况下,至少应该添加一位新元素,若大于start之后的元素总和,则start及之后的元素都将被删除。
itemN 指定新增的元素,如果缺省,则该方法只删除数组元素。
返回值 由原数组中被删除元素组成的数组,如果没有删除,则返回一个空数组。

let array = ["apple","boy"];
let splices = array.splice(-3,1,"cat");
console.log(array); // ["cat", "boy"]
console.log(splices); // ["apple"], 可见即使-start超出数组长度,数组默认从首位开始删除
------
let array = ["apple","boy"];
let splices = array.splice(3,3,"cat");
console.log(array); // ['apple', 'boy', 'cat']
console.log(splices); // []
可见当start超出数组长度,数组没变化,默认从末尾该添加啥添加啥

reverse 语法:arr.reverse()

reverse()方法颠倒数组中元素的位置,第一个会成为最后一个,最后一个会成为第一个,该方法返回对数组的引用。

let array = [1,2,3,4,5];
let array2 = array.reverse();
console.log(array); // [5,4,3,2,1]
console.log(array2===array); // true

sort 语法:arr.sort([comparefn])

sort()方法对数组元素进行排序,并返回这个数组。

comparefn是可选的,如果省略,数组元素将按照各自转换为字符串的Unicode(万国码)位点顺序排序,例如”Boy”将排到”apple”之前。当对数字排序的时候,25将会排到8之前,因为转换为字符串后,”25”将比”8”靠前。例如:

let array = ["Boy","apple","Cat","dog"];
let array2 = array.sort();
console.log(array); // ["Boy", "Cat", "apple", "dog"]
console.log(array2 == array); // true
let array3 = array.sort((a, b) => a.localeCompare(b));
console.log(array3)//["apple", "Boy", "Cat", "dog"]

let array = [10, 1, 3, 20];
let array1 = array.sort();
console.log(array1); // [1, 10, 20, 3]
let array2 = array.sort((a, b) => a-b);
console.log(array2)//[1, 3, 10, 20]

实际上,ECMAscript规范中并未规定具体的sort算法,这就势必导致各个浏览器不尽相同的sort算法,请看sort方法在Chrome浏览器下表现:

let array = [{ n: "a", v: 1 }, { n: "b", v: 1 }, { n: "c", v: 1 }, { n: "d", v: 1 }, { n: "e", v: 1 }, { n: "f", v: 1 }, { n: "g", v: 1 }, { n: "h", v: 1 }, { n: "i", v: 1 }, { n: "j", v: 1 }, { n: "k", v: 1 }, ];
array.sort((a, b) => return a.v - b.v);
for (let i = 0,len = array.length; i < len; i++) {
    console.log(array[i].n);
}

由于v值相等,array数组排序前后应该不变,然而Chrome却表现异常,而其他浏览器(如IE 或 Firefox) 表现正常。
这是因为v8引擎为了高效排序(采用了不稳定排序)。即数组长度超过10条时,会调用另一种排序方法(快速排序);而10条及以下采用的是插入排序,此时结果将是稳定的。

解决办法:
// 由于快速排序会打乱值相同的元素的默认排序,因此我们需要先标记元素的默认位置
array.forEach((v, k) =>v.__index = k);
// 由于__index标记了初始顺序,这样的返回才保证了值相同元素的顺序不变,进而使得排序稳定
array.sort((a, b) => return a.v - b.v || a.__index - b.__index);

值得注意的是:
各浏览器的针对sort方法内部算法实现不尽相同,排序函数尽量只返回-1、0、1三种不同的值,不要尝试返回true或false等其它数值,因为可能导致不可靠的排序结果。

let array = [7, 6, 5, 4, 3, 2, 1, 0, 10, 9, 8];
let comparefn = (x, y) => x > y;
array.sort(comparefn);

flat 语法:arr.flat(depth)

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。

var arr1 = [1, 2, [3, 4]];
arr1.flat(); 
// [1, 2, 3, 4]

var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(2);
// [1, 2, 3, 4, 5, 6]

//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr2.flat(Infinity); 
// [1, 2, 3, 4, 5, 6]

var arr4 = [1, 2, , 4, 5];
arr4.flat(); //flat() 方法会移除数组中的空项:
// [1, 2, 4, 5] 

flatMap 语法:arr.flatMap(fn,thisArg)

flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。

var arr1 = [1, 2, 3, 4];

arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]

// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

copyWithin(ES6) 语法:arr.copyWithin(target, start, end)

copyWithin() 方法基于ECMAScript 2015(ES6)规范,用于数组内元素之间的替换,即替换元素和被替换元素均是数组内的元素。
其中,taget 指定被替换元素的索引,start 指定替换元素起始的索引,end 可选,指的是替换元素结束位置的索引。
如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。

let array = [1,2,3,4,5]; 
let array2 = array.copyWithin(0,3);
console.log(array===array2,array2); // true [4, 5, 3, 4, 5]

let array = [1,2,3,4,5]; 
console.log(array.copyWithin(0,3,4)); // [4, 2, 3, 4, 5]

let array = [1,2,3,4,5]; 
console.log(array.copyWithin(0,-2,-1)); // [4, 2, 3, 4, 5]

fill(ES6) 语法:arr.fill(value, start, end)

fill() 方法基于ECMAScript 2015(ES6)规范,它同样用于数组元素替换,但与copyWithin略有不同,它主要用于将数组指定区间内的元素替换为某个值。
其中,value 指定被替换的值,start 指定替换元素起始的索引,end 可选,指的是替换元素结束位置的索引。
如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。

let array = [1,2,3,4,5];
let array2 = array.fill(10,0,3);
console.log(array===array2,array2); // true [10, 10, 10, 4, 5], 可见数组区间[0,3]的元素全部替换为10

6. concat、join、slice、toString、toLocateString、indexOf、lastIndexOf、未标准的toSource以及ES7新增的方法includes,不会改变自身的方法

concat 语法:arr.concat(value1, value2, …, valueN)

concat() 方法将传入的数组或者元素与原数组合并,组成一个新的数组并返回。

let array = [1, 2, 3];
let array2 = array.concat(4,[5,6],[7,8,9]);
console.log(array2); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(array); // [1, 2, 3], 可见原数组并未被修改

若concat方法中不传入参数,那么将基于原数组浅复制生成一个一模一样的新数组(指向新的地址空间)。

let array = [{a: 1}];
let array3 = array.concat();
console.log(array3); // [{a: 1}]
console.log(array3 === array); // false
console.log(array[0] === array3[0]); // true,新旧数组第一个元素依旧共用一个同一个对象的引用

join 语法:arr.join([separator = ‘,’]) separator可选,缺省默认为逗号。

join() 方法将数组中的所有元素连接成一个字符串。

let array = ['We', 'are', 'Chinese'];
console.log(array.join()); // "We,are,Chinese"
console.log(array.join('+')); // "We+are+Chinese"
console.log(array.join('')); // "WeareChinese"

slice 语法:arr.slice(start, end)

slice() 方法将数组中一部分元素浅复制存入新的数组对象,并且返回这个数组对象。
参数 start 指定复制开始位置的索引,end如果有值则表示复制结束位置的索引(不包括此位置)。

如果 start 的值为负数,假如数组长度为 length,则表示从 length+start 的位置开始复制,此时参数 end 如果有值,只能是比 start 大的负数,否则将返回空数组。

slice方法参数为空时,同concat方法一样,都是浅复制生成一个新数组。

let array = ["one", "two", "three","four", "five"];
console.log(array.slice()); // ["one", "two", "three","four", "five"]
console.log(array.slice(2,3)); // ["three"]

let array = [{color:"yellow"}, 2, 3];
let array2 = array.slice(0,1);
console.log(array2); // [{color:"yellow"}]
array[0]["color"] = "blue";
console.log(array2); // [{color:"bule"}]

toString 语法:arr.toString()

toString() 方法返回数组的字符串形式,该字符串由数组中的每个元素的 toString() 返回值经调用 join() 方法连接(由逗号隔开)组成。

let array = ['Jan', 'Feb', 'Mar', 'Apr'];
let str = array.toString();
console.log(str); // Jan,Feb,Mar,Apr

toLocaleString 语法:arr.toLocaleString()

toLocaleString() 类似toString()的变型,该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成。

let array= [{name:'zz'}, 123, "abc", new Date()];
let str = array.toLocaleString();
console.log(str); // [object Object],123,abc,2019/3/28 下午12:16:23

toString()和toLocaleString()两点区别:

1.当数字是四位数及以上时
    let a=1234.5678
    a.toString()//"1234.5678"
    a.toLocaleString()//"1,234.568"
2.当目标是标准时间格式时
    let sd=new Date()//Thu Mar 07 2019 12:11:55 GMT+0800 (中国标准时间)
    sd.toLocaleString()//"2019/3/7 下午12:21:55"
    sd.toString()//Thu Mar 07 2019 12:11:55 GMT+0800

indexOf 语法:arr.indexOf(element, fromIndex=0)

indexOf() 方法用于查找元素在数组中第一次出现时的索引,如果没有,则返回-1。

element 为需要查找的元素,fromIndex 为开始查找的位置,缺省默认为0。如果超出数组长度,则返回-1。如果为负值,假设数组长度为length,则从数组的第 length + fromIndex项开始往数组末尾查找,如果length + fromIndex<0 则整个数组都会被查找。

let array = ['abc', 'def', 'ghi','123'];
console.log(array.indexOf('def')); // 1
console.log(array.indexOf('def',-1)); // -1 此时表示从最后一个元素往后查找,因此查找失败返回-1
console.log(array.indexOf('def',-4)); // 1 由于4大于数组长度,此时将查找整个数组,因此返回1
console.log(array.indexOf(123)); // -1, 由于是严格匹配,因此并不会匹配到字符串'123'

lastIndexOf 语法:arr.lastIndexOf(element, fromIndex=length-1)

lastIndexOf() 方法用于查找元素在数组中最后一次出现时的索引,如果没有,则返回-1。并且它是indexOf的逆向查找,即从数组最后一个往前查找。

element 为需要查找的元素,fromIndex 为开始查找的位置,缺省默认为arr.length-1。如果超出数组长度,则返回-1。如果为负值,假设数组长度为length,则从数组的第 length + fromIndex项开始往数组栈底查找,如果length + fromIndex<0 则整个数组都会被查找。

同 indexOf 一样,lastIndexOf 也是严格匹配数组元素。

includes(ES7) 语法:arr.includes(element, fromIndex=0)

includes() 方法基于ECMAScript 2016(ES7)规范,它用来判断当前数组是否包含某个指定的值,如果是,则返回 true,否则返回 false。

element 为需要查找的元素,fromIndex 表示从该索引位置开始查找 element,缺省为0,它是正向查找,即从索引处往数组末尾查找。

let array = [0, 1, 2];
console.log(array.includes(0)); // true
console.log(array.includes(1)); // true
console.log(array.includes(2,-4)); // true

你可能会问,既然有了indexOf方法,为什么又造一个includes方法,arr.indexOf(x)>-1不就等于arr.includes(x)?看起来是的,几乎所有的时候它们都等同,唯一的区别就是includes能够发现NaN,而indexOf不能。

let array = [NaN];
console.log(array.includes(NaN)); // true
console.log(arra.indexOf(NaN)>-1); // false

7. 基于ES6,不会改变自身的方法一共有12个,分别为forEach、every、some、filter、map、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values

forEach 语法:arr.forEach(fn(value, index,array), thisArg)

forEach() 方法指定数组的每项元素都执行一次传入的函数,返回值为undefined。

fn 表示在数组每一项上执行的函数,接受三个参数:
value 当前正在被处理的元素的值
index 当前元素的数组索引
array 数组本身
thisArg 可选,用来当做fn函数内的this对象。

forEach 将为数组中每一项执行一次 fn 函数,那些已删除,新增或者从未赋值的项将被跳过(但不包括值为 undefined 的项)。

let array = [1, 3, 5];
let obj = {name:'cc'};
let sReturn = array.forEach(function(value, index, array){
  array[index] = value * value;
  console.log(this.name); // cc被打印了三次
},obj);
console.log(array); // [1, 9, 25], 可见原数组改变了
console.log(sReturn); // undefined, 可见返回值为undefined

*forEach无法直接退出循环,只能使用return 来达到for循环中continue的效果
*它总是返回 undefined值,即使你return了一个值。

1. 对于空数组是不会执行回调函数的
2. 对于已在迭代过程中删除的元素,或者空元素会跳过回调函数
3. 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
4. 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。
eg:
let a = [1, 2, ,3]; // 最后第二个元素是空的,不会遍历(undefined、null会遍历)
let obj = { name: 'obj的名字' };
let result = a.forEach(function (value, index, array) { 
    a[3] = '改变元素';
    a.push('添加到尾端,不会被遍历')
    console.log(value, 'forEach传递的第一个参数'); // 分别打印 1 ,2 ,改变元素
    console.log(this.name); // 'obj的名字' 打印三次 this绑定在obj对象上
    // break; // break会报错
    return value; // return只能结束本次回调 会执行下次回调
    console.log('不会执行,因为return 会执行下一次循环回调')
}, obj);
console.log(result); // 即使return了一个值,也还是返回undefined

every 语法:arr.every(fn, thisArg)

every()方法检测数组所有元素是否都符合判断条件(参数同上)

方法返回值规则:
1 ,如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测
2. 如果所有元素都满足条件,则返回 true

eg:
function isBigEnough(element, index, array) { 
  return element >= 10; // 判断数组中的所有元素是否都大于10
}
[12, 5, 8, 130, 44].every(x => x >= 10); // false
[12, 54, 18, 130, 44].every(x => x >= 10); // true

some 语法:arr.some(fn, thisArg)

some()数组中的是否有满足判断条件的元素(参数同上)

方法返回值规则:
1 ,如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测
2. 如果没有满足条件的元素,则返回false

eg:
function isBigEnough(element, index, array) {
   return (element >= 10); //数组中是否有一个元素大于 10
}
let result = [2, 5, 8, 1, 4].some(isBigEnough); // false
let result = [12, 5, 8, 1, 4].some(isBigEnough); // true

filter 语法:arr.filter(fn, thisArg)

filter()过滤原始数组,返回通过所提供函数实现的所有元素组成的新数组(参数同上)

eg:
let a = [32, 33, 16, 40];
let result = a.filter(function (value, index, array) {
  return value >= 18; // 返回a数组中所有大于18的元素
});
console.log(result,a);// [32,33,40] [32,33,16,40]

map 语法:arr.map(fn, thisArg)

map()对数组中的每个元素进行处理,返回新的数组(参数同上)

eg:
let a = ['1','2','3','4'];
let result = a.map(function (value, index, array) {
  return value + 0
});
console.log(result);  //["10", "20", "30", "40"]

reduce 语法:array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

reduce()为数组提供累加器,合并为一个值

1. 如果 initialValue 在调用 reduce 时被提供,那么第一个 total 将等于 initialValue,此时 currentValue 等于数组中的第一个值
2. 如果 initialValue 未被提供,那么 total 等于数组中的第一个值,currentValue 等于数组中的第二个值。此时如果数组为空,那么将抛出 TypeError
3. 如果数组仅有一个元素,并且没有提供 initialValue,或提供了 initialValue 但数组为空,那么回调不会被执行,数组的唯一值将被返回

eg:
// 数组求和 
let sum = [0, 1, 2, 3].reduce(function (a, b) {
  return a + b;
}, 0);
// 6
------
// 将二维数组转化为一维 将数组元素展开//见flat()
let flattened = [[0, 1], [2, 3], [4, 5]].reduce((a, b) => a.concat(b),[]);
 // [0, 1, 2, 3, 4, 5]

reduceRight 语法:array.reduceRight(function(total, currentValue, currentIndex, arr), initialValue)

这个方法除了与reduce执行方向相反外,其他完全与其一致

ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值

三个方法都返回一个新的 Array Iterator 对象,对象根据方法不同包含不同的值

eg:
for (let index of ['a', 'b'].keys()) {
  console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
  console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
  console.log(index, elem);
}
// 0 "a"
// 1 "b"

在for..of中如果遍历中途要退出,可以使用break退出循环。

如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历:
    
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

ES6 find()& findIndex() 根据条件找到数组成员 语法:arr.find(fn, thisArg),arr.findIndex(fn, thisArg)

find()定义:用于找出第一个符合条件的数组成员,并返回该成员,如果没有符合条件的成员,则返回undefined。

findIndex()定义:返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。

这两个方法都可以识别NaN,弥补了indexOf的不足.

eg:
// find
let a = [1, 4, -5, 10].find((n) => n < 0); // 返回元素-5
let b = [1, 4, -5, 10,NaN].find((n) => Object.is(NaN, n));  // 返回元素NaN
// findIndex
let a = [1, 4, -5, 10].findIndex((n) => n < 0); // 返回索引2
let b = [1, 4, -5, 10,NaN].findIndex((n) => Object.is(NaN, n));  // 返回索引4

8.扩展运算符

扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

扩展运算符后面可以放置表达式。
console.log((...[1, 2]))
注意,扩展运算符如果放在括号中,JavaScript引擎就会认为这是函数调用。如果这时不是函数调用,就会报错。
// Uncaught SyntaxError: Unexpected number

替代函数的 apply 方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。

// ES5 的写法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的写法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);

下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。

// ES5 的写法
Math.max.apply(null, [14, 3, 77])

// ES6 的写法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);

另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。

// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);

还有一个小例子
// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]);

扩展运算符的应用
(1)复制数组
    ES5:
    const a1 = [1, 2];
    const a2 = a1.concat();
    a2[0] = 2;
    a1 // [1, 2]
    
    使用扩展运算符:
    const a1 = [1, 2];
    // 写法一
    const a2 = [...a1];
    // 写法二
    const [...a2] = a1;

(2)合并数组
    const a1 = [{ foo: 1 }];
    const a2 = [{ bar: 2 }];
    
    const a3 = a1.concat(a2);
    const a4 = [...a1, ...a2];
    
    这两种方法都是浅拷贝
    a3[0] === a1[0] // true
    a4[0] === a1[0] // true
   
(3)与解构赋值结合
    // ES5
    a = list[0], rest = list.slice(1)
    // ES6
    [a, ...rest] = list
    
    const [first, ...rest] = [1, 2, 3, 4, 5];
    first // 1
    rest  // [2, 3, 4, 5]
    
    const [first, ...rest] = [];
    first // undefined
    rest  // []
    
    注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。    
    const [...butLast, last] = [1, 2, 3, 4, 5];
    // 报错
    
    const [first, ...middle, last] = [1, 2, 3, 4, 5];
    // 报错
(4)字符串
    扩展运算符还可以将字符串转为真正的数组。
    [...'hello']
    // [ "h", "e", "l", "l", "o" ]
    
    'x\uD83D\uDE80y'.length // 4
    [...'x\uD83D\uDE80y'].length // 3
    上面代码的第一种写法,JavaScript 会将四个字节的 Unicode 字符,
    识别为 2 个字符,采用扩展运算符就没有这个问题。
    因此,正确返回字符串长度的函数,可以像下面这样写。
    function length(str) {
      return [...str].length;
    }
    length('x\uD83D\uDE80y') // 3

(5)实现了 Iterator 接口的对象
    任何定义了遍历器(Iterator)接口的对象都可以用扩展运算符转为真正的数组。
    
    下面代码中,先定义了Number对象的遍历器接口,扩展运算符将5自动转成
    Number实例以后,就会调用这个接口,就会返回自定义的结果。
    Number.prototype[Symbol.iterator] = function*() {
      let i = 0;
      let num = this.valueOf();
      while (i < num) {
        yield i++;
      }
    }
    console.log([...5]) // [0, 1, 2, 3, 4]
    
    下面代码中,arrayLike是一个类似数组的对象,但是没有部署Iterator接口
    ,扩展运算符就会报错。
    let arrayLike = {
      '0': 'a',
      '1': 'b',
      '2': 'c',
      length: 3
    };
    // TypeError: Cannot spread non-iterable object.
    let arr = [...arrayLike];
(6)Map 和 Set 结构,Generator 函数
    扩展运算符内部调用的是数据结构的 Iterator 接口,
    因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。
    let map = new Map([
      [1, 'one'],
      [2, 'two'],
      [3, 'three'],
    ]);
    let arr = [...map.keys()]; // [1, 2, 3]
    
    Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
    const go = function*(){
      yield 1;
      yield 2;
      yield 3;
    };
    [...go()] // [1, 2, 3]
    上面代码中,变量go是一个 Generator 函数,执行后返回的是一个遍历器对象,
    对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
    
    

9.数组的空位

Array(3) // [, , ,]
空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。

0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false

ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。

forEach(), filter(), reduce(), every() 和some()都会跳过空位。
map()会跳过空位,但会保留这个值
join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。

// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1

// filter方法
['a',,'b'].filter(x => true) // ['a','b']

// every方法
[,'a'].every(x => x==='a') // true

// reduce方法
[1,,2].reduce((x,y) => x+y) // 3

// some方法
[,'a'].some(x => x !== 'a') // false

// map方法
[,'a'].map(x => 1) // [,1]

// join方法
[,'a',undefined,null].join('#') // "#a##"

// toString方法
[,'a',undefined,null].toString() // ",a,,"


ES6 则是明确将空位转为undefined。

Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。
Array.from(['a',,'b'])
// [ "a", undefined, "b" ]

扩展运算符(...)也会将空位转为undefined。
[...['a',,'b']]
// [ "a", undefined, "b" ]

copyWithin()会连空位一起拷贝
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]

fill()会将空位视为正常的数组位置。
new Array(3).fill('a') // ["a","a","a"]

for...of循环也会遍历空位。
let arr = [, ,];
for (let i of arr) {
  console.log(1);
}
// 1
// 1

entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]

// keys()
[...[,'a'].keys()] // [0,1]

// values()
[...[,'a'].values()] // [undefined,"a"]

// find()
[,'a'].find(x => true) // undefined

// findIndex()
[,'a'].findIndex(x => true) // 0


注: 如果与别处相似,纯属巧合~
若有问题或者错误请在下方留言~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343