阅读 1796

【js进阶系列】一文学会31个数组方法

前面的话

番外

JS数组上的方法太多了,有的修改原数组,有的返回一个新数组,记住了又混淆了,所以我决定关上书本一个都不记! 哼!用的时候再说吧。

然后每出现一个数组方法的时候看着都知道,就是用的时候还是模棱两可,又去查文档,这样是不能算作精通JavaScript的哦,于是玲珑好好的把每个方法认真复习、认真总结、认真的写了一篇博客(认真三连嘻嘻嘻)。

文章介绍

Array对象是js中的重点,面试题中经常出现,平时的项目中也少不了对数组的操作。一直一直都想对数组做一个整理。

这篇文章将会详细介绍数组的9+9+13个方法(包括es6中对数组扩展的一些新方法)。每个方法的传参要求,返回值,是否对原数组进行修改等等都在文中详细说明,大家可以收藏方便日后查阅。


在开始之前我们先对数组对象做一个简单的回顾。先来看看Array对象上的一些属性和方法。以及创建数组实例的方式。

如何创建一个数组实例

(一)使用 Array 构造函数: 在es5中创建一个数组有两种方法。

var arr1 = new Array(); //创建一个空数组
var arr2 = new Array(20); // 创建一个包含20项的数组
var arr3 = new Array("lily","lucy","Tom"); // 创建一个包含3个字符串的数组
复制代码

(二)使用数组字面量表示法:

var arr4 = []; //创建一个空数组
var arr5 = [20]; // 创建一个包含1项的数组
var arr6 = ["lily","lucy","Tom"]; // 创建一个包含3个字符串的数组
复制代码

Array对象的方法

Array对象自身有三个方法。

Array.isArray()

判断传入该方法的值是不是一个Array,返回值是布尔类型。

Array.isArray([1, 2, 3]);  
// true
Array.isArray({foo: 123}); 
// false
Array.isArray("foobar");   
// false
Array.isArray(undefined);  
// false
复制代码

(ES6扩展)Array.of()

定义:返回由所有参数值组成的数组,如果没有参数,就返回一个空数组。

目的:Array.of() 出现的目的是为了解决上述构造器因参数个数不同,导致的行为有差异的问题。

栗子:这里举例来说明of方法到底解决了什么问题。

Array();//[]即一个空数组
Array(3);//[ , , ]一个length==3的空数组
Array(1, 2, 3);//[1, 2, 3]

//of方法
let a = Array.of(3, 11, 8); // [3,11,8]
let b = Array.of(3); // [3]  b.length ==1

复制代码

我们new Array生成一个新数组的时候,会因为传递的参数不同而做出不同的解释,当参数大于等于2的时候会将参数作为数组的值,0或1个参数是指数组的length的值。length的值就是传入的一个参数的值,上面例子中传入一个参数3,那么这个数组的length==3.但是of方法就不会出现这种不统一的问题。

(ES6扩展)Array.from()

定义:用于将两类对象(类数组对象/可遍历对象)转为真正的数组(不改变原对象,返回新的数组)。

参数: 第一个参数(必需):要转化为真正数组的对象。 第二个参数(可选): 类似数组的map方法,对每个元素进行处理,将处理后的值放入返回的数组。 第三个参数(可选): 用来绑定this。

举例: 举一个类数组对象的例子。

   //一个类数组
    let arrLike = {
            '0': '玲珑',
            '1': '还是',
            '2': '一个',
            '3': '小白',
            length: 4
        }
        //es5中使用call改变this指向
        var arr1 = [].slice.call(arrLike);
        console.log(arr1);          //["玲珑", "还是", "一个", "小白"]
        
        //es6中的扩展from方法
        let arr2 = Array.from(arrLike);
        console.log(arr2);          //["玲珑", "还是", "一个", "小白"]
复制代码

有了from方法就不用call方法将类数组改成数组了。

Array对象的属性

  • Array对象上有三个属性,访问器属性返回Array的构造函数。
  • Array 构造函数的 length 属性,其值为1(注意该属性为静态属性,不是数组实例的 length 属性)。
  • prototype属性,Array.prototype 通过数组的原型对象可以为所有数组对象添加属性。所有数组实例都会从 Array.prototype 继承属性和方法。修改 Array 的原型会影响到所有的数组实例。 (就是说当创建一个数组实例的时候默认继承到了Array.prototype上的属性和方法。如果对原型上的方法进行修改,那么实例的数组会继承修改了的数组方法。)

