会改变自身值的数组方法(9种)

数组原型提供的方法非常之多,主要分为三种,一种是会改变自身值的,一种是不会改变自身值的,另外一种是遍历方法。

基于ES6,改变自身值的方法一共有9个,分别为pop、push、reverse、shift、sort、splice、unshift,以及两个ES6新增的方法copyWithin 和 fill。

一、栈方法:

数组对象可以像栈一样,也就是一种限制插入和删除项的数据结构。栈是一种后进先出(LIFO, Last-In-First-Out)的结构,也就是最近添加的项先被删除。数据项的插入(称为推入,push)和删除(称为弹出,pop)只在栈的一个地方发生,即栈顶。ECMAScript数组提供了push()和pop()方法,以实现类似栈的行为。

1、pop

(1)pop()方法删除一个数组中的最后一个元素,同时减少数组的length值, 并且返回这个元素。如果是栈的话,这个过程就是栈顶弹出。

image.png

(2) 由于设计上的巧妙,pop方法可以应用在类数组对象上,即 鸭式辨型. 如下:

image.png

(3) 但如果类数组对象不具有length属性,那么该对象将被创建length属性,length值为0。如下:

var o = {0:"cat", 1:"dog", 2:"cow", 3:"chicken", 4:"mouse"};
var item = Array.prototype.pop.call(o);
复制代码

image.png

2、push

push()方法添加一个或者多个元素到数组末尾,并且返回数组新的长度。如果是栈的话,这个过程就是栈顶压入。

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

image.png

(2) 同pop方法一样,push方法也可以应用到类数组对象上,如果length不能被转成一个数值或者不存在length属性时,则插入的元素索引为0,且length属性不存在时,将会创建它。

var o = {0:"football", 1:"basketball"};
var i = Array.prototype.push.call(o, "golfball");
复制代码

image.png (3)实际上,push方法是根据length属性来决定从哪里开始插入给定的值。

利用push根据length属性插入元素这个特点,可以实现数组的合并,并且合并后第二个数组合并到第一个数组中,即第一个数组变成新的数组,第二个数组不会发生改变。

例子如下:

image.png

二、排序方法:

数组有两个方法可以用来对元素重新排序:reverse()和sort()。顾名思义,reverse()方法就是将数组元素反向排列。

1、sort

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

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

  • comparefn是可选的,如果省略。
  • 数组元素将按照各自转换为字符串的Unicode(万国码)位点顺序排序。
  • 默认情况下,sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。为此,

因此,sort()会在每一项上调用String()转型函数,然后比较字符串来决定顺序。即使数组的元素都是数值,也会先把数组转换为字符串再比较、排序。比如

image.png

一开始数组中数值的顺序是正确的,但调用sort()会按照这些数值的字符串形式重新排序。因此,即使5小于10,但字符串"10"在字符串"5"的前头,所以10还是会排到5前面。很明显,这在多数情况下都不是最合适的。为此,sort()方法可以接收一个比较函数,用于判断哪个值应该排在前面。

comparefn比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相等,就返回0;如果第一个参数应该排在第二个参数后面,就返回正值。下面是使用简单比较函数的一个例子:

function compare (v1,v2){
    if(v1<v2){
        return -1;
    } else if (v1>v2) {
        return 1;
    } else {
        return 0;
    }
}
复制代码

在给sort()方法传入比较函数后,数组中的数值在排序后保持了正确的顺序

image.png

这个比较函数也可以简写为:

values.sort((a,b)=> a<b ? 1: a>b ? -1 : 0);
复制代码

(2)如果数组的元素是数值,或者是其valueOf()方法返回数值的对象(如Date对象),这个比较函数可以直接写为:

function compare (v1,v2){
    return v2 - v1;
}
复制代码

(3)同上, sort一样受益于鸭式辨型,比如:

image.png

注意:使用sort的鸭式辨型特性时,若类数组对象不具有length属性,它并不会进行排序,也不会为其添加length属性。

image.png

2、reverse

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

(1) 语法:arr.reverse()

var array = [1,2,3,4,5];
var array2 = array.reverse();
console.log(array); // [5,4,3,2,1]
console.log(array2===array); // true
复制代码

同上,reverse 也是鸭式辨型的受益者,颠倒元素的范围受length属性制约。如下:

