会改变自身值的9个数组方法

目录

一、栈方法:

1、pop

2、push

二、排序方法:

1、sort

2、reverse

三、队列方法:

1、shift

2、push

四、操作方法:

1、splice

2、unshift

五、复制和填充的方法:

1、copyWithin

2、fill


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

基于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值, 并且返回这个元素。如果是栈的话,这个过程就是栈顶弹出。

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

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

var o = {0:"cat", 1:"dog", 2:"cow", 3:"chicken", 4:"mouse"};

var item = Array.prototype.pop.call(o);

2、push

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

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

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

var o = {0:"football", 1:"basketball"};

var i = Array.prototype.push.call(o, "golfball");

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

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

例子如下:

二、排序方法:

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

1、sort

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

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

  • comparefn是可选的,如果省略。

  • 数组元素将按照各自转换为字符串的Unicode(万国码)位点顺序排序。

  • 默认情况下,sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。为此,

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

一开始数组中数值的顺序是正确的,但调用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()方法传入比较函数后,数组中的数值在排序后保持了正确的顺序

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

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

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

function compare (v1,v2){

return v2 - v1;

}

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

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

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属性制约。如下:

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

三、队列方法:

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

1、shift

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

(1) 语法:arr.shift()

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

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

2、push

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

四、操作方法:

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一样受益于鸭式辨型,

由此可见,对象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)

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

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

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

注意:如果类数组对象不指定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");

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

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

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

五、复制和填充的方法:

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的位置。

结果如下:

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

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

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

结果如下:

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

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

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

结果如下:

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

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

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

结果如下:

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

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