还是不明白就看一个代码吧。我们自动在原型上面添加一个将字符数组转大写的方法myUcase。看看最后的结果截图。

let a = new Array('linglong', 'is', 'a', 'cool' , 'girl');
       
Array.prototype.myUcase = function(){
   for (i=0;i<this.length;i++){
   	this[i]=this[i].toUpperCase();
   }
}
a.myUcase();
console.log(a);
复制代码

看控制台的结果显示的是一个length==5的数组,翻译一下玲珑是一个酷酷的女孩

这下明白了吧,数组对象原型上增加了一个方法,那么实例出来的数组会继承这个方法。

前面的内容都是开胃菜,接下来是主要的知识点---Array原型对象上的方法。 如果你看不先去了,请记住数组对象Array上只有三个方法,其它的是像pop,fill,reserve等数组方法都是Array对象的prototype对象上的方法。


我们创建的数组能够拥有这些方法是因为通过数组的的__proto_ _属性,为了加深印象再看看下图黄色高亮部分_proto__指向的是Array原型对象,里面有许多我们平时说的数组方法,如下图所示,其中myUcase是我刚才人为添加的。

(这里只是部分方法不是所有哦)

一、改变原数组的方法

所谓的改变原数组就是不返回新数组,在原有的数组上进行改变。官方文档中也叫修改器方法。这些方法会改变调用它们的对象自身的值。一共有9个,一起来看看吧。

1、(ES6扩展)Array.prototype.copyWithin()

这个方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。 这个方法可以接受3个参数。 语法:

```
array.copyWithin(target, start = 0, end = this.length)
```
复制代码

参数:

三个参数都是数值,如果不是,会自动转为数值.

  • target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  • start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
  • end(可选):到该位置停止读取数据,默认等于数组长度。使用负数可从数组结尾处规定位置。

下面用代码说话加深理解。其实整个过程可以看成我们平时写word文档的时候选中复制,然后粘贴的过程。

const array1 = ['a', 'b', 'c', 'd', 'e'];

// copy to index 0 the element at index 3
console.log(array1.copyWithin(0, 3, 4));
// expected output: Array ["d", "b", "c", "d", "e"]

// copy to index 1 all elements from index 3 to the end
console.log(array1.copyWithin(1, 3));
// expected output: Array ["d", "d", "e", "d", "e"]
复制代码

第一次使用copyWithin方法传入的第一个参数0表示从第0位开始被复制;第二个参数3表示表示从第三号位置array1[3]=='d'开始选中;第三个参数4表示第四号位置之前选中结束。array1[4]=='e',也就是只选中d。

将选中的d复制到第0号位置。就得到了结果["d", "b", "c", "d", "e"]。

后面输出array1.copyWithin(1, 3)的结果先试着分析一下吧,第三个参数没有写就是默认参数当前数组的长度。

2、(ES6扩展)Array.prototype.fill()

将数组中指定区间的所有元素的值,都替换成某个固定的值。 定义: 使用给定值,填充一个数组。

参数:

  • 第一个元素(必须): 要填充数组的值

  • 第二个元素(可选): 填充的开始位置,默认值为0

  • 第三个元素(可选):填充的结束位置,默认是为this.length 用代码说话

    ['a', 'b', 'c'].fill(7)
    // [7, 7, 7]
    ['a', 'b', 'c'].fill(7, 1, 2)
    // ['a', 7, 'c']
    new Array(3).fill(7);
    //[7, 7, 7]
复制代码

需要注意的是第三个参数结束位置:是该位置之前结束,比如上面代码中的第二个例子中只是将1号位置填充为7.

3、Array.prototype.pop()

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

参数: 无。

用代码说话:

    let  a =  [1,2,3];
    let item = a.pop();  // 3
    console.log(a); // [1,2]

复制代码

注意:如果你在一个空数组上调用 pop(),它返回 undefined。

4、Array.prototype.push()

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

定义:push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

参数: item1, item2, ..., itemX ,要添加到数组末尾的元素

    let  a =  [1,2,3];
    let item = a.push('末尾');  // 4
    console.log(a); // [1,2,3,'末尾']
复制代码

5、Array.prototype.reverse()

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

定义: reverse() 方法用于颠倒数组中元素的顺序。

参数: 无。

