ES6—数组

959 阅读9分钟

作者:米书林 参考文章:《菜鸟教程》、《 ECMAScript 6 入门》(阮一峰)

数组的创建

传统js数组的创建

传统js创建数组有两种方式:

1.new一个Array()构造函数

// 先定义,后赋值
let arr = new Array();
arr[0] = 1;
arr[1] = 2;
console.log(arr)  // [1, 2]

// 定义时赋值
let arr = new Array(1,2,3);
console.log(arr);  // [1, 2,3]

2.使用字面量定义

let arr = [1,2,3];
console.log(arr);  // [1, 2, 3]

ES6数组的创建

1.Array.of()

作用:将参数中所有值作为元素形成数组 背景:传统js使用Array()构造函数创建数组,会因传递参数的个数存在争议(只传递一个参数时,参数会被当作数组长度处理)

Array() // []
Array(2) // [, , ]
Array(1, 2) // [1, 2]

用法: 直接使用Array.of(),向括号中传递任何类型的参数,参数全部处理为数组的元素

let arr = Array.of(1,"2",{},[],new Map(),new Set(),null,undefined,NaN,true,false,function a(){});
console.log(arr); 
// (12) [1, "2", {…}, Array(0), Map(0), Set(0), null, undefined, NaN, true, false, ƒ]

模拟Array.of方法:

function ArrayOf(){
  return [].slice.call(arguments);
}

2.Array.from()

作用:将类数组对象或可迭代对象(包括 ES6 新增的数据结构 Set 和 Map)转化为数组

1.基本用法:

接收三个参数: -第一个是转换的类数组对象或可迭代对象; -第二个是对参数进行处理的map 函数(可选); -第三个是指定 map 函数执行时的 this 对象(可选)

let map = {
    haha: function(n) {
        return  2;
    }
    do: function(n) {
        return n * 2;
    }
}
let arrayLike = [1, 2, 3];
console.log(Array.from(arrayLike, function (n){
    console.log(this);  /// 这里this是map对象
    return this.do(n);
}, map)); // [2, 4, 6]
2.转换类数组对象
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c'];

// 注意:一定要是类数组对象,即定义了length属性,若没有定义则返回空数组
let arrayLike1 = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
};
let arr3 = Array.from(arrayLike1);   // [];

// 元素属性名不为数值且无法转换为数值,返回长度为 length 元素值为 undefined 的数组  
let arr4 = Array.from({
  a: 1,
  b: 2,
  length: 2
});
console.log(array4); // [undefined, undefined]
3.转换可迭代对象

a.数组本身

let arr = [1,2,3];
let arr1 = Array.from(arr);   // [1,2,3]

// 注意这是数组浅拷贝,上面我们修改arr1的值不会影响arr,但下面的情况就不一样的
let arr2 = [1,2,[3,4]];
let arr3 = Array.from(arr2);
arr3[2][0] = 5;
console.log(arr2);  //  [1,2,[5,4]]
// 上面的代码我们可以看出,我们修改新数组,原数组的值也跟着变了,同样的还有concat,slice等方法

b.转换Map结构数据

let map = new Map();
map.set("one",1);
map.set("two",2);
let arr = Array.from(map);  // [["one",1],["two",2]]

c.转换Set结构数据

let set = new Set([1,2,3,3]);
let arr = Array.from(set);  // [1, 2, 3]

d.转换字符串

let str = 'abc';
console.log(Array.from(str)); // ["a", "b", "c"]

扩展运算符

写法:... 用途:将一个数组转为用逗号分隔的参数序列

// 基础用法
console.log(...[1,2,3]);  // 1 2 3
// 上面语句相当于console.log(1,2,3)

// 数组缺少某元素时用undefined替换
console.log(...[1,2,,3]);  // 1 2 undefined 3

//  扩展运算符后面放置表达式
const arr = [1,...[1+2]];  // arr = [1,3];
const arr1 = [
  ...(x > 0 ? ['a'] : []),
  'b',
]; 
const arr2 = ['a',...('b'+'c')];  // ["a", "b", "c"]
// 注意:...后面表达式必须是可迭代对象,即const arr = [1,(1+2)]会报错

