Array 对象/关于数组

137 阅读10分钟

1、构造函数

Array是 JavaScript 的原生对象,同时也是一个构造函数,可以用它生成新的数组。

let arr = new Array(2);
arr.length //2
arr // [empty*2]

上面代码中,Array()构造函数的参数2,表示生成一个两个成员的数组,每个位置都是空值。

如果没有使用new关键字,运行结果也是一样的。考虑到语义性最好总是加上new

let arr = new Array(2);
//等同于
let arr = Array(2);

Array()构造函数有一个很大的缺陷,不同的参数个数会导致不一致的行为。

//无参数时返回空数组
new Array(); // []

//单个正整数参数 返回数组长度
new Array(2); //[empty*2]

//非正整数数值作为参数会报错 会报错
new Array(1.2);
new Array(-2);
// RangeError: Invalid array length

//单个非数值作为参数 例如布尔值、对象、字符串等 则该参数返回的是新数组的成员
new Array('qwer') // ['qwer']
new Array([1]) // [Array[1]]

//多参数时返回新数组成员
new Array(1,2) // [1,2]
new Array('q','w','e','r') // ['q','w','e','r']

所以建议写法为

//bad
let arr = new Array(1,2);

//good
let arr = [1,2]

注意一种特殊情况

let a = new Array(3);
let b = [undefined,undefined,undefined]

a.length //3 
b.length //3 

a[0] // undefined
b[0] // undefined

0 in a // false
0 in b // true

a的键名(成员的序号)都是空的,b的键名是有值的。

2、静态方法

2.1 Array.isArray()

Array.isArray方法返回一个布尔值,表示参数是否为数组。它可以弥补typeof运算符的不足

let arr = [1, 2, 3];

typeof arr // "object"
Array.isArray(arr) // true

3、实例方法

3.1 valueOf(), toString()

valueOf方法是一个所有对象都拥有的方法,表示对该对象求值。不同对象的valueOf方法不尽一致,数组的valueOf方法返回数组本身。

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

toString方法也是对象的通用方法,数组的toString方法返回数组的字符串形式。

let arr1 = [1, 2, 3];
arr.toString() // "1,2,3"

let arr2 = [1, 2, 3, [4, 5, 6]];
arr.toString() // "1,2,3,4,5,6"

3.2 push(),pop()

push方法会在数组末尾添加一个元素或多个元素,并返回新数组的长度 push方法会改变原数组

let arr = [1,2,3]
let a = arr.push(5) 
a // 4 
arr // [1,2,3,5]

pop方法会在数组末尾删除一个元素 pop方法会改变原数组

let arr = [1,2]
let a1 = arr.pop()
a1 // 2 
arr // [1]
arr.pop().pop() //undefined

对空数组使用pop方法,不会报错,而是返回undefined

3.3 shift(),unshift()

shift方法删除数组的第一个元素 并返回该元素 shift方法会改变原数组

let arr = [1,2,3]
let a1 = arr.shift()
a1    // 1
arr   // [2,3]

unshift方法在数组都一个位置添加一个新元素 并返回新数组的长度 unshift方法会改变原数组

let b1 = arr.unshift(0)
b1     // 3 
arr    // [0,2,3]

3.4 sort()

sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。

let arr = [11,2,3,4,5]
arr.sort() // [11,2,3,4,5]

sort()方法不是按照大小排序,而是按照字典顺序。也就是说,数值会被先转成字符串,再按照字典顺序进行比较,所以11排在2的前面。

如果想让sort方法按照自定义方式排序,可以传入一个函数作为参数。

[11, 2, 3].sort(function (a, b) {
  return a - b;
})
[11, 2, 3].sort((a,b)=>a-b)
// [2, 3, 11]

3.5 splice()

splice()方法用于删除原数组的一部分成员,并可以在删除的位置添加新的数组成员,返回值是被删除的元素。 该方法会改变原数组

       //开始下标  删除个数  新元素
arr.splice(start, count, addElement1, addElement2, ...);
let arr = ['a','b','c','d']
         //从下标2开始删除两个 包含下标2 
let a1 = arr.splice(2,2,1,2)
a1     // ['c','d']
arr    // ['a','b',1,2]

如果只是插入元素,splice方法的第二个参数可以设为0

let a = [1, 1, 1];
a.splice(1, 0, 2) // []
                  //在下标1前添加
a                 // [1, 2, 1, 1]

如果只提供第一个参数,等同于将原数组在指定位置拆分成两个数组。

let a = [1, 2, 3, 4];
a.splice(2) // [3, 4]
a           // [1, 2]

3.6 reverse()

reverse方法用于颠倒排列数组元素,返回改变后的数组。注意,该方法将改变原数组。

let a = ['a', 'b', 'c'];

a.reverse() // ["c", "b", "a"]
a // ["c", "b", "a"]

3.2 ~3.6 以上7种方法会改变原数组

3.7 join()

join()方法以指定参数作为分隔符,将所有数组成员连接为一个字符串返回。如果不提供参数,默认用逗号分隔。

let a = [1,2,3,4]

a.join(' ')   // '1 2 3 4'
a.join(' | ') // "1 | 2 | 3 | 4"
a.join()      // "1,2,3,4"

如果数组成员是undefinednull或空位,会被转成空字符串。

3.8 concat()

concat方法用于多个数组的合并。它将新数组的成员,添加到原数组成员的后部,然后返回一个新数组,原数组不变。

let arr =[1,2]
let a1 = arr.concat([3,4])
a1   // [1,2,3,4]
arr  // [1,2]

如果数组成员包括对象,concat方法返回当前数组的一个浅拷贝。所谓“浅拷贝”,指的是新数组拷贝的是对象的引用。

let obj = { a: 1 };
let oldArray = [obj];