const array1 = ['one', 'two', 'three'];
console.log('array1:', array1);
// expected output: "array1:" Array ["one", "two", "three"]

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

// Careful: reverse is destructive -- it changes the original array.
console.log('array1:', array1);
// expected output: "array1:" Array ["three", "two", "one"]
复制代码

6、Array.prototype.shift()

shift 方法移除索引为 0 的元素(即第一个元素),并返回被移除的元素,其他元素的索引值随之减 1。如果 length 属性的值为 0 (长度为 0),则返回 undefined。

Array.prototype.pop() 有着和 shift相似的行为, 但是是作用在数组的最后一个元素上的。

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

复制代码

7、Array.prototype.sort()

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

由于它取决于具体实现,因此无法保证排序的时间和空间复杂性。

语法:

arr.sort([compareFunction])
复制代码

参数:[compareFunction]

这个参数是一个比较函数,一个一个可选参数,用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的Unicode位点进行排序。

这个函数有两个参数,暂且叫做a,b。是在函数中用于比较的参数。

  • 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  • 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本);
  • 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
function compare(a, b) {
  if (a < b ) {           // 按某种排序标准进行比较, a 小于 b
    return -1;
  }
  if (a > b ) {
    return 1;
  }
  // a must be equal to b
  return 0;
}
复制代码

栗子:下面看一个简单的例子。用sort方法将[8,90]这个数组进行排序。分别看看sort方法传参和不传参的情况。

传参

var numbers = [80,9]; 
// numbers.sort();
 numbers.sort((a, b) => a - b); 
 console.log(numbers);
 //[9, 80]
复制代码

传入参数发现9-80小于0,那么9就在80的前面。

不传参

       var numbers = [80,9]; 
        numbers.sort();
// numbers.sort((a, b) => a - b); 
        console.log(numbers);
        //[80, 9]
复制代码

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

8、Array.prototype.splice()

该方法删除或替换现有元素或者添加新元素到数组中去。返回值是由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

语法:

array.splice(index,howmany,item1,.....,itemX)
复制代码

参数:

  • index:必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。
  • howmany:可选。要删除的项目数量。如果设置为 0,则不会删除项目。
  • item1, ..., itemX: 可选。向数组添加的新项目。

通过四个代码来看看传入不同的的参数返回值以及数组的变化情况

传入三个参数都为正

var myFish = ["angel", "clown", "mandarin", "sturgeon"];
var removed = myFish.splice(2, 0, "drum");

// 运算后的 myFish: ["angel", "clown", "drum", "mandarin", "sturgeon"]
// 被删除的元素: [], 没有元素被删除
复制代码

splice方法第一个参数2说明从myFish[2]这个位置进行相关操作,第二个参数是0说明不删除项目;第三个参数说明添加‘drum’第2号位置上。

传入两个参数为正

var myFish = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
var removed = myFish.splice(3, 1);

// 运算后的 myFish: ["angel", "clown", "drum", "sturgeon"]
// 被删除的元素: ["mandarin"]
复制代码

从第三号位置上删除1个元素,将三号元素删除。

传入2个参数第一个为负

从倒数第二个位置开始进行删除或者是添加操作(最后一个元素是-1位置),只有两个参数那么就是删除,并且删除元素的个数是2,所以从‘mandarin’开始往后删除2个元素就ok了。

传入一个参数

var myFish = ['angel', 'clown', 'mandarin', 'sturgeon'];
var removed = myFish.splice(2);

// 运算后的 myFish: ["angel", "clown"]
// 被删除的元素: ["mandarin", "sturgeon"]
复制代码

从第二个位置开始删除后面的所有元素。

9、Array.prototype.unshift()

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

语法:

arr.unshift(element1, ..., elementN)
复制代码

参数列表:有多个参数表示添加到开头的元素。

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

arr = [4,5,6]; // 重置数组
arr.unshift(1);
arr.unshift(2);
arr.unshift(3);
console.log(arr); // [3, 2, 1, 4, 5, 6]
复制代码

上述方法总结

