前端笔记

数值类型扩展

2017-10-03  本文已影响1人  faremax

数组的扩展

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(2, 3, 5);   //[2, 3, 5]
Array.of(2);   //[2]
Array.of();   //[]
Array.of(undefined);   //[undefined]
[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]
[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

[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];
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'
//该方法同样可以找到 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.”
<small>摘自<a href="http://www.ecma-international.org/ecma-262/6.0/" target="_blank">ECMAScript® 2015 Language Specification</a></small>

翻译如下。

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

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

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

新建一个长为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]
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."
上一篇下一篇

猜你喜欢

热点阅读