// 扩展运算符后面是空数组会被忽略
[...[], 1]  // [1]

扩展运算符的应用场景

1.替换函数的apply方法

// ES5 的写法
Math.max.apply(null, [20, 35, 17])

// ES6 的写法
Math.max(...[20, 35, 17])

// 等同于
Math.max(20, 35, 17);

2.复制数组

// ES5写法
const arr1 = [1, 2];
const arr2 = arr1.concat();
arr2[0] = 3;
console.log(arr1,arr2)  //  [1, 2]   [3, 2]

// ES6写法
const arr1 = [1, 2];
const arr2 = [...arr1];
arr2[0] = 3;
console.log(arr1,arr2) ;
 //  [1, 2]   [3, 2]

// 但是当数组中包含多层数组时,修改新数组还是会影响原数组
const arr1 = [1, 2,[3,4]];
const arr2 = [...arr1];
arr2[0] = 3;
arr2[2][0] = 5;
console.log(arr1,arr2) ;  // [1, 2,[5,4]]   [3, 2,[5,4]]
// 这时候我们还是得使用递归才能进行深度拷贝

3.合并数组

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并数组
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并数组
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]

4.与解构赋值结合生成新数组

const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest  // [2, 3, 4, 5]

5.将字符串转为真正的数组

能够识别四个字节的 Unicode 字符,这是使用split()方法不能实现的

let str = 'x\uD83D\uDE80y';
let arr = str.split("");  // ["x", "�", "�", "y"]
[...str];  // ["x", "🚀", "y"]

6.将Map 和 Set 结构数据转换为数组

let map = new Map([[1,"一"],[2,"二"]]);
let set = new Set([1,2,3]);
[...map]; // [[1,"一"],[2,"二"]]
[...set];  // [1, 2, 3]

7.返回Generator 函数执行的结果

let fun= function*(){
  yield 1;
  yield 2;
  yield 3;
};

[...fun()] // [1, 2, 3] 

扩展的方法

查找

1.find()

作用:查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素,如果没有符合条件的成员,则返回undefined

[1,2,3,4,5].find(x=>x>3);  // 4
[1,2,3,4,5].find(x=>x<0); // undefined

// 返回数组的第一个元素
[1,2,3,4].find(x=>true);  // 1

find方法的回调函数可以接受三个参数,依次为当前的值、当前的位置和原数组。

[1, 2, 3, 4].find(function(value, index, arr) {
  return value > 3;
}) // 4

find()方法都可以接受第二个参数,用来绑定回调函数的 this对象。

function foo(value){
  return value> this.age;
}
let person = {name: '张三', age: 22};
[18, 2, 24, 20].find(foo, person);    // 24

2.findIndex()

作用:查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引,若未查找到符号条件的元素则返回-1. 其他和find()函数类似 另外,这两个方法都可以发现NaN,弥补了数组的indexOf方法的不足

[NaN].indexOf(NaN)
// -1

[NaN].findIndex(y => Object.is(NaN, y))
// 0

填充

1.fill()

作用:使用给定值填充数组(修改原数组) 基本用法:

[1,2,3,4].fill(5);
// [5, 5, 5, 5]

可传递的参数:共三个,第一个是用于填充的内容,第二个是指定开始位置,第三个指定结束位置

[1,2,3,4].fill(5,1,2); // [1, 5, 3, 4]

特殊情况:属于浅拷贝,即若填充内容是引用类型,则改变填充后的内容会影响原数据

let obj = {name:"张三"};
let arr = [1,2,3,4];
arr.fill(obj,0,1);
console.log(arr);   // [{name:"张三"}, 2, 3, 4]
arr[0].name = "李四";  
console.log(arr); // [{name:"李四"}, 2, 3, 4]
console.log(obj);  // {name: "李四"}
// 引用类型都会被影响

2.copyWithin()

作用:将同数组的某一部分用另一部分替换 参数:供三个,第一个指定被替换元素的起始位置,第二个指定用来替换的元素的起始位置,第三个指定用来替换元素的结束位置(实际结束位置=第三个参数-1)。 用法:

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

特殊情况: 1.省略第三个参数,表示替换元素结束位置是字符串末尾

