一、不好用的构造函数
Array是JS的内置对象,同时也是一个构造函数,可以用它生成新的数组。但是由于Array作为构造函数,行为很不一致,所以我们平时直接用数组字面量要来的更直观一些。
// 不好的方式
var arr = new Array(1, 2);
// 推荐的做法
var arr = [1, 2];
注意,如果参数是一个正整数,返回数组的成员都是空位。虽然读取的时候返回undefined
,但实际上该位置没有任何值。虽然可以取到length
属性,但是取不到键名。
var arr = new Array(3);
arr.length // 3
arr[0] // undefined
arr[1] // undefined
arr[2] // undefined
0 in arr // false
1 in arr // false
2 in arr // false
上面代码中,arr
是一个长度为3的空数组。虽然可以取到每个位置的键值undefined
,但是所有的键名都取不到。
二、Array.isArray()
这是ES5对数组的一个扩展方法(IE9+),是Array对象的一个静态方法,用来判断一个值是否为数组。它可以弥补typeof
运算符的不足。
var a = new Array(123);
var b = new Date();
Array.isArray(a); //true
Array.isArray(b); //false
三、Array实例的方法
这才是Array对象的重点也是难点,灵活应用这些实例对象的方法,对日常工作将会有非常大的帮助。
3.1、valueOf(),toString()
valueOf
方法返回数组本身。
var a = [1, 2, 3];
a.valueOf(); // [1, 2, 3]
toString
方法返回数组的字符串形式。
var a = [1, 2, 3];
a.toString() // "1,2,3"
var a = [1, 2, 3, [4, 5, 6]];
a.toString() // "1,2,3,4,5,6"
值得注意的是valueOf
并不是这个数组实例上的方法,而是Object.prototype.valueOf
的方法,而toString
方法则是被数组实例Array.prototype.toString
方法所覆盖的结果。
3.2、栈方法
3.2.1、push()
在数组的末端添加一个或多个元素,返回添加新元素后的数组长度,会改变原数组。
var a = [];
a.push(1); // 1
a.push('a'); // 2
a.push(true, {}); // 4
a; // [1, 'a', true, {}]
3.2.2、pop()
删除数组的最后一个元素,返回该元素,会改变原数组。
var a = ['a', 'b', 'c'];
a.pop(); // 'c'
a; // ['a', 'b']
结合使用push()
和pop()
方法,就构成了后进先出的栈结构。
3.3、队列方法
3.3.1、unshift()
在数组的第一个位置添加元素,返回添加新元素后的数组长度,会改变原数组。
var arr = [ 'c', 'd' ];
arr.unshift('a', 'b'); // 4
arr; // [ 'a', 'b', 'c', 'd' ]
3.3.2、shift()
删除数组的第一个元素,返回该元素,会改变原数组。
var a = ['a', 'b', 'c'];
a.shift(); // 'a'
a; // ['b', 'c']
3.4、重排序方法
3.4.1、reverse()
颠倒数组中元素的顺序,返回改变后的数组,该方法将改变原数组。
var a = ['a', 'b', 'c'];
a.reverse(); // ["c", "b", "a"]
a; // ["c", "b", "a"]
3.4.2、sort()
对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。
['d', 'c', 'b', 'a'].sort()
// ['a', 'b', 'c', 'd']
[11, 101].sort()
// [101, 11]
可以看到该方法不是按大小排序的,而是数值会先转成字符串,再按照字典顺序进行比较。
这种排序结果显然不是我们想要的,那我们有没有办法自定义排序?如果想让sort
方法按照自定义方式排序,可以传入一个函数作为参数,表示按照自定义方法进行排序。该函数本身又接受两个参数,表示进行比较的两个元素。如果函数的返回值(也就是sort
方法的参数)大于0(a-b>0
即b<a
),调换参数位置,其他情况下,都是第一个参数排在第二个参数前面。
var arr = [123, 23, 1200];
//默认按字典顺序排序
arr.sort();
//如果我想实现按数值从小到大排序,即假如数组[1,2]排序,可以看到排序后位置要不变,所以sort参数要小于等于0才行,也就是函数的返回值要小于等于0,所以用1-2满足要求,所以会用a-b。
arr.sort(function(a, b){ //a,b是取得数组的顺序元素
return a-b; //返回值大于0,第一个在第二个后面;返回值小于等于0,顺序不变
});
//如果我想实现按数值从大到小排序
arr.sort(function(a, b){
return b-a;
});
自定义排序方法
3.5、操作方法
3.5.1、concat()
concat
方法用于多个数组的合并。它将新数组的成员,添加到原数组的尾部,然后返回一个新数组,原数组不变。
['hello'].concat(['world'])
// ["hello", "world"]
['hello'].concat(['world'], ['!'])
// ["hello", "world", "!"]
除了接受数组作为参数,concat
也可以接受其它类型的值作为参数,它们会作为新的元素,添加到数组尾部。
[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]
// 等同于
[1, 2, 3].concat(4, [5, 6])
[1, 2, 3].concat([4], [5, 6])
由于concat
方法添加元素到尾部并返回一个新数组,所以如果我们不提供参数,那就是返回当前数组的一个浅拷贝。所谓浅拷贝,指的是如果数组成员包括复合类型的值(比如对象),则新数组拷贝的是该值得引用。
var obj = { a:1 };
var oldArray = [obj];
var newArray = oldArray.concat();
obj.a = 2;
newArray[0].a // 2
3.5.2、slice()
slice
方法用于提取原数组的一部分,返回一个新数组,原数组不改变。
它的第一个参数为起始位置(从0开始),第二个参数为终止位置(但该位置的元素本身不包括在内)。如果省略第二个参数,则一直返回到原数组的最后一个元素。
// 格式
arr.slice(start_index, upto_index);
// 用法
var a = ['a', 'b', 'c'];
a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]
上面的代码中,最后一个例子slice
没有参数,实际上等于返回一个原数组的拷贝。
如果slice
方法的参数是负数,则表示倒数计算的位置:
var a = ['a', 'b', 'c'];
a.slice(-2) // ["b", "c"]
a.slice(-2, -1) // ["b"]
上面的代码中,-2表示倒数计算的第二个位置,-1表示倒数计算的第一个位置。
如果参数值大于数组成员的个数,或者第二个参数小于第一个参数,则返回空数组。
var a = ['a', 'b', 'c'];
a.slice(4) // []
a.slice(2, 1) // []
3.5.3、splice()
splice
方法用于删除原数组的一部分成员,并可以在被删除的位置添加新的数组成员,返回值是被删除的元素,该方法会改变原数组。
splice
的第一个参数是删除的起始位置,第二个参数是被删除的元素个数。如果后面还有更多的参数,则表示这些就是要被插入数组的新元素。
// 格式
arr.splice(index, count_to_remove, addElement1, addElement2, ...);
// 用法
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2) // ["e", "f"]
a // ["a", "b", "c", "d"]
上面的代码从原数组4号位置,删除了两个数组成员,原数组被改变。
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]
上面代码除了删除成员,还插入了两个新成员。
当然第一个参数也可以是负数,表示从倒数位置开始删除。
var a = ['a', 'b', 'c', 'd', 'e', 'f'];
a.splice(-4, 2); //["c", "d"]
如果只是单纯地插入元素,splice
方法的第二个参数可以设为0。
var a = [1, 1, 1];
a.splice(1, 0, 2) // []
a // [1, 2, 1, 1]
如果只提供一个参数,等同于将原数组在指定位置拆分成两个数组。
var a = [1, 2, 3, 4];
a.splice(2) // [3, 4]
a // [1, 2]
3.5.4、join()
join
方法以参数作为分隔符,将所有数组成员组成一个字符串返回,如果不提供参数,默认用逗号分隔。
var a = [1, 2, 3, 4];
a.join();
a.join(' ');
a.join('|');
join方法拼接数组成员
3.6、ES5数组扩展
前面我们介绍了一种ES5对Array对象的扩展方法,ES5对数组的实例还添加了一些实用的方法。
3.6.1、位置方法
-
indexOf()
indexOf
方法返回给定元素在数组中第一次出现的位置,如果没有出现就返回-1。
var a = ['a', 1, 'b'];
a.indexOf('b');
a.indexOf(1);
a.indexOf('x');
验证
indexOf
方法还可以接受第二个参数,表示搜索的开始位置。
['a', 'b', 'c'].indexOf('a', 1); //-1
上面代码从1号位置开始搜索字符a
,结果为-1,表示没有搜索到。
-
lastIndexOf()
lastIndexOf
方法返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1。
var a = ['a', 9, 'b', 9];
a.lastIndexOf('b');
a.lastIndexOf(9);
a.lastIndexOf('x');
验证
3.2.2、迭代方法
-
every()、some()
这两个方法类似“断言”(assert),用来判断数组成员是否符合某种条件。
它们接受一个函数作为参数,所有数组成员依次执行该函数,返回一个布尔值。该函数接受三个参数,依次是当前位置的成员、当前位置的序号和整个数组。
some
方法是只要有一个数组成员的返回值是true
,则整个some
方法的返回值就是true
,否则false
。
var arr = [1, 2, 3, 4, 5];
arr.some(function (elem, index, arr) {
return elem >= 3;
});
// true
上面代码表示,如果存在大于等于3的数组成员,就返回true
。
every
方法则是所有数组成员的返回值都是true
,才返回true
,否则false
。
var arr = [1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
return elem >= 3;
});
// false
上面代码表示,只有所有数组成员大于等于3,才返回true
。
-
filter()
filter
方法的参数是一个函数,所有数组成员依次执行该函数,返回结果为true
的成员所组成的一个新数组,该方法不会改变原数组。
[1, 2, 3, 4, 5].filter(function (elem) {
return (elem > 3);
})
// [4, 5]
filter
方法的参数函数可以接受三个参数,第一个参数是当前数组成员的值,这是必需的,后两个参数是可选的,分别是当前数组成员的位置和整个数组。
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
return index % 2 === 0;
});
// [1, 3, 5]
上面代码返回偶数位置的成员组成的新数组。
-
map()
map
方法对数组的所有成员依次调用一个函数,根据函数结果返回一个新数组。
var numbers = [1, 2, 3];
numbers.map(function (n) {
return n + 1;
});
// [2, 3, 4]
numbers// [1, 2, 3]
map
方法接受一个函数作为参数,该函数调用时,map
方法会将其传入三个参数,分别是当前成员、当前位置和数组本身。
[1, 2, 3].map(function(elem, index, arr) {
return elem * index;
});
// [0, 2, 6]
上面代码中,map
方法的回调函数的三个参数之中,elem
为当前成员的值,index
为当前成员的位置,arr
为原数组([1, 2, 3]
)。
如果数组有空位,map
方法的回调函数在这个位置不会执行,会跳过数组的空位。
var f = function(n){ return n+1 };
[1, undefined, 2].map(f); //[2, NaN, 3]
[1, null, 2].map(f); //[2, 1, 3]
[1, , 2].map(f); //[2, , 3]
可以看出,map
方法不会跳过undefined
和null
,但是会跳过空位。
来一个更明显的例子:
Array(2).map(function(){
console.log('enter..');
return 1;
});
结果
可以看到,map
方法根本就没有执行,直接返回了Array(2)
生成的空数组。
-
forEach()
forEach
和map
方法很相似,也是遍历数组的所有成员,执行某种操作,但是forEach
方法一般不返回值,只是用来操作数据,如果需要有返回值,一般用map
方法。
forEach
方法的参数与map
方法一致,也是一个函数,数组的所有成员会依次执行该函数。它接受三个参数,分别是当前位置的值、当前位置的编号和整个数组。
function log(element, index, array) {
console.log('[' + index + '] = ' + element);
}
[2, 5, 9].forEach(log);
// [0] = 2
// [1] = 5
// [2] = 9
上面代码中,forEach
遍历数组不是为了得到返回值,而是为了在屏幕输出内容,所以应该使用forEach
方法,而不是map
方法,虽然后者也可以实现同样目的。
同样,forEach
方法会跳过数组的空位:
var log = function (n) { console.log(n + 1);};
[1, undefined, 2].forEach(log)
// 2
// NaN
// 3
[1, null, 2].forEach(log)
// 2
// 1
// 3
[1, , 2].forEach(log)
// 2
// 3
可以看出forEach
方法不会跳过undefined
和null
,但是会跳过空位。
3.2.3、归并方法
reduce
方法和reduceRight
方法依次处理数组的每个成员,最终累计为一个值。
它们的差别是,reduce
是从左到右处理(从第一个成员到最后一个成员),reduceRight
则是从右到左(从最后一个成员到第一个成员),其它完全一样。
这两个方法的第一个参数都是一个函数,该函数接受以下4个参数:
1、累积变量,默认为数组的第一个成员
2、当前变量,默认为数组的第二个成员
3、当前位置(从0开始)
4、原数组
这四个参数中,前两个是必须的,后面两个则是可选的。
例子求数组成员之和:
[1, 2, 3, 4, 5].reduce(function(x, y){
console.log(x, y);
return x + y;
});
结果
每一轮结束之后,x
为上一轮的返回值,y
为当前数组成员,直到遍历完所有成员,返回最后一轮计算后的x
。
如果要对累积变量指定初值,可以把它放在reduce
和reduceRight
方法的第二个参数,相当于设置了默认值。
[1, 2, 3, 4, 5].reduce(function(x, y){
console.log(x, y);
return x + y;
}, 10);
//25
结果
(本系列下一节为 — 三大包装对象)
网友评论