let newArray = oldArray.concat();

obj.a = 2;
newArray[0].a // 2

上面代码中,原数组包含一个对象,concat方法生成的新数组包含这个对象的引用。所以,改变原对象以后,新数组跟着改变。

3.9 slice()

slice()方法用于提取目标数组的一部分,返回一个新数组,原数组不变。

arr.slice(start, end);
//从下标start开始 到end结束 不包含结束下标

如果省略第二个参数,则一直返回到原数组的最后一个成员。 如果没有参数,实际上等于返回一个原数组的拷贝。 如果参数为负数,则表示倒数计算的位置。

let a = ['a', 'b', 'c'];

a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]

a.slice(-2) // ["b", "c"]
a.slice(-2, -1) // ["b"]

如果第一个参数大于等于数组长度,或者第二个参数小于第一个参数,则返回空数组。

let a = ['a', 'b', 'c'];
a.slice(4) // []
a.slice(2, 1) // []

slice()方法的一个重要应用,是将类似数组的对象转为真正的数组。

Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// ['a', 'b']

Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);

上面代码的参数都不是数组,但是通过call方法,在它们上面调用slice()方法,就可以把它们转为真正的数组。

3.10 map()

map()方法将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回。

map()方法向它传入三个参数:当前成员、当前位置和数组本身。

数组.map(function(ele, index, arr) {
  return ele * index;
});
//例 把数组每个成员+1
let arr1 = [2,5,7]
let arr2 = arr1.map(item=> item+1)
console.log(arr2)  // [3,6,8] 

如果数组有空位,map()方法的回调函数在这个位置不会执行,会跳过数组的空位。

let f = function (n) { return 'a' };

[1, undefined, 2].map(f) // ["a", "a", "a"]
[1, null, 2].map(f) // ["a", "a", "a"]
[1, , 2].map(f) // ["a", , "a"]

上面代码中,map()方法不会跳过undefinednull,但是会跳过空位

3.11 filter()

filter()方法用于过滤数组成员,满足条件的成员组成一个新数组返回。 它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。

filter()方法的参数函数可以接受三个参数:当前成员,当前位置和整个数组

数组.filter(function (ele, index, arr) {
  return index % 2 === 0;
});
// [1, 3, 5]
let arr = [0,"a",1,false]
let a1 = arr.filter(Boolean)
console.log(a1)   //["a",1]

3.12 forEach()

forEach()方法与map()方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach()方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用map()方法,否则使用forEach()方法。

forEach()的用法与map()方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。

数组.forEach(function(ele, index, arr){
   
})


[1, , 2].forEach(log)
// 2
// 3

注意,`forEach()`方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用`for`循环。

forEach()方法也会跳过数组的空位,不会跳过undefinednull

3.13 some() , every()

这两个方法类似“断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件。

它们接受一个函数作为参数,所有数组成员依次执行该函数。该函数接受三个参数:当前成员、当前位置和整个数组,然后返回一个布尔值。

some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false

every方法是所有成员的返回值都是true,整个every方法才返回true,否则返回false

let arr = [1, 2, 3, 4, 5];
arr.some(function (ele, index, arr) {
  return ele >= 3;
});
// true

arr.every(function (ele, index, arr) {
  return elem >= 3;
});
// false

3.14 reduce() ,reduceRight()

reduce()方法和reduceRight()方法依次处理数组的每个成员,最终累计为一个值。

它们的差别是,reduce()是从左到右处理(从第一个成员到最后一个成员),reduceRight()则是从右到左(从最后一个成员到第一个成员),其他完全一样。 如果要对累积变量指定初值,可以把它放在reduce()方法和reduceRight()方法的第二个参数。

[1, 2, 3, 4, 5].reduce(function (a, b) {
  return a + b;
}, 10);
// 25

3.15 indexOf() , lastIndexOf()

indexOf方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1

indexOf方法还可以接受第二个参数,表示搜索的开始位置。

let a = ['a', 'b', 'c'];

a.indexOf('b')   // 1
a.indexOf('y')   // -1

['a', 'b', 'c'].indexOf('a', 1) // -1

lastIndexOf()同理从后往前

3.17 flat()

var newArray = arr.flat([depth])
// depth 可选:指定要提取嵌套数组的结构深度,默认值为 1。

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

const list = [1, 2, [3, 4, [5, 6]]]; 
list.flat(Infinity); // [1, 2, 3, 4, 5, 6]

flatMap()

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

const list = [1, 2, 3]; 
list.flatMap((el) => [el, el * el]); // [1, 1, 2, 4, 3, 9]

3.18 at()

at() 方法接收一个整数值并返回该索引的项目,允许正数和负数。负整数从数组中的最后一个项目开始倒数。

// Code
const list = [1, 2, 3, 4, 5]; 
list.at(1);  // 2 
list.at(-1); // 5 
list.at(-2); // 4

3.19 includes()

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false

const list = [1, 2, 3, 4, 5]; 
list.includes(3); // true 
list.includes(6); // false

3.20 find() ,findIndex()

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined findIndex() 方法返回数组中满足提供的测试函数的第一个元素的索引。若没有找到对应元素则返回-1

const list = [1, 2, 3, 4, 5]; 
list.find((el) => el === 3); // 3 
list.find((el) => el === 6); // undefined

const array = [5, 12, 8, 130, 44]; 
array.findIndex((element) => element > 13); // 3

3.21 copyWithIn() ,fill() ....MDN

3.22 链式调用

let users = [
  {name: 'tom', email: 'tom@example.com'},
  {name: 'peter', email: 'peter@example.com'}
];

users.map(function (user) {
  return user.email;
}).filter(function (email) {
   //过滤以t开头的
  return /^t/.test(email);
}).forEach(function (email) {
  console.log(email);
});
// "tom@example.com"