引用类型-Array 1

182 阅读15分钟

除了Object之外,Array类型恐怕是ECMAScript中最常用的类型了。ECMAScript中的数组和其他语言中的数组有着相当大的区别。ECMAScript数组的每一项可以保存任何类型的数据,而且它的大小是可以动态调整的,即可以随着数据的添加自动增长以容纳新增数据。

一、数组的创建

创建数组的基本方法有两种。

1.使用Array构造函数

var colors = new Array();

如果要创建一个指定长度的数组,可以给构造函数传递该数量(其中每一项的初始值都是undefined)

var colors = new Array(5)

也可以向Array中传递数组中应该包含的项。

var colors = new Array("red","blue","yellow","green","gray")

2.使用数组字面量表示法

数组字面量由一对包含数组项的方括号表示,多个数组项之间以逗号隔开。

var colors = [];
var colors = [,,,,];
var colors = ["red","blue","yellow","green","gray"];

3.这两种方法创建的差别

new Array(3)     //创建一个长度为3,初始值为undefined
[3]              //创建一个长度为1,初始值为3
new Array('red') // 创建一个长度为1,初始值为red
['red']          //创建一个长度为1,初始值为red

Array()是一个对象,[]是一个数据原型,使用new Array()系统每次都会新生成一个对象(浏览器每生成一个对象都会耗费资源去构造他的属性和方法),他的子集是[]; 所有性能上面[] 优于new Array() 数组最多可以包含4294967295个项,这几乎已经满足任何变成需求,如果添加的数据项超过这个上限就会发生异常,而创建一个初始大小和找个上限值接近的数组,则可能会导致运行时间超长的脚本错误。

二、数组的方法

1)concat()

用于连接两个或多个数组
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。

arrayObject.concat(arrayX,arrayX,......,arrayX)
var a = [1,2,3];
console.log(a.concat(4,5)); //[1, 2, 3, 4, 5]
console.log(a)              //[1, 2, 3]

2) constructor

constructor 属性返回对创建此对象的数组函数的引用。

var test=new Array();
if (test.constructor==Array){
	document.write("This is an Array");
}
if (test.constructor==Object){
	document.write("This is an Object");
}
if (test.constructor==Boolean){
	document.write("This is a Boolean");
}
if (test.constructor==Date){
	document.write("This is a Date");
}
if (test.constructor==String){
	document.write("This is a String");
}

3) copyWithin()

从数组的指定位置拷贝元素到数组的另一个指定位置中。
array.copyWithin(target, start, end)
target: 必需。复制到指定目标索引位置。
start: 可选。元素复制的起始位置。 end:可选。停止复制的索引位置 (默认为 array.length)。如果为负值,表示倒数。

var colors = ["red","blue","yellow","green","gray"];
colors.copyWithin(2, 0, 2);  //["red", "blue", "red", "blue", "gray"]

4) entries()

entries() 方法返回一个数组的迭代对象,该对象包含数组的键值对 (key/value)。 迭代对象中数组的索引值作为 key, 数组元素作为 value。

var _colors = colors.entries();
x.next().value  // [0,red]
x.next().value  // [1,blue]
x.next().value  // [2,yellow]

5) every()

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。
注意:若收到一个空数组,此方法在一切情况下都会返回 true。

[1,2,3,4,5,6].every((curValue)=> curValue>0))  // true
[1,2,3,4,5,6].every((curValue)=> curValue>4))  // false
[].every((curValue)=> curValue>0))  // true

6) fill()

fill() 方法用于将一个固定值替换数组的元素。
array.fill(value, start, end)
value: 必需。填充的值。
start: 可选。开始填充位置。 end: 可选。停止填充位置 (默认为 array.length)

colors.fill("pink",0,2) // ["pink", "pink", "yellow", "green", "gray"]

7) filter()

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
注意: filter() 不会对空数组进行检测。
注意: filter() 不会改变原始数组。

let backArr = [20,30,35,50,60].filter(function(age){
        return age >= 30
    });
console.log(backArr)  // [30, 35, 50, 60]

8) find()

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。

let result = [20,30,35,50,60].find(function(age) {
        return age>=30
    })
console.log(result)  // 30

