整理了 Array 中很基础的要掌握的知识点,希望可以帮助初学者,也希望自己以后多用多融会贯通。
创建数组
- 使用Array构造函数:
var a=new Array();//创建一个空数组var a=new Array(20);//创建给定数值的项数的数组var a=new Array("a","b","c");//包含3个字符串值的数组var a=Array();//可以省略new关键字var a=Array(3);
给构造函数传递一个值也可以创建数组:
- 如果传递的数值,则会创建数值给定项数的数组
- 如果传递的是其他类型的参数,则会创建则会创建包含那个值的只有一项的数组
- 使用数组字面量表示法:
var a=['a','b','c'];//创建一个包含三个字符串的数组var a=[];//创建一个空数组
不建议如图写法:
原因:<=IE8版本中的ECMAScript实现在数组字面量方面存在bug,a会成为一个包含3个项,其值为 [1,3,undefined] , [undefined,undefined,undefined] (这个结果与 new Array(3) 逻辑上相同)
数组长度length属性
数组的 length 属性不是只读的(不太清楚value特性的值为何总是数组第二项的值,而且当数组 length<=1 时,则 Object.getOwnPropertyDescriptor(a,length) 返回为 undefined ,知道的盆友感谢告知!)。
因此通过设置这个属性,可以从数组末尾移除项。但若将 length 设置为大于数组项数的值,后面的值会为 undefiend 。
var a=['a','b','c'];a.length=2;console.log(a[2]);//undefined
可以向数组中添加项
var a=['a','b','c'];a[a.length]='d';//["a", "b", "c", "d"]
当把一个值放在超出当前数组大小的位置,数组会重新计算其长度。
var a=['a','b','c'];a[99]='p';a.length;//100a[3];//a[3]~a[98]都为undefined
检测数组
- a instanceof Array;//true 判断a是否为 Array 的实例
缺点: instanceof 假定当前只有一个全局执行环境。如果在网页中包含多个框架,那么存在两个以上不同的全局执行环境,从而存在两个以上不同版本 window ,不同版本的 Array 构造函数,如果从一个框架向另一个框架输入数组,那么传入的该数组和目标框架的数组可能有不同的构造函数,这样就不能判断是否是实例了。以上代码要返回 true ,a必须是一个数组且和 Array 构造函数在同一个全局作用域中,而 Array 又是 window 的属性。如果a是在另一个 frame 中定义的的数组,那么以上代码会返回 false 。写了个不同框架引用数据测试了一下,还牵扯到chrome由于同源策略出现的一些问题,感兴趣的请戳
- Array.isArray(a);//true 不分环境,只判断最终确定的某个值到底是不是数组
- Object.prototype.toString.call(a) 原生数组的构造函数名 Array 与全局作用域无关,使用 toString() 可以保证返回一致的值。
在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串,每个类内部都有一个 [[class]] 属性,这个属性中就指定了上述字符串中的构造函数名。基于这一思路也可检测某个值是不是原生函数或正则表达式。但是不能检测非原生构造函数的构造函数名,自定义的任何构造函数都将返回 [object Object] 。
下面介绍的各种数组方法均来自 Array.prototype
转换方法
var a=['a','b','c'];a.toString();//"a,b,c" 为了创建这个字符串会调用数组的每一项toString()方法,若为a.toLocaleString()则会调用数组每一项的toLocaleString() a.valueOf();//["a","b","c"]
调用数组的 toString() 方法返回由数组中每个值的字符串形式拼接而成的一个以逗号分隔的字符串,但调用 valueOf() 返回的还是数组
a.join();//"a,b,c"a.join(undefined);//"a,b,c"a.join('||');//"a||b||c"
join() 方法只接收一个参数,即用作分隔符的字符串,返回包含所有数组项的字符串。不给 join() 传任何值或传入 undefined (<=IE7会使用 undefined 分隔符)则使用逗号作为分隔符。
如果数组中的某一项值为 null 或 undefined ,那么该值在 join() , toLocaleString() , toString() , valueOf() 方法返回的结果中以空字符串表示。
a=[3,undefined,null,''];a.toString();//"3,,,"
栈方法
push() 方法接受任意数量的参数(参数可以有多个,多参数添加顺序是从左到右),把他们逐个添加到数组末尾,返回修改后数组长度。
pop() 方法从数组末尾移除最后一项,减少数组 length ,返回移除的项。
队列方法
shift() 移除数组中第一个数并返回该项,同时将数组长度减一。结合使用 shift 和 push 方法可以像使用队列一样使用数组。
unshift() 在数组前端添加任意个项(多参数添加顺序先右后左)并返回新数组长度(<=IE7返回 undefined )。同时使用 unshift 和 pop 方法可以从相反方向模拟队列。
var a=['a','b','c'];a.unshift('o','p','q');//6 a;["o", "p", "q", "a", "b", "c"]
重排序方法
reverse() 反转数组项的顺序,返回反转后的原数组。
sort() 方法按升序排列数组项,返回排序后的原数组。为了实现排序, sort() 方法会调用每个数组项的 toString() 转型方法,然后比较得到的字符串,以确定如何排序(个人觉得是按照ASCII表比较的, sort 内部比较排序算法的实现标准中并没有定义,可能是冒泡或快排,感兴趣可参考我另篇文章)。即使数组中每一项都是数值, sort() 方法比较的也是字符串,看下面代码
再进行字符串比较时,“10"位于“5”的前面,对照ASCII表,“1”的十进制是49,“5”的十进制是53。因此 sort() 方法可以接收一个比较函数(接收两个参数)作为参数,以便认为指定哪个值位于哪个值前面。
var compare=function(value1,value2){ if(value1value2) return 1; else return 0}var a=[1,5,10,6];a.sort(compare);//[1,5,6,10]
对于数值类型或其 valueOf() 方法会返回数值类型的对象类型,可以使用一个更简单的比较函数,这个函数只需要第二个值减第一个值就能产生降序结果(升序就是被减数和减数交换)。之所以能这样做是因为 sort() 内部根据返回的一个小于0,大于0,等于0来影响排序结果。
var compare=function(value1,value2){ return value2-value1;}var a=[1,5,10,6];a.sort(compare);//[10, 6, 5, 1]
操作方法
concat() 基于当前数组中所有项创建一个新数组。这个方法会先创建当前数组的一个副本,然后将接收到的参数添加到这个副本的末尾,最后返回新构建的数组。
- 在没有传参的情况下,它只是复制当前数组并返回副本。
- 传给 concat() 方法的是一个或多个数组,该方法会将这些数组中的每一项都添加到结果数组中。
- 传递的值不是数组,这些值就会被简单的添加到结果数组的末尾。
slice() ,翻译过来为”切出“,它能够基于当前数组中一个或多个项创建一个新数组。 slice 方法接受1~2个参数,即要返回项的起始和结束位置。
- 不传参,返回复制原数组的一份新数组。
- 只有一个参数情况下,返回从该参数指定位置到当前数组末尾的所有项。
- 两个参数,返回起始位置和结束位置之间的项(但不包括结束位置)。
- 参数中有负数情况下,则用数组长度加上该负数来确定相应位置。如果结束位置小于起始位置则返回空数组。如果计算出来的位置还是负数,则也只是复制当前数组并返回副本。
splice() ,翻译过来为”拼接“,主要用途为向数组中部插入项,该方法的使用会改变原数组。 splice() 始终都会返回一个数组,该数组中包含从原始数组中删除的项(如没有删除项则返回空数组)
- 删除:可以删除任意数量的项,返回删除的项。
- 无参数,则不会删除任何项。
- 只有一个参数,则会删除该项到当前数组末尾的所有项。
- 两个参数:要删除的第一项的位置和要删除的项数。
- 参数中有负数情况下,则用数组长度加上该负数来确定相应位置。要删除的项数若为负则返回空数组。
- 插入:可以为指定位置插入任意数量的项,三个参数:起始位置,0(要删除的项数),要插入的项,如果要插入多个项,可以再传入任意多个项。
- 替换:可以向指定位置插入任意数量的项,且同时删除任意数量的项。三个参数:起始位置,要删除的项数,要插入任意数量的项。
位置方法
indexOf() 和 lastIndexOf() ,都接受两个参数:要查找的项,表示查找起点位置的索引(可选)。返回要查找的项在数组中的位置,在没找到情况下返回-1。在比较第一个参数与数组中每项时会使用全等操作符。
indexOf() 从数组的开头(位置0)向后查找, lastIndexOf() 从数组末尾向前查找。
a=['a','b','c','d','e','b'];a.indexOf('b',2);//5a.lastIndexOf('b',0);//-1a.lastIndexOf('b',5);//5
var person={name:'xx'};var people1=[{name:'xx'}];var people2=[person];people1.indexOf(person);//-1people2.indexOf(person);//0
迭代方法
每个迭代方法都接收两个参数:要在每项上运行的函数,运行该函数的作用域对象(即影响this的值)。传入这些方法中的函数可以会接收3个参数:数组项的值,该项在数组中位置,数组对象本身。
根据使用方法的不同,这个函数执行后的返回值可能会也可能不会影响方法中的返回值。
- every() :如果该函数对每一项都返回 true ,则整个数组调用函数完后返回值为 true 。
- filter() :返回该数组会返回 true 的项的组成的数组。该方法对查询符合某些条件的所有数组项非常有用。
- forEach() :没有返回值。
- map() :返回每次函数调用的结果组成的数组。该方法适合创建包含的项与另一个数组一一对应的数组。
- some() :如果该函数对数组中任一项返回 true ,则返回 true 。
every() 和 some() 的区别:
a=["a", "b", "c", "d", "e", "b"];a.every(function(item,i,array){ return typeof item=='string'});//truea.some(function(item,i,array){ return item=='b';});true
//利用指定的函数确定是否在返回的数组中包含某项a.filter(function(item,i,array){ return item=='b'});//["b", "b"]//返回数组的每一项都是在原始数组中对应项的基础上运行传入的函数的结果a.map(function(item,i,array){ return item+'xx';});//["axx", "bxx", "cxx", "dxx", "exx", "bxx"]
归并数组方法
reduce() 和 reduceRight() ,这两个方法都会迭代数组中的所有项,然后构建一个最终返回的值。都接收两个参数:在每一项上调用的函数(接收4个参数:前一个值,当前值,项的索引,数组对象),作为归并基础的初始值(可选)。这个函数返回的任何值都会做为第一个参数自动传给下一项。
注意:若忽略传入初始值时,第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数是数组的第二项。
reduce() 从数组的第一项开始,逐个遍历到最后。
reduceRight() 从数组最后一项开始,向前遍历到第一项。
------分割线-----
在知乎上看到 其实原因是forEach会将数组中被删掉的项或未赋值的项跳过,不过由于forEach是后来才加进来的,可以修改它的源码啦,更多的可以参考下面MDN的链接。惊觉原来 Array 身上还有这么多方法,整理一下!
参考:《javascript高级程序设计》