上面九个都是修改器方法,有两个是es6新增的方法,copyWithin和fill方法。其他是es5就有的。

  • Array.prototype.copyWithin() 在数组内部,将一段元素序列拷贝到另一段元素序列上,覆盖原有的值。
  • Array.prototype.fill() 将数组中指定区间的所有元素的值,都替换成某个固定的值。
  • Array.prototype.pop() 删除数组的最后一个元素,并返回这个元素。
  • Array.prototype.push() 在数组的末尾增加一个或多个元素,并返回数组的新长度。
  • Array.prototype.reverse() 颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个。
  • Array.prototype.shift() 删除数组的第一个元素,并返回这个元素。
  • Array.prototype.sort() 对数组元素进行排序,并返回当前数组。
  • Array.prototype.splice() 在任意的位置给数组添加或删除任意个元素。
  • Array.prototype.unshift() 在数组的开头增加一个或多个元素,并返回数组的新长度。 访问方法

二、不改变原数组的方法

不改变原数组的方法也称作访问方法。就是返回一个新的数组或者其他,不会对原来的数组产生影响。

1、Array.prototype.concat()

这个方法用于合并两个或者多个数组,会返回合并后的新数组。该方法的参数可以是具体的值比如123,也可以是数组。

    let a = [1, 2, 3];
    let b = [4, 5, 6];
    //连接两个数组
    let newVal=a.concat(b); // [1,2,3,4,5,6]
    // 连接三个数组
    let c = [7, 8, 9]
    let newVal2 = a.concat(b, c); // [1,2,3,4,5,6,7,8,9]
    // 添加元素
    let newVal3 = a.concat('添加元素',b, c,'再加一个'); 
    // [1,2,3,"添加元素",4,5,6,7,8,9,"再加一个"]
   // 合并嵌套数组  会浅拷贝嵌套数组
   let d = [1,2 ];
   let f = [3,[4]];
   let newVal4 = d.concat(f); // [1,2,3,[4]]

复制代码

将值连接到数组

var alpha = ['a', 'b', 'c'];

var alphaNumeric = alpha.concat(1, [2, 3]);

console.log(alphaNumeric); 
// results in ['a', 'b', 'c', 1, 2, 3]
复制代码

将值连接到数组要与嵌套数组区别开来哦,值连接到数组是因为该方法的参数形式不只有一种,但最后都是将参数连接到数组中去。

(延伸)es6中的扩展运算符

这里是数组的连接,在es6中新增的扩展运算符...也能够将多个数组拼接。代码也更加简洁

    let a = [2, 3, 4, 5]
    let b = [ 4,...a, 4, 4]
    console.log(a,b); //  [2, 3, 4, 5] [4,2,3,4,5,4,4]
复制代码

2、Array.prototype.indexOf()

定义: 返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

语法:

    array.indexOf(searchElement,fromIndex)
复制代码

参数:

searchElement(必须):被查找的元素

fromIndex(可选):开始查找的位置(不能大于等于数组的长度,返回-1),接受负值,默认值为0.

var array = [2, 5, 9];
array.indexOf(2);     // 0
array.indexOf(7);     // -1
array.indexOf(9, 2);  // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0

var array = [2, 5, 9];
console.log(array.indexOf(2, -2));
//-1
复制代码

这里要说明的是:

第二个可选参数是负数的时候,如果是负数表示从末尾开始查找,查找的顺序也是从前往后的,比如上面的例子array.indexOf(2, -1)中的-1表示从数组的倒数第一位9这个值开始查找,从9这个位置a[-1]往后查找是否存在2,发现没有返回-1;array.indexOf(2, -3); // 0中的-3表示从倒数第三个位置(a[0])开始从前往后查询2,发现第0号位置刚好是2,结果返回0.

如果第二个参数是负数并且绝对值大于数组的长度那么整个数组都会被查询,相当于默认值0。看看下面的截图你就知道啦

第二个参数是-4或者-5的时候,其绝对值大于数组的长度3,相当于从头开始查找。

注意:indexOf方法有两个缺点

  • 不够语义化,理解起来比较麻烦
  • 内部使用严格===进行判断会导致NaN出错,但是includes方法就不会。
[NaN].indexOf(NaN)//-1
复制代码

3、(ES6扩展)Array.prototype.includes()

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

参数:

searchElement(必须): 被查找的元素

fromIndex(可选): 默认值为0,参数表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false。负值绝对值超过长数组度,重置从0开始搜索。

说明:

把includes方法放到indexOf方法的后面是因为首字母都是i嘛,当然不是。而是因为includes方法解决了前面indexOf的两个缺陷。返回值是布尔值而不是数组元素的位置索引号更加清晰,然后NaN的问题也不存在了。

[NaN].includes[NaN]//true
复制代码

4、Array.prototype.lastIndexOf()

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

语法:

arr.lastIndexOf(searchElement,fromIndex)
复制代码

参数: searchElement(必须): 被查找的元素

fromIndex(可选): 逆向查找开始位置,默认值数组的长度-1,即查找整个数组。 关于fromIndex有三个规则:

  • 正值。如果该值大于或等于数组的长度,则整个数组会被查找。

  • 负值。将其视为从数组末尾向前的偏移。(比如-2,从数组最后第二个元素开始往前查找)

  • 负值。其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

 let a=['OB',4,'Koro1',1,2,'Koro1',3,4,5,'Koro1']; // 数组长度为10
 // let b=a.lastIndexOf('Koro1',4); // 从下标4开始往前找 返回下标2
 // let b=a.lastIndexOf('Koro1',100); //  大于或数组的长度 查找整个数组 返回9
 // let b=a.lastIndexOf('Koro1',-11); // -1 数组不会被查找
 let b=a.lastIndexOf('Koro1',-9); // 从第二个元素4往前查找,没有找到 返回-1
复制代码

5、Array.prototype.join()

join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。在强调一下返回的是字符串字符串字符串;如果 arr.length 为0,则返回空字符串。

语法:

 array.join(str)
复制代码

参数: str(可选): 指定要使用的分隔符,默认使用逗号作为分隔符。如果参数是空字符串(""),则所有元素之间都没有任何字符。

var a = ['Wind', 'Rain', 'Fire'];
var myVar1 = a.join();      // myVar1的值变为"Wind,Rain,Fire"
var myVar2 = a.join(', ');  // myVar2的值变为"Wind, Rain, Fire"
var myVar3 = a.join(' + '); // myVar3的值变为"Wind + Rain + Fire"
var myVar4 = a.join('');    // myVar4的值变为"WindRainFire"
复制代码

6、Array.prototype.slice()

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

语法:

    array.slice(begin, end);
复制代码

参数:

begin(包括改位置,可选参数):

  • 拷贝的起始位置索引,如果是负数表示从倒数第几位开始拷贝,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
  • 默认值是0。
  • 如果值大于数组的长度会返回一个空数组。

end(不包括该位置,可选参数)

  • 拷贝的结束位置,默认值是数组的长度
  • 负数表示从倒数第几位开始,slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
var fruits = ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango'];
var citrus = fruits.slice(1, 3);

// fruits contains ['Banana', 'Orange', 'Lemon', 'Apple', 'Mango']
// citrus contains ['Orange','Lemon']
复制代码

浅拷贝举例

slice是对数组的浅拷贝,拷贝的时候有以下规则

  • 如果拷贝的元素是普通数据类型,向Number、String那么修改新数组对原数组没有影响。
    let a= ['hello','world'];
    let b=a.slice(0,1); // ['hello']
    a[0]='改变原数组';
    console.log(a,b); // ['改变原数组','world'] ['hello']
    b[0]='改变拷贝的数组';
    console.log(a,b); // ['改变原数组','world'] ['改变拷贝的数组']

复制代码
  • 如果拷贝的元素本身就是引用类型,比如对象和数组那么改变新数组或者原数组都会产生影响。这是因为slice拷贝的是对象的引用,两个数组的元素都是引用的同一个对象,如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
let arr = [{name:'LINGLONG', explain:'我是原数组'}];
let arr2 = arr.slice();
// console.log(arr2);
arr[0].explain = '修改的原数组';
console.log(arr2); //[{name: "LINGLONG", explain: "修改的原数组"}]
复制代码

7、Array.prototype.toSource()

遮蔽了原型链上的 Object.prototype.toSource() 方法。返回一个字符串,代表该数组的源代码.

var alpha = new Array("a", "b", "c");
alpha.toSource();   //返回["a", "b", "c"]
复制代码

该方法是非标准的,尽量不要在开发环境使用。并且只有火狐的安卓浏览器兼容,在谷歌浏览器中是找不到这个方法的。

谷歌浏览器下没有这个方法

8、Array.prototype.toString()

  • 返回一个由所有数组元素组合而成的字符串。遮蔽了原型链上的Object.prototype.toString() 方法.

我们先看看Object上的toString方法,大家都知道他能够判断一个值的类型。

var toString = Object.prototype.toString;

toString.call(new Date); // [object Date]
toString.call(new String); // [object String]
toString.call(Math); // [object Math]