let arr = [1,2,3,4,5,6];
arr.copyWithin(0,2);   // 相当于arr.copyWithin(0,2,6); 或者arr.copyWithin(0,2,arr.length); 
console.log(arr); // [3, 4, 5, 6, 5, 6]

2.第一个参数为负数表示从倒数第n个元素开始替换

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

3.后面两个参数同为负数表示从倒数第几个开始到倒数第结束的替换元素

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

4.后面两个参数有一个为负数,另一个不为负数将不会起作用

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

##遍历 ####1.entries() 作用:遍历键值对

// 使用for...of遍历
let arr = [1,2]
for(let chl of arr.entries()){
  console.log(chl )
}
// [0, 1]
// [1, 2]
// chl是包含键和值数组

// 不用for...of循环遍历
let entr = arr.entries();
console.log(entr.next().value); // [0,1]
console.log(entr.next().value); // [1, 2]

// 数组含空位
console.log([...[,'a'].entries()]); // [[0, undefined], [1, "a"]]

2.keys()

作用:遍历键名

for(let key of ['a', 'b'].keys()){
    console.log(key);
}
// 0
// 1
 
// 数组含空位
console.log([...[,'a'].keys()]); // [0, 1]

3.values()

作用:遍历键值

for(let value of ['a', 'b'].values()){
    console.log(value);
}
// "a"
// "b"
 
// 数组含空位
console.log([...[,'a'].values()]); // [undefined, "a"]

包含

1.includes()

作用:数组是否包含指定值

let arr = [1,"2",null,NaN,undefined,true];
arr.includes(1);  // true
arr.includes("2"); // true
arr.includes(2);  // false
arr.includes(null); // true
arr.includes(undefined);  // true
arr.includes(NaN);  // true
arr.includes(true);  // true

let arr1 = [];
arr1.includes(undefined);  // false
arr1.includes(null);  // false
arr1.includes(NaN);  // false

该方法的第二个参数表示搜索的起始位置,默认为0。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。

[1, 2, 3].includes(3, 3);  // false
[1, 2, 3].includes(3, -1); // true

Map 和 Set 数据结构有一个has方法,需要注意与includes区分。 Map 结构的has方法,是用来查找键名的,比如Map.prototype.has(key)WeakMap.prototype.has(key)Reflect.has(target, propertyKey)。 Set 结构的has方法,是用来查找值的,比如Set.prototype.has(value)WeakSet.prototype.has(value)

嵌套数组转一维数组

1.flat()

作用:用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。 基本用法:

console.log([1 ,[2, 3]].flat());

// 自动跳过空位
console.log([1, [2, , 3]].flat());<p> // [1, 2, 3]

特殊情况: 1.拉平多层

console.log([1 ,[2, [3,4,5,[6,7]]]].flat(2)); // [1, 2, 3, 4, 5, [6,7]]

2.拉平所有

console.log([1 ,[2, [3,4,5,[6,7]]]].flat(Infinity)); // [1, 2, 3, 4, 5, 6,7]

2.flatMap()

作用:先对数组中每个元素进行了的处理,再对数组执行 flat() 方法。该方法返回一个新数组,不改变原数组。

// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]

注意: flatMap()只能展开一层数组

// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]

// 参数1:遍历函数,该遍历函数可接受3个参数:当前元素、当前元素索引、原数组 // 参数2:指定遍历函数中 this 的指向

arr.flatMap(function callback(currentValue[, index[, array]]) {
  // ...
}[, thisArg])

排序稳定性

排序稳定性:是排序算法的重要属性,指的是排序关键字相同的项目,排序前后的顺序不变。

const arr = [
  'peach',
  'straw',
  'apple',
  'spork'
];

const stableSorting = (s1, s2) => {
  if (s1[0] < s2[0]) return -1;
  return 1;
};

arr.sort(stableSorting)
// ["apple", "peach", "straw", "spork"]  稳定的

const unstableSorting = (s1, s2) => {
  if (s1[0] <= s2[0]) return -1;
  return 1;
};

arr.sort(unstableSorting)
// ["apple", "peach", "spork", "straw"]  不稳定的