9) findIndex()

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引。否则返回-1。

let index = [20,30,35,50,60].findIndex(function(age) {
        return age>=30
    })
console.log(index)  // 1

10) flat()

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
var newArray = arr.flat([depth])
depth: 指定要提取嵌套数组的结构深度,默认值为 1。

let newArr =  [20,30,[35,[50,60]]].flat()
console.log(newArr)    // [20,30,35,[50,60]]
let newArr2 =  [20,30,[35,[50,60]]].flat(2)
console.log(newArr2)   // [20,30,35,50,60]

11) flatMap()

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

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

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

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

// only one level is flattened
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]

let arr1 = ["it's Sunny in", "", "California"];

arr1.map(x => x.split(" "));
// [["it's","Sunny","in"],[""],["California"]]

arr1.flatMap(x => x.split(" "));
// ["it's","Sunny","in", "", "California"]

12) forEach()

forEach() 方法对数组的每个元素执行一次给定的函数。
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
注意: 除了抛出异常以外,没有办法中止或跳出 forEach() 循环。如果你需要中止或跳出循环,forEach() 方法不是应当使用的工具。
只要条件允许,也可以使用 filter() 提前过滤出需要遍历的部分,再用 forEach() 处理。

如果数组在迭代时被修改了,则其他元素会被跳过。
下面的例子会输出 "one", "two", "four"。当到达包含值 "two" 的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 "four" 正位于在数组更前的位置,所以 "three" 会被跳过。 forEach() 不会在迭代之前创建数组的副本。

var words = ['one', 'two', 'three', 'four'];
words.forEach(function(word) {
  console.log(word);
  if (word === 'two') {
    words.shift();
  }
});
// one
// two
// four

13) includes()

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
注意:使用 includes()比较字符串和字符时是区分大小写。
arr.includes(valueToFind[, fromIndex])

从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。

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

如果 fromIndex 大于等于数组的长度,则会返回 false,且该数组不会被搜索。

var arr = ['a', 'b', 'c'];
arr.includes('c', 3);   // false
arr.includes('c', 100); // false

14) indexOf()

indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。
arr.indexOf(searchElement[, fromIndex])
开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.

15) join()

join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。

const elements = ['Fire', 'Air', 'Water'];

console.log(elements.join());
// expected output: "Fire,Air,Water"

console.log(elements.join(''));
// expected output: "FireAirWater"

console.log(elements.join('-'));
// expected output: "Fire-Air-Water"

如果一个元素为 undefined 或 null,它会被转换为空字符串。

16)keys()

keys() 方法返回一个包含数组中每个索引键的Array Iterator对象。

const array1 = ['a', 'b', 'c'];
const iterator = array1.keys();

for (const key of iterator) {
  console.log(key);
}

// expected output: 0
// expected output: 1
// expected output: 2

索引迭代器会包含那些没有对应元素的索引

var arr = ["a", , "c"];
var sparseKeys = Object.keys(arr);
var denseKeys = [...arr.keys()];
console.log(sparseKeys); // ['0', '2']
console.log(denseKeys);  // [0, 1, 2]

17)lastIndexOf()

lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。
arr.lastIndexOf(searchElement[, fromIndex])

fromIndex 可选
从此位置开始逆向查找。默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

18) map()

map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]

通常情况下,map 方法中的 callback 函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着 map 只给 callback 传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。

["1", "2", "3"].map(parseInt); // [1, NaN, NaN]

parseInt 经常被带着一个参数使用, 但是这里接受两个。第一个参数是一个表达式而第二个是callback function的基, Array.prototype.map 传递3个参数:element, index, array

// parseInt(string, radix) -> map(parseInt(value, index))
/*  first iteration (index is 0): */ parseInt("1", 0); // 1
/* second iteration (index is 1): */ parseInt("2", 1); // NaN
/*  third iteration (index is 2): */ parseInt("3", 2); // NaN

下面让我们来讨论解决方案:

function returnInt(element) {
  return parseInt(element, 10);
}

['1', '2', '3'].map(returnInt); // [1, 2, 3]
// Actual result is an array of numbers (as expected)

// Same as above, but using the concise arrow function syntax
['1', '2', '3'].map( str => parseInt(str) );