//Since JavaScript 1.8.5
toString.call(undefined); // [object Undefined]
toString.call(null); // [object Null]
复制代码

而数组的toString返回的是一个字符串,但是该方法的效果和join方法不传参数一样,都是用于数组转字符串的,但是与join方法相比没有优势,也不能自定义字符串的分隔符,因此不推荐使用。

const array1 = [1, 2, 'a', '1a'];
console.log(array1.toString());
// expected output: "1,2,a,1a"
复制代码
  • 当一个数组被作为文本值或者进行字符串连接操作时,将会自动调用其 toString 方法。
   let b= [ 'toString','演示'].toString(); // toString,演示
   let a= ['调用toString','连接在我后面']+'啦啦啦'; // 调用toString,连接在我后面啦啦啦
复制代码

9、Array.prototype.toLocaleString()

这是不改变原数组的最后一个方法啦。该方法返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。

语法:

arr.toLocaleString([locales[,options]]);
复制代码

参数:

  • locals,可选参数: 带有BCP 47语言标记的字符串或字符串数组。关于这里我认为是和html中的lang属性规定元素的代码语言类似吧
  • options,可选参数: options 参数必须是一个对象,其属性值在不同的构造函数和方法中会有所变化。 对于数字Number.prototype.toLocaleString(),对于日期Date.prototype.toLocaleString().

关于参数的问题建议查阅官方文档Array.prototype.toLocaleString()

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
复制代码

上例子中arr1数字的1是数字,使用Number.prototype.toLocaleString();日期是Date.prototype.toLocaleString().

三、数组的遍历方法(不改变原数组)

1、Array.prototype.forEach()

语法:

array.forEach(function(currentValue, index, arr), thisValue)
复制代码

参数:

  • function(currentValue, index, arr):数组中每个元素都能够执行的函数,这个函数有三个参数currentValue, index, arr。
    // 回调函数的参数
    1. currentValue(必须),数组当前元素的值
    2. index(可选), 当前元素的索引值
    3. arr(可选),数组对象本身
复制代码
  • thisValue:是一个可选参数,当执行回调函数的时候表示this的值,也就是回调函数内的this指向thisValue。
const array1 = ['a', 'b', 'c'];

array1.forEach(element => console.log(element));
复制代码

稀疏数组的forEach

const arraySparse = [1,3,,7];
let numCallbackRuns = 0;

arraySparse.forEach(function(element){
  console.log(element);
  numCallbackRuns++;
});

console.log("numCallbackRuns: ", numCallbackRuns);

// 1
// 3
// 7
// numCallbackRuns: 3
复制代码

arraySpare就是一个稀疏数组,a[2]是空,那么该方法不会对这个空数组元素有效,因而回调函数只执行3次。

如果数组在迭代时被修改了,则其他元素会被跳过。

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

当数组元素是two的时候就会移除掉0号位置的数组元素,shift方法会改变原数组,此时的words==[ 'two', 'three', 'four']。前面的两个位置的元素遍历过了,那么three就被跳过,遍历four这个元素。

上面这个例子我让word==‘one’的时候移除第一个元素。原数组变成['two', 'three', 'four'],第一个元素被遍历了,此时two在第一个元素的位置(0号位置)所以two被跳过。

2、Array.prototype.every()

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

若收到一个空数组,那么任何操作都返回true

语法:

    array.every(function(currentValue, index, arr), thisValue)
复制代码

参数:同forEach

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

复制代码

说明:如果回调函数的每一次返回都为 truthy 值,返回 true ,否则返回 false。

3、Array.prototype.some()

这个方法和前面的every方法对立,every方法规定每个元素都能够通过指定函数的测试,some方法只用一个元素能够通过指定函数的测试。

如果用一个空数组进行测试,在任何情况下它返回的都是false

语法参数都比较类似

arr.some(callback(element[, index[, array]])[, thisArg])
复制代码

用代码说话:

function isBiggerThan10(element, index, array) {
  return element > 10;
}

[2, 5, 8, 1, 4].some(isBiggerThan10);  // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
复制代码

4、Array.prototype.filter()

filter本身就有过滤的意思,该方法返回一个新数组,这个数组的元素就是听过测试的元素。

语法:

var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])
复制代码

参数同some等方法一样,一个必填的函数参数和一个可选的thisValue。

用代码说话,过滤出大于10的元素。

function isBigEnough(element) {
  return element >= 10;
}
var filtered = [12, 5, 8, 130, 44].filter(isBigEnough);
// filtered is [12, 130, 44] 
复制代码