2、fill

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

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

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

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

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

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
顺序结构   •顺序结构就是程序从上到下一行一行地执行,中间没有任何判断和跳转。   •如果main方法多行代码之间没有任何流程控制,则程序总是从上向下依次执行,排在前面的代码先执行,排在后 面的代码后执行。 分支结构 •Java提供了两种常见的分支控制结构: –if语句:使用布尔表达式或布尔作为分支条件来进行分支控制。 –switch语句:用于对多个整型进行匹配,从而实现分支控制。 if条件语句 •if条件语句的3种形式: • ▲ if (logic expression) { statements…} • ▲ if (logic expression) { statements…} • else { statements…} • ▲ if (logic expression) { statements…} • else if (logic expression) { statements…} • …//可以有0个或多个else if 语句 • else { statements…} //最后的else语句也可以省略 •注意:if、else、else if 后条件执行体要么是一个花括号括起来的语句块,则这个语句块整体作为条件执行体; 要么是以分号为结束符的一行语句,甚至可能是一个空语句(空语句就是一个分号)。 If语句常见的错误 •如果if、else、else if后的执行体只有一行语句时,则可以省略花括号,但我们最好不要省略花括号,因为保留花 括号有更好的可读性,且还可以减少发生错误的可能。 •对于if 语句,还有一个很容易出现的逻辑错误,这个逻辑错误并不属于语法问题,但引起错误的可能性更大。如 后面程序TestIfError.java我们想打印的是中年人,但打印出来的结果是青年人。 • 对于任何的if else 语句,表面上看起来else后没有任何条件,或者else if后只有一个条件,但这不是真相:因为 else的含义是“否则”,else本身就是一个条件!else 的隐含条件就是对前面条件取反。 switch分支语句 •可以省略case后代码块的花括号 ◆使用break;语句,防止case穿透 ◆default可以省略,但不推荐省略 ◆switch语句中控制表达式的类型只能是byte、short、char、int、String(JDK7新增)和枚举 Switch语句容易导致的错误 •switch语句后的expression表达式的数据类型只能是byte、short、char、int、String类型和枚举; •小心省略了case后代码块的break;时所引入的陷阱。 循环结构 •Java支持3种基本的循环语句: –while 循环语句 –do while 循环语句 – for 循环语句 while & do while 循环语句 ★ while 循环的语法格式如下: [init_statements] while (test_expression) { statements; [iteration_statements] } ★ 执行过程:先判断逻辑表达式的,若为true 则执行其后面的语句,然后再次判断条件并反复 执行,直到条件不成立为止。 ★ do while 循环的语法格式如下: [init_statements] do {   statements; [iteration_statements] }while (test_expression); ※注意:do while 循环的循环条件必须有一个分 号,这个分号表明循环结束。 ★ 执行过程:先执行语句,再判断逻辑表达式的 ,若为true,再执行语句,否则结束循环 控制循环条件 •使用循环时,一定要保证循环条件有变成false的时候,如果循环条件永远为true,那就是死循环。使用while循   环时还有一个陷阱,while循环条件后紧跟一个分号。 •do while 循环语句里,即使test_expression循环条件的开始是假,do while循环也执行循环体。因此,   do while循环的循环体至少执行一次。 本文原创作者:pipi-changing 本文原创出处:http://www.cnblogs.com/pipi-changing/ for 循环语句 •for ([inint_statements] ; [test_expression] ; [iteration_statements]){ statements } •★ 执行过程:首先计算表达式1,即init_statements,接着执行表达式2,即test_expression,若表达式2的 为true,则执行语句(statements),接着执行表达式3,即iteration_statements,再判断表达式2的; 依次重复下去,直到表达式的=false,则结束for循环。因此,for循环的循环条件(表达式2)比循环体(语 句)要多执行一次。 •注意:for循环的循环迭代语句并没有与循环体放在一起,因此即使在执行循环体时遇到continue语句结束本次 循环,循环迭代语句一样得到执行。 for循环指定多个初始化语句 •for 循环允许同时指定多个初始化语句,循环条件也可以是一个包含逻辑运算符的表达式。但只能有一个声明语   句,因此如果需要在初始化表达式中声明多个变量,那么这些变量应该有相同的数据类型。 •初学者使用for循环时也容易犯一个错误,他们以为只要在for后的括号内控制了循环循环迭代语句就万无一失,   但实际情况则不是这样的。 for循环的分号 •for 循环圆括号中只有两个分号是必须的,初始化语句、循环条件、迭代语句部分都可以省略,如果省略了循环   条件,则这个循环条件默认是true,将产生一个死循环。 •使用for循环时,还可以把初始化条件定义在循环体之外,把循环迭代语句放在循环体内,这种做法将非常类似前   面的while循环。 嵌套循环 •各种基本类型的循环都可以作为外层循环,各种基本类型的循环也可以作为内层循环。 •假设外层循环的循环次数为n次,内层循环的循环次数为m次,那么内层循环的循环体实际上需要执行n*m次。 •实际上,嵌套循环不仅可以是两层嵌套,还可以是三层嵌套,四层嵌套…… break语句 •break用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到break,系统将完全结束循   环,开始执行循环之后的代码。 •break不仅可以结束其所在的循环,还可结束其外层循环。此时需要在break后紧跟一个标签,这个标签用于标 识一个外层循环。Java中的标签就是一个紧跟着英文冒号(:)的标识符。且它必须放在循环语句之前才有作用。 continue 语句 •continue的功能和break有点类似,区别是continue只是中止本次循环,接着开始下一次循环。而break则是 完全中止循环。 return语句 • return关键字并不是专门用于跳出循环的,return的功能是结束一个方法。 •一旦在循环体内执行到一个return语句,return语句将结束该方法,循环自然也随之结束。与continue和 break不同的是,return直接结束整个方法,不管这个return处于多少层循环之内。 数组类型 •在任何已有类型后加上方括号[ ],又变成一种新类型,这种类型统称为数组类型,所有的数组类型又称为引用类 型,所以又称引用类型。 •Java的数组要求所有数组元素具有相同的数据类型。因此,在一个数组中,数组元素的类型是唯一的,即一个数 组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。 •一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。即使把某个数组 元素的数据清空,但它所占的空间依然被保留,依然属于该数组数组的长度依然不变。 •Java的数组既可以存储基本类型的数据,也可以存储引用类型的数据。 •得指出的是:数组也是一种数据类型,它本身是一种引用类型。 定义数组 •Java语言支持两种语法格式来定义数组: –type[ ] arrayName; –type arrayName[ ]; •对于这两种语法格式,一般推荐使用第一种格式。因为第一种格式不仅具有更好的语意,也具有更好的可读性。 •数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是定义了一个指   针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。 •※注意:定义数组时不能指定数组的长度。 数组的初始化 •静态初始化:初始化时由程序员显式指定每个数组的初始,由系统决定需要的数组长度。 •动态初始化:初始化时程序员指定数组长度,由系统为数组元素分配初始 动态初始化 •arrayName = new type[ length]; 在上面的语法中,需要指定一个int整型的length参数,这个参数指定了数组的长度,也就是可以容纳数组元素的 个数。 使用数组数组最常用的用法就是访问数组元素,包括对数组元素赋和访问数组元素的,访问数组元素是通过在数组引用变 量后紧跟一个方括号([ ]),方括号里是数组元素的索引。 •Java语言的数组索引是从0开始的,也就是说,第一个数组元素的索引为0,最后一个数组元素的索引为数组长度 减1。 •如果访问数组元素进指定的索引小于0,或者大于等于数组的长度,编译程序不出现任何错误,但运行时出现异 常:java.lang.ArrayIndexOutOfBoundsException:2(数组索引越界异常),在这个异常提示信息后有一个int 整数,这个整数就是程序员试图访问的数组索引。 •所有数组都提供了一个length属性,通过这个属性可以访问到数组的长度,一旦获得了数组的长度后,就可以通过循 环来遍历该数组的每个数组元素。 JDK1.5 提供了foreach循环 •从JDK1.5 之后,Java提供了一种更简单的循环:foreach循环,这种循环遍历数组和集合更加简洁。使用 foreach循环遍历数组和集合元素时,无须获得数组和集合长度,无须根据索引来访问数组元素和集合元素, foreach循环自动遍历数组和集合的每个元素。 •当使用foreach循环来迭代输出数组元素或集合时,通常不要对循环变量进行赋,虽然这种赋在语法上是允 许的,但没有太大的意义,而且极容易引起错误。 深入数组数组元素和数组变量在内存里是分开存放的。 实际的数组元素是存储在堆(heap)内存中;数组引用变量是一个引用类型的变量,被存储在栈(stack)内存 中。 •如果堆内存中数组不再有任何引用变量指向自己,则这个数组将成为垃圾,该数组所占的内存将被系统的垃圾 回收机制回收。因此,为了让垃圾回收机制回收一个数组所占的内存空间,则可以将该数组变量赋为null,也就切 断了数组引用变量和实际数组之间的引用关系,实际数组也就成了垃圾。 数组长度不可变 •只要类型相互兼容,可以让一个数组变量指向另一个实际的数组,这种操作产生数组的长度可变的错觉。 •但由于数组变量整体赋导致的数组的长度可以改变,只是一个假相。 基本类型数组的初始化 •对于基本类型数组而言,数组元素的直接存储在对应的数组元素中,因此,初始化数组时,先为该数组分配内 存空间,然后直接将数组元素的存入对应数组元素中, TestPrimitiveArray 引用类型数组的初始化 引用类型数组数组元素是引用,因此情况变得更加复杂:每个数组元素里存储的还是引用,它指向另一块内存, 这块内存里存储了有效数据。 没有多维数组 •Java语言提供了多维数组的语法,但多维数组实质上还是一维数组。 Java语言里的数组类型是引用类型,因此,数组变量其实是一个引用,这个引用指向真实的数组内存。数组元素 的类型也可以是引用,如果数组元素的引用再次指向真实的数组内存,这种情形看上去很像多维数组。 •定义二维数组的语法: •type[ ] [ ] arrName; TestTwoDimension 我们可以得到一个结论: 二维数组是一维数组,其数组元素是一维数组;三维数组也是一维数组,其数组元素是二维数组;四维数组还是一维数 组,其数组元素是三维数组……从这个角度来看,Java语言里没有多维数组

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值