image.png

如果 length 属性小于1 或者 length 属性不为数值,那么原类数组对象将没有变化。即使 length 属性不存在,该对象也不会去创建 length 属性。特别的是,当 length 属性较大时,类数组对象的『索引』会尽可能的向 length 看齐。

image.png

三、队列方法:

就像栈是以LIFO形式限制访问的数据结构一样,队列以先进先出(FIFO,First-In-First-Out)形式限制访问。队列在列表末尾添加数据,但从列表开头获取数据。因为有了在数据末尾添加数据的push()方法,所以要模拟队列就差一个从数组开头取得数据的方法了。这个数组方法叫shift(),它会删除数组的第一项并返回它,然后数组长度减1。使用shift()和push(),可以把数组当成队列来使用:

1、shift

shift()方法删除数组的第一个元素,长度会减1,并返回这个元素。类似于队列的队尾弹出最后一个元素。

(1) 语法:arr.shift()

image.png

(2) 同样受益于鸭式辨型,对于类数组对象,shift仍然能够处理。如下:

image.png

(3)如果类数组对象length属性不存在,将添加length属性,并初始化为0.如下:

image.png

2、push

在队列最后插进一个新元素,与进栈的push()用法一致。

image.png

四、操作方法:

1、splice

splice()方法用新元素替换旧元素的方式来修改数组。它是一个常用的方法,复杂的数组操作场景通常都会有它的身影,特别是需要维持原数组引用时,就地删除或者新增元素,splice是最适合的。

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

start: 指定从哪一位开始修改内容。如果超过了数组长度,则从数组末尾开始添加内容;如果是负值,则其指定的索引位置等同于 length+start (length为数组的长度),表示从数组末尾开始的第 -start 位。

deleteCount: 指定要删除的元素个数,若等于0,则不删除。这种情况下,至少应该添加一位新元素,若大于start之后的元素总和,则start及之后的元素都将被删除。

itemN: 指定新增的元素,如果缺省,则该方法只删除数组元素。

返回值: 由原数组中被删除元素组成的数组,如果没有删除,则返回一个空数组。

❑ 删除。需要给splice()传2个参数:要删除的第一个元素的位置和要删除的元素数量。可以从数组中删除任意多个元素,比如splice(0, 2)会删除前两个元素。

❑ 插入。需要给splice()传3个参数:开始位置、0(要删除的元素数量)和要插入的元素,可以在数组中指定的位置插入元素。第三个参数之后还可以传第四个、第五个参数,乃至任意多个要插入的元素。比如,splice(2, 0,"red", "green")会从数组位置2开始插入字符串"red"和"green"。

❑ 替换。splice()在删除元素的同时可以在指定位置插入新元素,同样要传入3个参数:开始位置、要删除元素的数量和要插入的任意多个元素。要插入的元素数量不一定跟删除的元素数量一致。比如,splice(2, 1, "red","green")会在位置2删除一个元素,然后从该位置开始向数组中插入"red"和"green"。

splice()方法始终返回这样一个数组,它包含从数组中被删除的元素(如果没有删除元素,则返回空数组)。

如下例子:

// 删除

var array = ["apple","boy"];

var splices = array.splice(1,1);

console.log(array); // ["apple"]

console.log(splices); // ["boy"] ,可见是从数组下标为1的元素开始删除,并且删除一个元素,由于itemN缺省,故此时该方法只删除元素




// 新增

array = ["apple","boy"];

splices = array.splice(2,1,"cat");

console.log(array); // ["apple", "boy", "cat"]

console.log(splices); // [], 可见由于start超过数组长度,此时从数组末尾开始添加元素,并且原数组不会发生删除行为



//替换

array = ["apple","boy"];

splices = array.splice(-2,1,"cat");

console.log(array); // ["cat", "boy"]

console.log(splices); // ["apple"], 可见当start为负值时,是从数组末尾开始的第-start位开始删除,删除一个元素,并且从此处插入了一个元素



array = ["apple","boy"];

splices = array.splice(-3,1,"cat");

console.log(array); // ["cat", "boy"]

console.log(splices); // ["apple"], 可见即使-start超出数组长度,数组默认从首位开始删除



array = ["apple","boy"];

splices = array.splice(0,3,"cat");