// A simpler way to achieve the above, while avoiding the "gotcha":
['1', '2', '3'].map(Number); // [1, 2, 3]

// But unlike parseInt(), Number() will also return a float or (resolved) exponential notation:
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
// For comparison, if we use parseInt() on the array above:
['1.1', '2.2e2', '3e300'].map( str => parseInt(str) ); // [1, 2, 3]

19) pop()

pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。 返回值:从数组中删除的元素(当数组为空时返回undefined)。

const plants = ['broccoli', 'cauliflower', 'cabbage', 'kale', 'tomato'];

console.log(plants.pop());
// expected output: "tomato"

console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage", "kale"]

plants.pop();

console.log(plants);
// expected output: Array ["broccoli", "cauliflower", "cabbage"]

20)push()

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

const animals = ['pigs', 'goats', 'sheep'];

const count = animals.push('cows');
console.log(count);
// expected output: 4
console.log(animals);
// expected output: Array ["pigs", "goats", "sheep", "cows"]

animals.push('chickens', 'cats', 'dogs');
console.log(animals);
// expected output: Array ["pigs", "goats", "sheep", "cows", "chickens", "cats", "dogs"]

21) reduce()

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
reduce为数组中的每一个元素依次执行callback函数,不包括数组中被删除或从未被赋值的元素,接受四个参数:
accumulator 累计器
currentValue 当前值
currentIndex 当前索引
array 数组
initialValue 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:如果调用reduce()时提供了initialValue,accumulator取值为initialValue,currentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。

const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。

几个应用例子

1.数组里所有值的和

var total = [ 0, 1, 2, 3 ].reduce(
  ( acc, cur ) => acc + cur,
  0
);
// 6

2.累加对象数组里的值

var initialValue = 0;
var sum = [{x: 1}, {x:2}, {x:3}].reduce(
    (accumulator, currentValue) => accumulator + currentValue.x
    ,initialValue
);

console.log(sum) // logs 6

3.将二维数组转化为一维

var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
 ( acc, cur ) => acc.concat(cur),
 []
);
// flattened is [0, 1, 2, 3, 4, 5]

4.计算数组中每个元素出现的次数

var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];

var countedNames = names.reduce(function (allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

5.按属性对object分类

var people = [
  { name: 'Alice', age: 21 },
  { name: 'Max', age: 20 },
  { name: 'Jane', age: 20 }
];

function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}

var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// { 
//   20: [
//     { name: 'Max', age: 20 }, 
//     { name: 'Jane', age: 20 }
//   ], 
//   21: [{ name: 'Alice', age: 21 }] 
// }

22) reduceRight()

reduceRight() 方法接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。

const array1 = [[0, 1], [2, 3], [4, 5]].reduceRight(
  (accumulator, currentValue) => accumulator.concat(currentValue)
);

console.log(array1);
// expected output: Array [4, 5, 2, 3, 0, 1]

23) reverse()

reverse() 方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。

const array1 = ['one', 'two', 'three'];

const reversed = array1.reverse();
console.log('reversed:', reversed);
// ["three", "two", "one"]

24) shift()

shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。

const array1 = [1, 2, 3];

const firstElement = array1.shift();

console.log(array1);
// expected output: Array [2, 3]

console.log(firstElement);
// expected output: 1

25) slice()

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];

console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]

console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]

console.log(animals.slice(1, 5));
// expected output: Array ["bison", "camel", "duck", "elephant"]

26) some()

some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。
注意:如果用一个空数组进行测试,在任何情况下它返回的都是false。

const array = [1, 2, 3, 4, 5];

// checks whether an element is even
const even = (element) => element % 2 === 0;

console.log(array.some(even));
// expected output: true

[2, 5, 8, 1, 4].some(x => x > 10);  // false
[12, 5, 8, 1, 4].some(x => x > 10); // true

27) sort()

sort() 方法用原地算法对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的

const months = ['March', 'Jan', 'Feb', 'Dec'];
months.sort();
console.log(months);
// expected output: Array ["Dec", "Feb", "Jan", "March"]

