4

数组的扩展

  • Array, from()

将类数组对象和可遍历对象转化为真正的数组。

var arrayLike = {
  '0' : 'a',
  '1' : 'b',
  '2' : 'c',
  'length': 3
}

var arr;
//ES5
arr = [].slice.call(arrayLike);    //arr=['a', 'b', 'c']

//ES6
arr = Array.from(arrayLike);    //arr=['a', 'b', 'c']

和它类似的是扩展运算符, 一样可以实现该功能(要求对象具有遍历器接口):

function(){
  var arg = [...arguments];   //转化 arguments 为数组
}

Array.from() 接受第二参数(函数), 用来映射结果, 相当于 map, 并且可以用第三个参数绑定 this:

Array.from(obj, func, context);
//等价于
Array.from(obj).map(func, context);

技巧, 用 Array.from() 指定函数运行次数:

var i = 0;
Array.from({length: 3}, ()=>i++);   //[0, 1, 2]

建议:使用Array.from方法,将类似数组的对象转为数组。

  • Array.of()

将多个值组成数组:

Array.of(2, 3, 5);   //[2, 3, 5]
Array.of(2);   //[2]
Array.of();   //[]
Array.of(undefined);   //[undefined]
  • Array.prototype.copyWithin()

函数参数是 Array.prototype.copyWithin(target, start=0, end=this.length), 对当前数组, 从截取下标为 start 到 end 的值, 从target 位置开始覆盖 this 中的值。如果 start 或 end 为负数则倒数。

[1, 2, 3, 4, 5].copyWithin(0, 3, 4);   //[4, 2, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, -2, -1);   //[4, 2, 3, 4, 5]

[].copyWithin.call({length: 5, 3: 1}, 0, 3);   //{0: 1, 3: 1, length: 5}

var i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);   //[3, 4, 5, 4, 5]
  • Array.prototype.find(), Array.prototype.findIndex()

这两个函数的参数都是回调函数。遍历数组, 找到符合条件(回调函数返回为true)的第一个值, find()返回其值, findIndex()返回其下标。如果没找到符合条件的值find()返回undefined, findIndex()返回-1。

[1, 2, -3, 4].find((item) => item < 0);   //-3
[1, 2, -3, 4].findIndex((item) => item < 0);   //2

[NaN].findIndex(y => y !== y);   //0
[NaN].indexOf(NaN);   //-1, indexOf 找不到 NaN

这两个函数还接受第二参数, 用来绑定回调函数中的 this

  • Array.prototype.fill()

完整形式: Array.prototype.fill(value, start=0, end=this.length), 对数组 start 到 end 直接部分填充 value, 覆盖原有值。

[1, 2, 3, 4, 5].fill('a', 2, 4);    //[1, 2, 'a', 'a', 5];
var arr = new Array(5).fill(0);   //arr = [0, 0, 0, 0, 0];
  • Array.prototype.entries(), Array.prototype.keys(), Array.prototype.values()

这三个方法, 用来遍历数组, 返回一个遍历器, 供 for...of 使用, 其中 keys()是对键的遍历, values() 是对值的遍历, entires()是对键值对的遍历。babel 已实现

var a = ['a', 'b', 'c'];

for(let item of a.values()){
  console.log(item);     //依次输出 'a', 'b', 'c'
}

for(let key of a.keys()){
  console.log(key);     //依次输出 0, 1, 2
}
for(let pair of a.entries()){
  console.log(pair);     //依次输出 [0, 'a'], [1, 'b'], [2, 'c']
}

当然也可以用遍历器的 next() 方法遍历

var a = ['a', 'b', 'c'];
var values = a.values();
console.log(values.next().value);   //'a'
console.log(values.next().value);   //'b'
console.log(values.next().value);   //'c'
  • Array.prototype.includes()

这是个 ES7 的方法, 判断数组中是否含有某个值, 含有则返回 true, 否则返回 false。可以用第二个参数指定查找起始点(小于0倒数)。

//该方法同样可以找到 NaN, 而 indexOf 不行
[1, 2, NaN].includes(NaN);   //true
[1, 2, 3, 4, 5].includes(2, 3);  //false
  • 数组的空位

我们比较以下两个数组:

var empty = new Array(3);  //[, , , ]
var unempty = new Array(3).fill(undefined);   //[undefined, undefined, undefined]

console.log(0 in empty);     //false
console.log(0 in unempty);   //true

结合手册内容如下就很好理解这个问题:

“Array elements may be elided at the beginning, middle or end of the element list. Whenever a comma in the element list is not preceded by
an AssignmentExpression (i.e., a comma at the beginning or after another comma), the missing array element contributes to the length of the
Array and increases the index of subsequent elements. Elided array elements are not defined. If an element is elided at the end of an array,
that element does not contribute to the length of the Array.”
摘自ECMAScript® 2015 Language Specification

翻译如下。

"数组成员可以省略。只要逗号前面没有任何表达式,数组的length属性就会加1,并且相应增加其后成员的位置索引。被省略的成员不会被定
义。如果被省略的成员是数组最后一个成员,则不会导致数组length属性增加。”

很明显, 一个完全空的数组是没有东西的, 而填充了undefined的数组并不是空的。
结合 ES5, 发现不同函数方法对空位处理方式是不一样的:

  • forEach(), filter(), every(), some() 会忽略空值
  • map(), copyWithin() 会保留空值, 但不做处理
  • join(), toString(), 会把空值处理为空字符串
  • fill() 不区分空值与非空值
  • Array.from(), 扩展运算符(...), for...of, entires(), keys(), values(), find(), findIndex() 会视空值为 undefined

如果你记不住这些, 或者为了程序的健壮性, 可维护性, 尽量避免在数组中出现空值。
举个实例, 理解一下这个问题:

新建一个长为200的数组, 并初始化每个位置的值等于其索引
//错误方法
var arr = new Array(200).map(function(item, index){
  return index;
});
console.log(arr);    //[undefined × 200]
//正确做法
var arr = new Array(200).join().split(',').map(function(item, index){
  return index;
});
console.log(arr);    //[1, 2, 3, ..., 200]
  • 数组推导

这是一个 ES7 的功能, 暂时还没能实现。我们可以先看一下它如何推导的:

var a1 = [1, 2, 3, 4];
var a2 = [for( i of a1) i * 2];   //a2=[2, 4, 6, 8]

不难看出, 数组 a2 通过 for...of 直接从 a1 生成。但是它的功能不仅仅这么简单, 还可以有 if 条件:

var a1 = [1, 2, 3, 4];
var a3 = [for( i of a1) if(i > 2) i * 2];   //a3=[6, 8]

这样, 我们可以简单的用数组推导模拟 map(), filter() 方法了。比如上面2个例子等价于:

var a1 = [1, 2, 3, 4];
var a2 = a1.map( (i) => i * 2 );
var a3 = a1.filter( (i) => i > 2 ).map( (i) => i * 2 );

当然我们还可以用多个 for...of 构成循环嵌套:

var a = ['x1', 'x2'];
var b = ['y1', 'y2'];

[for(i of a) for(j of b), console.log(i+', '+j)];
//输出
//['x1', 'y1']
//['x1', 'y2']
//['x2', 'y1']
//['x2', 'y2']

数组推导由 [] 构建了一个作用域, 其内部新建的变量, 等同于用 let 关键字声明的变量。除此之外, 字符串也可以被视为数组, 所以同样可以使用数组推导:

[for(c of 'abcde'). c+'.'].join('');  //"a.2.3.4.5."

Faremax
1.7k 声望705 粉丝