4月阅读周·你不知道的JavaScript | 用户库扩展最多的特性——Array,ES6为它增添新活力
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读三个月。
4月份的阅读计划有两本,《你不知道的JavaScrip》系列迎来收尾。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》。
当前阅读周书籍:《你不知道的JavaScript(下卷)》。
Array
静态函数Array.of(..)
Array.of(..)取代了Array(..)成为数组的推荐函数形式构造器,因为Array.of(..)并没有这个特殊的单个数字参数的问题。
var a = Array(3);
a.length; // 3
a[0]; // undefined
var b = Array.of(3);
b.length; // 1
b[0]; // 3
var c = Array.of(1, 2, 3);
c.length; // 3
c; // [1,2,3]
如果有一个回调函数需要传入的参数封装为数组,Array.of(..)可以完美解决这个需求。
另外一种情况是,如果构建Array的子类,并且想要在子类实例中创建和初始化元素,比如:
class MyCoolArray extends Array {
sum() {
return this.reduce(function reducer(acc, curr) {
return acc + curr;
}, 0);
}
}
var x = new MyCoolArray(3);
x.length; // 3--oops!
x.sum(); // 0--oops!
var y = [3]; // Array, 而不是MyCoolArray
y.length; // 1
y.sum(); // sum不是一个函数
var z = MyCoolArray.of(3);
z.length; // 1
z.sum(); // 3
不能(简单地)只是为MyCoolArray创建一个构造器来覆盖Array父构造器的行为,因为那个构造器对于实际构造一个行为符合规范的数组值(初始化this)是必要的。MyCoolArray子类“继承来的”静态of(..)方法提供了很好的解决方案。
静态函数Array.from(..)
JavaScript中的“类(似)数组对象”是指一个有length属性,具体说是大于等于0的整数值的对象。普遍的需求就是把它们转换为真正的数组,这样就可以应用各种Array.prototype方法(map(..)、indexOf(..)等)了。
新的ES6 Array.from(..)方法都是更好理解、更优雅、更简洁的替代方法:
var arr = Array.from(arrLike);
var arrCopy = Array.from(arr);
Array.from(..) 检查第一个参数是否为iterable(参见3.1节),如果是的话,就使用迭代器来产生值并“复制”进入返回的数组。因为真正的数组有一个这些值之上的迭代器,所以会自动使用这个迭代器。
而如果你把类数组对象作为第一个参数传给Array.from(..),它的行为方式和slice()(没有参数)或者apply(..)是一样的,就是简单地按照数字命名的属性从0开始直到length值在这些值上循环。
创建数组和子类型
Array.of(..)和Array.from(..)都使用访问它们的构造器来构造数组。所以如果使用基类Array.of(..),那么得到的就是Array实例;如果使用MyCoolArray.of(..),那么得到的就是MyCoolArray实例。
原型方法copyWithin(..)
Array#copyWithin(..)是一个新的修改器方法,(包括带类型的数组在内的,参见第5章)所有数组都支持。copyWithin(..)从一个数组中复制一部分到同一个数组的另一个位置,覆盖这个位置所有原来的值。
参数是target(要复制到的索引)、start(开始复制的源索引,包括在内)以及可选的end(复制结束的不包含索引)。如果任何一个参数是负数,就被当作是相对于数组结束的相对值。
例如:
[1, 2, 3, 4, 5].copyWithin(3, 0); // [1,2,3,1,2]
[1, 2, 3, 4, 5].copyWithin(3, 0, 1); // [1,2,3,1,5]
[1, 2, 3, 4, 5].copyWithin(0, -2); // [4,5,3,4,5]
[1, 2, 3, 4, 5].copyWithin(0, -2, -1); // [4,2,3,4,5]
opyWithin(..)方法不会增加数组的长度。到达数组结尾复制就会停止。
原型方法fill(..)
可以通过ES6原生支持的方法Array#fill(..)用指定值完全(或部分)填充已存在的数组:
var a = Array(4).fill(undefined);
console.log(a); // [undefined, undefined, undefined, undefined]
fill(..)可选地接收参数start和end,它们指定了数组要填充的子集位置,比如:
var a = [null, null, null, null].fill(42, 1, 3);
console.log(a); // [null,42,42, null]
原型方法find(..)
ES6的find(..)和some(..)的工作方式一样,除了一旦回调返回true/真值,会返回实际的数组值:
var a = [1, 2, 3, 4, 5];
a.find(function matcher(v) {
return v == '2';
}); // 2
a.find(function matcher(v) {
return v == 7; // undefined
});
原型方法findIndex(..)
有些需求,需要找到匹配值的位置索引。
indexOf(..)会提供这些,但是无法控制匹配逻辑;它总是使用===严格相等。所以ES6的findIndex(..)才是解决方案:
var points = [
{ x: 10, y: 20 },
{ x: 20, y: 30 },
{ x: 30, y: 40 },
{ x: 40, y: 50 },
{ x: 50, y: 60 },
];
points.findIndex(function matcher(point) {
return point.x % 3 == 0 && point.y % 4 == 0;
}); // 2
points.findIndex(function matcher(point) {
return point.x % 6 == 0 && point.y % 7 == 0;
}); // -1
不要使用findIndex(..) ! = -1(这是indexOf(..)的惯用法)从搜索中得到布尔值,因为some(..)已经yield出你想要的true/false。也不要用a[ a.findIndex(..) ]来得到匹配值,因为这是find(..)所做的事。最后,如果需要严格匹配的索引值,那么使用indexOf(..);如果需要自定义匹配的索引值,那么使用findIndex(..)。
原型方法entries()、values()、keys()
从传统角度来说,Array可能不会被看作是“集合”,但是它提供了同样的迭代器方法entries()、values()和keys(),从这个意义上说,它是一个集合。
var a = [1, 2, 3];
[...a.values()]; // [1,2,3]
[...a.keys()]; // [0,1,2]
[...a.entries()]; // [ [0,1], [1,2], [2,3] ]
[...a[Symbol.iterator]()]; // [1,2,3]
就像Set一样,默认的Array迭代器和values()返回的值一样。
总结
各种JavaScript用户库扩展最多的特性之一就是数组(Array)类型。所以ES6为Array增加了一些静态函数和原型(实例)方法辅助函数也在意料之中。
Array新增了静态函数of(..)和from(..),以及像copyWithin(..)和fill(..)这样的原型函数。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)