const array1 = [1, 30, 4, 21, 100000];
array1.sort();
console.log(array1);
// expected output: Array [1, 100000, 21, 30, 4]

如果没有指明 compareFunction ,那么元素会按照转换为的字符串的诸个字符的Unicode位点进行排序。例如 "Banana" 会被排列到 "cherry" 之前。当数字按由小到大排序时,9 出现在 80 之前,但因为(没有指明 compareFunction),比较的数字会先被转换为字符串,所以在Unicode顺序上 "80" 要比 "9" 要靠前。

var items = [
  { name: 'Edward', value: 21 },
  { name: 'Sharpe', value: 37 },
  { name: 'And', value: 45 },
  { name: 'The', value: -12 },
  { name: 'Magnetic' },
  { name: 'Zeros', value: 37 }
];

// sort by value
items.sort(function (a, b) {
  return (a.value - b.value)
});

// sort by name
items.sort(function(a, b) {
  var nameA = a.name.toUpperCase(); // ignore upper and lowercase
  var nameB = b.name.toUpperCase(); // ignore upper and lowercase
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }

  
// names must be equal

  return 0;
});

28) splice()

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。

const months = ['Jan', 'March', 'April', 'June'];
months.splice(1, 0, 'Feb');
// inserts at index 1
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "June"]

months.splice(4, 1, 'May');
// replaces 1 element at index 4
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "April", "May"]
months.splice(3, 1);
// delete 1 element at index 3
console.log(months);
// expected output: Array ["Jan", "Feb", "March", "May"]
// delete all element at index 2
months.splice(2);
console.log(months);
// expected output: Array ["Jan", "Feb"]

29)toLocaleString()

toLocaleString() 返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。
arr.toLocaleString([locales[,options]]);

locales 可选
带有BCP 47语言标记的字符串或字符串数组,关于locales参数的形式与解释,请看Intl页面。

const array1 = [1, 'a', new Date('21 Dec 1997 14:12:00 UTC')];
const localeString = array1.toLocaleString('en', { timeZone: 'UTC' });

console.log(localeString);
// expected output: "1,a,12/21/1997, 2:12:00 PM",
// This assumes "en" locale and UTC timezone - your results may vary

var prices = ['¥7', 500, 8123, 12];
prices.toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' });

// "¥7,¥500,¥8,123,¥12"

30) toString()

toString() 返回一个字符串,表示指定的数组及其元素。

const array1 = [1, 2, 'a', '1a'];

console.log(array1.toString());
// expected output: "1,2,a,1a"

31) unshift()

unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度(该方法修改原有数组)。

const array1 = [1, 2, 3];

console.log(array1.unshift(4, 5));
// expected output: 5

console.log(array1);
// expected output: Array [4, 5, 1, 2, 3]

32) values()

values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值

const array1 = ['a', 'b', 'c'];
const iterator = array1.values();

for (const value of iterator) {
  console.log(value);
}

// expected output: "a"
// expected output: "b"
// expected output: "c"

1.使用 .next() 迭代

var arr = ['a', 'b', 'c', 'd', 'e'];
var iterator = arr.values(); 
iterator.next();               // Object { value: "a", done: false }
iterator.next().value;         // "b"
iterator.next()["value"];      // "c"
iterator.next();               // Object { value: "d", done: false }
iterator.next();               // Object { value: "e", done: false }
iterator.next();               // Object { value: undefined, done: true } 
iteraroe.next().value;         // undefined

注意:一次性:数组迭代器是一次性的,或者说临时对象

var arr = ['a', 'b', 'c', 'd', 'e'];
 var iterator = arr.values();
 for (let letter of iterator) {
 console.log(letter); 
} //"a" "b" "c" "d" "e"
for (let letter of iterator) {
console.log(letter);
} // undefined

解释: 当 next().done=true 或 currentIndex>length 时, for..of 循环结束。
值: 数组迭代器中存储的是原数组的地址,而不是数组元素值。如果数组中元素改变,那么迭代器的值也会改变

var arr = ['a', 'b', 'c', 'd', 'e']; 
var iterator = arr.values();
console.log(iterator); // Array Iterator {  }
iterator.next().value; // "a"
arr[1] = 'n';                 
iterator.next().value; //  "n"