console.log(array); // ["cat"]

console.log(splices); // ["apple", "boy"], 可见当deleteCount大于数组start之后的元素总和时,start及之后的元素都将被删除
复制代码

(2)同上,splice一样受益于鸭式辨型,

image.png

由此可见,对象o删除了一个属性,并且length-1,并且返回的依旧是被删除的属性。

(3)如果需要删除数组中一个已经存在的元素,可参考如下:

var array = ['a', 'b', 'c'];
array.splice(array.indexOf('b'), 1);
复制代码

2、unshift

unshift() 方法:顾名思义,unshift()就是执行跟shift()相反的操作:在数组开头添加任意多个值,然后返回新的数组长度。通过使用unshift()和pop(),可以在相反方向上模拟队列,即在数组开头添加新数据,在数组末尾取得数据。

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

image.png

length为5说明,这两个属性成功的插入到数组中了。是队列的头部插进的。

(2)如果传进去的是一个数组,则:

image.png

(3)unshift也受益于鸭式辨型,例子如下:

image.png

注意:如果类数组对象不指定length属性,则会返回{0: 'gray', 1: 'green', 2: 'blue', length: 1},unshift会认为数组长度为0,此时将从对象下标为0的位置开始插入,相应位置属性将被替换,此时初始化类数组对象的length属性为插入元素个数。

var o2 = {0:"red", 1:"green", 2:"blue"};
var length = Array.prototype.unshift.call(o2,"gray", "orange", "black");

复制代码

image.png

varo2 = {0:"red", 1:"green", 2:"blue"};

var length = Array.prototype.unshift.call(o2,"gray", "orange", "black");
复制代码

image.png

如果插进去的属性个数多了,好像原来的数据就会消失????

五、复制和填充的方法:

ES6新增了两个方法:批量复制方法copyWithin(),以及填充数组方法fill()

1、copyWithin

copyWithin() 方法基于ECMAScript 2015(ES6)规范,用于数组内元素之间的替换,即替换元素和被替换元素均是数组内的元素。

(1)语法:arr.copyWithin(target, start[, end = this.length])

target: 开始插入的位置的索引(下标是从0开始数)【必选】;

start:要开始复制的那个元素的索引;

end:要结束复制元素的索引。

返回值就是复制后的新数组。

// start~end 的复制元素索引包括start索引的位置,不包括end索引的位置

var arr=[1,2,3,4,5,6];

arr.copyWithin(1,2,3); // 复制索引2~3位置上的元素,其实就是复制索引2位置的元素3,插入到索引为1的位置。
复制代码

结果如下:

image.png

// 如果只有一个参数,即该参数为插入的位置的索引,那就是默认从索引0开始复制到之后一个

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

array.copyWithin(2); // 复制从索引0开始的所有元素,从索引2的位置开始按顺序插入,
复制代码

结果如下:

image.png

// 如果有两个参数,第一个参数为插入位置的索引,第二个参数为开始复制位置的索引

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

array.copyWithin(1,3); // 复制从索引3开始以及后面的元素,插入到索引为1的位置以及后面
复制代码

结果如下:

image.png

// 如果参数为负数,则从最后一个元素倒着数,或者加上原来数组的长度,然后按照正数的规则来计算

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

array.copyWithin(-1,-3);// 复制从索引为(-3+5)2的位置开始以及后面的元素插入到索引为(-1+5)4的位置。
复制代码

结果如下:

image.png

(2)同上,copyWithin一样受益于鸭式辨型,例如:

image.png

注意:如果类数组对象不指定length属性,copyWithin会认为数组的长度为undefined。

image.png

2、fill

fill()同样用于数组元素替换,但与copyWithin略有不同,它主要用于将数组指定区间内的元素替换为某个值。

(1)语法:arr.fill(value, start[, end = this.length])

value:是要替换为的元素值,start是开始位置的索引,end是结束位置的索引

【注意】start~end之间的包括索引start,不包括索引end

(2)用法与copyWithin()基本一样。例子如下:

image.png

(3)同上,fill 一样受益于鸭式辨型,例如:

image.png

注意:如果类数组对象不指定length属性,copyWithin会认为数组的长度为undefined。

image.png

猜你喜欢

转载自juejin.im/post/7039633567898402846