5、Array.prototype.map()

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

var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt);
// roots的值为[1, 2, 3], numbers的值仍为[1, 4, 9]
复制代码

6、Array.prototype.reduce()(方法参数不一般)

这个方法返回的是一个函数处理的累计结果。数组中的每个元素执行一个由您提供的reducer函数(升序执行),将其结果汇总为单个返回值。

语法:

    array.reduce(callback(total, currentValue, currentIndex, arr), initialValue)
复制代码

参数:

  • callback()
    • total(必须),初始值或者上一次回调函数返回的值。如果initialValue存在初始值就是initialValue,如果不存在就是数组的第一个元素。
    • currentValue当前值,如果initialValue不存在就是下标为1的元素(第二个元素);如果initialValue存在就是下标为0的元素。
    • currentIndex可选参数,数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
    • arr:可选参数,调用reduce方法的数组。
  • initialValue:可选参数,作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

用代码说话:传入两个参数和一个参数的结果不同。

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开始。

7、Array.prototype.reduceRight()

这个方法同reduce方法唯一的区别是从数组的右边开始返回函数处理的累计结果,语法参数都一样,直接看代码。

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]
复制代码

8、9(ES6扩展)find()和findIndex()

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

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

区别:这两个方法返回的都要满足测试的函数。但是一个是返回符合条件元素的值,一个是返回符合条件元素的下标。

语法:

    let new_array = arr.find(function(currentValue, index, arr), thisArg)
    let new_array = arr.findIndex(function(currentValue, index, arr), thisArg)
复制代码

两者参数的意义和前面的map那四个遍历方法一样。

用代码说话

//find()
const array1 = [5, 12, 8, 130, 44];

const found = array1.find(element => element > 10);

console.log(found);
// expected output: 12

//findIndex
const array1 = [5, 12, 8, 130, 44];

const isLargeNumber = (element) => element > 13;

console.log(array1.findIndex(isLargeNumber));
// expected output: 3
复制代码

与indexOf的比较

  • 这两个方法都可以识别NaN,弥补了indexOf的不足.原理是借助了Object.is方法。
        // 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
复制代码
  • 如果你需要找到一个元素的位置或者一个元素是否存在于数组中,使用Array.prototype.indexOf() 或 Array.prototype.includes()。

10、11、12(ES6扩展)数组实例的entries()、keys()、values()

它们都返回一个遍历器对象,可以用for...of循环进行遍历,唯一的区别是

  • keys()是对键名的遍历
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

复制代码
  • values()是对键值的遍历
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"

复制代码
  • entries()是对键值对的遍历。
const array1 = ['a', 'b', 'c'];

const iterator1 = array1.entries();

console.log(iterator1.next().value);
// expected output: Array [0, "a"]

console.log(iterator1.next().value);
// expected output: Array [1, "b"]
复制代码

13、Array.prototype[@@iterator ]

这是这篇文章的最后一个方法,在阮一峰的es6标准入门一书中并没有提及。但是官方文档整理了这是es6新增的方法,这里还是说一下吧。

这个方法默认情况下,与 values() 返回值相同, arr[Symbol.iterator] 则会返回 values() 函数。

语法:arr[Symbol.iterator]()
复制代码

用代码说话:

function logIterable(it) {
  var iterator = it[Symbol.iterator]();
  // 浏览器必须支持 for...of 循环
  for (let letter of iterator) {
      console.log(letter);
  }
}

// Array
logIterable(['a', 'b', 'c']);
// a
// b
// c

// string
logIterable('abc'); 
// a
// b
// c
复制代码

文章的末尾

一些参考文章

一些悄悄话

呜呜呜,数组的方法终于干完了。

如果你看完了那真的很不错,我也是断断续续写了两天。

看完了之后是否是真理解记住了呢?如果没有看懂那就去再认真看一遍吧。看完之后就会发现自己依旧还是没有记住...

哼我再也不看了!

这其实是很正常的事情,光看是记不住的,还是要多用多实践,斯宾浩的遗忘曲线告诉我们即使记住了还是会遗忘的,玲珑也会经常复习一下自己写过的文章。

所以大家要多实(收)践(藏)总(此)结(文),您的鼓励也是我继续前行的动力~

如果有错误的地方还请您在评论区指出。 下篇文章见...


文章分类
前端
文章标签