JavaScript浅析 -- 数组方法(下)

一、数组遍历

Array提供了5种遍历操作数组元素的方法,有map、forEach、filter、some、every,他们分别适用于不同的场合,下面一一解析。

1. map方法

map方法接受两个参数,第一个参数是函数,第二个参数可以用来绑定函数执行中的this。map会把每个元素作为参数传入函数,然后将每个函数结果组成新数组返回。会跳过空位,不会跳过null和undefined。

2. forEach方法

forEach方法接受两个参数,第一个参数是函数,第二个参数指定函数执行中的this指向。forEach会把每个元素作为参数传入函数进行处理(仅处理,不会有返回结果)。且无法用breakcontinue中断执行,若要中断请用for。

3. filter方法

filter同样接受两个参数,把每个元素作为函数参数传入,将函数结果为true的元素组成的新数组

4. some方法

some也接受两个参数,把每个元素作为函数参数传入,只要函数结果有一个返回true则some返回true。对于空数组some返回false,且回调函数不执行。

5. every方法

every也接受两个参数,把每个元素作为函数参数传入,所有函数结果都返回true时则every返回true。对于空数组every返回true,且回调函数不执行。

总结补充:
  • 除了forEach没返回值,其他4个都有返回值,some和every返回的是布尔值,map和filter返回的是数组。由于filter和map返回的都是数组,可以链式调用,即map的结果可以再调用map。
  • 上面的是Array提供的遍历方法,而可以用来遍历数组还有forfor-offor-in。其中for-of是es6增加的用来遍历具有iterator接口的数据结构的的,而数组原生具备iterator接口,所以可用来遍历数组。for-in是用来遍历对象属性的,而数组也是对象的一种(但不推荐用此法遍历数组,因为for-in会遍历所有可枚举属性包括继承的)。用法如下:
var arr = ['first', 'second', 'third'];
// for用法
for (var i=0,len=arr.length; i<len; i++) { // 先将len存起来以免动态增加或多次重复读取len
  console.log(arr[i]);
}

// for-of用法
for (var value of arr) {
  console.log(value);
}

// for-in用法
for (var key in arr) {
  console.log(arr[key]);
}
// 上面三种都依次打印出 first second third
  • 遍历效率:for > for-of > forEach > map > for-in

二、数组的截取

slice用于截取某个区域段的元素,然后返回这些元素组成的新数组,原数组不发生改变。该方法接受两个参数,第一个参数表示从数组哪个位置(包含)开始截取,第二个表示到哪个位置(不包含)停止截取,若没有第二个参数则默认为arr.length。两个参数若为负数则倒着算,如果第一个参数大于第二个则返回空数组。

var arr = [1, 2, 3, 4];
arr.slice(1); // [2, 3, 4],相当于arr.slice(1, arr.length)
arr.slice(1, 3); // [2, 3]
arr.slice(-4, -1); // [1, 2, 3],相当于arr.slice(arr.length-4, arr.length-1)
arr.slice(3, 2); // []

slice还有一个作用,可以用来将类数组对象转换为真正的数组。详细见下文。

三、数组判断

判断某个变量是否某种类型我们一般用typeof,但是typeof返回的只有基本数据类型(number, string, boolean, undefined, null, object, symbol;在此基础上去掉了null增加了function)。而Array也是一种对象,所以返回的也是object,并不会返回array,如下:

typeof 1; // "number"
typeof 'hello'; // "string"
typeof true; // "boolean"
typeof Symbol(); // "symbol",es6新增数据类型
typeof undefined; // "undefined"
typeof null; // "object"
typeof function(){}; // "function"
typeof new Object(); // "object"
typeof new Array(); // "object"

所以使用typeof来判断一个变量是否为数组并不可行。若要判断是否为数组,主要有以下四种方法:

1. 通过instanceof判断

instanceof主要用来判断某对象是否为另一个对象的实例的,而所有数组都是Array的实例,所以可以用此法判断变量是否为数组。instanceof的主要实现原理是判断某构造函数的prototype是否在被检测对象的原型链上。用法如下:

var arr = [1, 2];
arr instanceof Array;  // true
2. 通过constructor判断

由于所有数组都是Array的实例,而实例的constructor会返回对应的构造函数,所以所有数组的constructor都是Array,可以通过此法来判断是否为数组。

var arr = [1, 2];
arr.constructor === Array; // true
3. 通用Object.prototype.toString()判断

Object.prototype.toString()会返回对象的字符串形式,如[object Object],该字符串第一个是typeof的值,第二个是constructor的值,所以数组返回的是[object Array]。由于基本所有实例的原型链上都有Object.prototype,所以数组实例可以调用借用此法然后判断返回值是否是[object Array]来判断是不是数组。

var arr = [];
Object.prototype.toString.apply(arr) === '[object Array]'; // true
4. 通过Array.isArray()判断

ES6新增了一个Array.isArray()专门用于判断数组,这样的写法比上面几种更简洁明了表明了意图,使用起来也非常方便。

var arr = [];
Array.isArray(arr); // true

四、数组转化

有时候,我们会有一些类似数组的但实际又不是数组的对象,我们称为类数组对象,如string、DOM节点的NodeList、方法参数arguments等,他们一般都有数字作为索引且有length属性。由于他们不是真正的数组,所以无法使用数组的push等方法,要想使用需要先转为真正的数组(虽然可以call借用数组方法但效率没比处理真正的数组高)。一般有如下三种方法:

1. 通过slice来转化
var str = 'hello';
Array.prototype.slice.call(str); // ["h", "e", "l", "l", "o"]
Array.prototype.slice.apply(str); // ["h", "e", "l", "l", "o"]
Array.prototype.slice.bind(str)(); // ["h", "e", "l", "l", "o"]
2. Array.from()

ES6提供了真正转数组的方法Array.from(),他可以用来将类数组对象具有Iterator接口的对象转为真正的数组,用法如下:

var str = 'hello';
Array.from(str); // ["h", "e", "l", "l", "o"]
3. 扩展运算符

ES6的扩展运算符...能将具有Iterator接口的对象展开成多个序列元素。

var str = 'hello';
[...str]; // // ["h", "e", "l", "l", "o"]

五、创建数组

创建数组主要有3种方法,但也还可以借用转换成数组的方法生成数组:

1. 字面量法
var = [1, 2, 3]; // 最直接也最容易的方法
2. new构造函数法

用Array构造函数来生成数组实例,可以接受多个参数作为数组元素,当只有一个参数是表示数组长度。

var arr1 = new Array(1, 2, 3); // [1, 2, 3]
var arr2 = new Array(3); // [empty × 3] 即 [ , , ]
var arr3 = new Array('3'); // ["3"]
3. Array.of()

可以看到,上面的new构造法当只传一个数值元素的时候,会把他当做数组的长度,这样就没法生成一个只有一个数值元素的数组了,所以ES6有了Array.of()来统一都把参数都当做元素。

var arr1 = Array.of(1, 2, 3); // [1, 2, 3]
var arr2 = Array.of(3); // [3]
var arr3 = Array.of('3'); // ["3"]
4. 转数组法

如果想指定生成多少个元素的数组,还可以借助Array.from()Array.apply()等转换数组的方法,可以创造如下只有长度的类数组对象。

Array.from({length: 3}); // [undefined, undefined, undefined]
Array.apply(null, {length: 3}); // [undefined, undefined, undefined]
[...Array(3)]; // [undefined, undefined, undefined]

推荐阅读更多精彩内容