数组的创建
JavaScript 中的数组每一项可以保存任何类型的数据,并且,JavaScript 数组的大小时动态调整的。
JavaScript 中允许我们使用两种方式来创建数组:
// 方式一:使用 Array() 构造函数
var names = new Array();
var colors = new Array(5);
var ages = new Array(20, 18, 22);
// 方式二:使用字面量
var options = ['A', 'B', 'C'];
这里推荐第二种创建数组的方式。
注意:
var values = [1, 2,];
在数组字面量的最后一项添加 ,
会造成浏览器解析不一致。
在 IE8 及之前版本的浏览器中会创建一个包含3项的数组:[1, 2, undefined]
;而其他浏览器则表现正常。所以,强烈建议不要在数组字面量的最后一项添加 ,
。
数组的检测
JavaScript 中数组也是对象,通过 typeof
并不能区分数组、对象和 null
。这里是 MDN 关于 typeof 的返回结果表。
intanceof
intanceof
运算符可以用来判断某个构造函数的 prototype
属性所指向的对象是否存在于另外一个要检测对象的原型链上。可以这样使用:
var values = [1, 2, 3];
console.log(values instanceof Array); // true
Object.prototype.toString
var values = [1, 2, 3];
console.log(Object.prototype.toString.call(values)); // [object Array]
利用原生数组的构造函数名与全局作用域无关,因此可以保证使用 toString()
可以返回一致的值。这里可以封装一个函数:
function isArray(value){
return Object.prototype.toString.call(value) === '[object Array]';
}
var values = [1, 2, 3];
console.log(isArray(values)); // true
Array.isArray()
ES5 新增了这个方法,用于确定某个值到底是不是数组。
但是,这个方法不兼容 IE8 及以下的浏览器。所以,为了增加兼容性,可以添加下面的代码:
if(!Array.isArray){
Array.isArray = function(args){
return Object.prototype.toString.call(args) === '[object Array]';
};
}
转换方法
join()
join()
方法接收一个用作分隔符的字符串作为参数,然后返回包含所有项的字符串。当不传入参数时,默认使用,
分隔。
var values = [1, 2, 3];
console.log(values.join()); // '1,2,3'
console.log(values.join('')); // '123'
console.log(values.join(',')); // '1,2,3'
console.log(values.join(' | ')); // '1 | 2 | 3'
toLocaleString(), toString(), valueOf()
var values = [1, 2, 3];
console.log(values.toLocaleString());
console.log(values.toString());
console.log(values.valueOf());
alert(values.valueOf());
调用 valueOf()
方法还是返回数组本身,而 toLocaleString()
, toString()
, 方法是返回字符串。
注意:如果是 alert()
函数,由于它接收的是字符串参数,会在后台调用 toString()
方法,所以 alert(values.valueOf());
返回的也是字符串。
栈和队列方法
push(), pop()
栈是一种先入先出的数据结构。JavaScript 数组中的 push()
, pop()
方法就可以模拟这种栈结构。
push()
用于往数组的末尾添加一项,而 pop()
用于删除数组的最后一项。
push()
可以接受任意多数量的参数,并依次将它们添加到数组的最后一项,最后返回数组的长度。
而每执行一次 pop()
,都删除数组的最后一项,返回的是被删除的项。
执行一次 push()
,数组长度加1;反之,执行一次 pop()
数组长度减1。
var values = [1, 2, 3];
var newArrayLength = values.push(4);
console.log(values, newArrayLength);
var lastItem = values.pop();
console.log(values, lastItem);
console.log(values);
shift(), unshift()
队列是一种先进先出的数据结构。JavaScript 数组中的 shift()
, unshift()
方法可以模拟队列结构。
shift()
用于删除数组的第一项,而 unshift()
用于往数组的头部添加一项。
shift()
将删除的第一项返回。
而 unshift()
则用于向数组的头部依次添加任意多项,返回数组的长度。
执行一次 shift()
,数组长度减1;而执行一次 unshift()
,数组长度加1。
var values = [1, 2, 3];
var newArrayLength = values.unshift(0);
console.log(values, newArrayLength);
var firstItem = values.shift();
console.log(values, firstItem);
console.log(values);
重排序方法
reverse()
reverse()
比较简单,就是用于将数组每一项的顺序反转。比如:
var values = [1, 2, 3];
var rValues = values.reverse();
console.log(rValues); // [3, 2, 1]
console.log(values); // [3, 2, 1]
需要注意的是,第二行输出就说明了 reverse()
会改变原来的数组。
sort()
使用 reverse()
还远远不够,在实际中更多时候需要我们自定义排序规则。而 sort()
就是实现这种想法的方法。
在默认情况下,sort()
会调用数组项的 toString()
方法,按照字符串比较进行升序排列。比如:
var values = [1, 40, 10, 5];
values.sort();
console.log(values); // [1, 10, 40, 5]
和 reverse()
方法一样,它也会改变原来的数组。
还好,sort()
允许我们将排序函数作为它的参数传递。那么我们就可以声明一个这样的排序函数:
function sortFun(item1, item2){
if (item1<item2){
return -1;
} else if (item1>item2){
return 1;
} else {
return 0;
}
}
var values = [1, 40, 10, 5];
values.sort(sortFun);
console.log(values); // [1, 5, 10, 40]
上面这个比较函数适用于大多数数据类型的升序排列。如果要进行降序排列,则可以这样修改函数:
if (item1<item2){
return 1;
} else if (item1>item2){
return -1;
}
而对于数值类型或者其 valueOf()
方法能返回数值类型的对象类型,可以使用下面这个更简单的排序函数:
function sortFun (value1, value2){
return value1 - value2; // 升序
/*
return value2 - value1; // 降序
*/
}
操作方法
concat()
concat()
可以基于当前数组中的所有项创建一个新数组,并返回新构建的数组。
通过下面这个例子来说明,使用 concat()
不会改变原数组。
var values = [1, 2, 3];
var newValues = values.concat();
newValues.pop();
console.log(newValues); // [1, 2]
console.log(values); // [1, 2, 3]
通常,可以使用 concat()
将额外的数据合并进数组:
var values = [1, 2, 3];
var newValues = values.concat(4, [5, 6]);
console.log(newValues); // [1, 2, 3, 4, 5, 6]
slice()
slice()
则是基于当前数组截取满足条件的元素作为新数组返回。
一般来说,slice()
接收两个参数 - 起始位置和结束位置。范围是 [startIndex, endIndex)
。
当只有1个参数传递给 slice()
时,则截取范围为 [startIndex, arr.length-1]
。
来看下面的例子:
var values = [1, 2, 3, 4, 5, 6, 7];
var values2 = values.slice(2);
var values3 = values.slice(2, 5);
console.log(values2); // [3, 4, 5, 6, 7]
console.log(values3); // [3, 4, 5]
还需要注意的两点是:
- 如果
slice()
方法的参数中有负数,则用数组的长度加上该数来确定响应的位置。比如:数组长度为5,则slice(-2, -1)
与slice(3, 4)
的结果一致。 - 如果结束位置小于起始位置,则返回空数组。
splice()
splice()
算是最强大的数组方法了,它可以删除、插入、替换数组的项。
当 splice()
接收2个参数时,表示要删除的第一项的位置和要删除的项数。
当 splice()
接收3个参数(甚至更多)时,表示要插入项的起始位置、0(要删除的项数)和要插入的项。
要替换数组项,则只需要让 splice()
的第2个参数不为0即可。
注意: splice()
始终返回从原数组中删除的数组项,如果没有删除项,则返回空数组。
var values = [1, 2, 3, 4, 5, 6, 7];
var remove = values.splice(2, 1);
console.log(values); // [1, 2, 4, 5, 6, 7]
console.log(remove); // [3]
remove = values.splice(4, 0, 8, 9);
console.log(values); // [1, 2, 4, 5, 8, 9, 6, 7]
console.log(remove); // []
remove = values.splice(3, 2, 10, 11);
console.log(values); // [1, 2, 4, 10, 11, 9, 6, 7]
console.log(remove); // [5, 8]
位置方法
indexOf(), lastIndexOf()
这两个方法都可以接收两个参数,第一个参数表示要查找的项;第二个参数为可选的,表示查找的起点位置。区别在于 indexOf()
从数组的头部开始查找,lastIndexOf()
从数组的末尾开始查找。
它们都返回要查找的项(第一次出现)的位置,如果没有找到则返回 -1。另外,查找过程中进行比较时是使用全等(===)进行比较。
var values = [1, 2, 3, 2, 1];
console.log(values.indexOf(2)); // 1
console.log(values.lastIndexOf(2)); // 3
console.log(values.indexOf(2, 2)); // 3
console.log(values.lastIndexOf(2, 2)); // 1
console.log(values.indexOf(4)); // -1
迭代方法
在 ES5 中,数组拥有5个迭代方法。
每个方法都接收两个参数:一个作用于每一项的迭代函数 iteratee
,和(可选的)运行该函数的作用域对象 [context]
。
迭代函数 iteratee
会接受三个参数:数组的每一项 item
,每一项的位置 index
,和数组对象 array
。注意只有 item
参数是必须的。
every()
如果数组的每一项都能被 iteratee
函数返回 true
,则返回 true
。
var values = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var result = values.every(function(item){
return item > 2;
});
console.log(result); // false
some()
遍历整个数组,只要有一个项满足 iteratee
函数的条件,则返回 true
。
var values = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var result = values.some(function(item){
return item > 2;
});
console.log(result); // true
filter()
遍历整个数组,返回满足 iteratee
函数条件的数组项组成的数组。
var values = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var result = values.filter(function(item){
return item > 2;
});
console.log(result); // [3, 4, 5, 4, 3]
forEach()
遍历整个数组,对每一个数组项运行 iteratee
函数。它没有返回值。
var values = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var result = values.forEach(function(item){
item * 2;
});
console.log(result); // undefined
console.log(values); // [1, 2, 3, 4, 5, 4, 3, 2, 1]
注意:它也不能改变原数组。可以看看 map()
方法。
即使显式写成 return
语句,也不会有返回值!
map()
遍历整个数组,返回 iteratee
函数每次调用后的结果组成的数组。
var values = [1, 2, 3, 4, 5, 4, 3, 2, 1];
var result = values.map(function(item){
return item * 2;
});
console.log(result); // [2, 4, 6, 8, 10, 8, 6, 4, 2]
console.log(values); // [1, 2, 3, 4, 5, 4, 3, 2, 1]
如果这里省去 return
关键字,那么 result
将会是 [undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]
。
注意:这里的 return
只是 iteratee
函数的返回,并不是这些迭代方法的返回!
归并方法
reduce(), reduceRight()
ES5 还新增了两个归并方法。这两个方法会遍历整个数组,然后根据 iteratee
函数构建一个最终值并返回。它们都接收2个参数:一个作用于每一项的迭代函数 iteratee
和可选的作为归并的基础值 memo
。
而这个 iteratee
函数可接收4个参数:前一个值 prev
, 当前值 cur
, 项的索引 index
和数组对象 array
。这个函数的返回值都会作为第一个参数自动传递给下一项。
var values = [1, 2, 3];
var sum = values.reduce(function(prev, cur){
return prev + cur;
});
console.log(sum); // 6