ES里关于数组的拓展
一、静态方法
在ES6以前,创建数组的方式主要有两种,一种是调用Array构造函数,另一种是用数组字面量语法,这两种方法均需列举数组中的元素,功能非常受限。如果想将一个类数组对象(具有数值型索引和length属性的对象)转换为数组,可选的方法也十分有限,经常需要编写额外的代码。为了进一步简化JS数组的创建过程,ES6新增了Array.of()和Array.from()两个方法
1、【Array.of()】
ES6之所以向JS添加新的创建方法,是要帮助开发者们规避通过Array构造函数创建数组时的怪异行为
- let items = new Array();
- console.log(items.length); // 2
- console.log(items[0]); // undefined
- console.log(items[1]); // undefined
- items = new Array("");
- console.log(items.length); //
- console.log(items[]); // "2"
- items = new Array(, );
- console.log(items.length); //
- console.log(items[]); //
- console.log(items[]); //
- items = new Array(, "");
- console.log(items.length); //
- console.log(items[]); //
- console.log(items[]); // "2"
如果给Array构造函数传入一个数值型的值,那么数组的length属性会被设为该值。如果传入多个值,此时无论这些值是不是数值型的,都会变为数组的元素。这个特性令人感到困惑,不可能总是注意传入数据的类型,所以存在一定的风险
ES6通过引入Array.of()方法来解决这个问题。Array.of()与Array构造函数的工作机制类似,只是不存在单一数值型参数值的特例,无论有多少参数,无论参数是什么类型的,Array.of()方法总会创建一个包含所有参数的数组
- let items = Array.of(, );
- console.log(items.length); //
- console.log(items[]); //
- console.log(items[]); //
- items = Array.of(2);
- console.log(items.length); // 1
- console.log(items[0]); // 2
- items = Array.of("");
- console.log(items.length); //
- console.log(items[]); // "2"
在大多数时候,可以用数组字面量来创建原生数组,但如果需要给一个函数传入Array的构造函数,则可能更希望传入Array.of()来确保行为一致
- function createArray(arrayCreator, value) {
- return arrayCreator(value);
- }
- let items = createArray(Array.of, value);
- //在这段代码中心createArray()函数接受两个参数,一个是数组创造者函数,另一个是要插入数组的值。
//可以传入Array.of()作为createArray()方法的第一个参数来创建新数组,如果不能保证传入的值一定不是数字,那么直接传入Array会非常危险
注意:Array.of()方法不通过Symbol.species属性确定返回值的类型,它使用当前构造函数(也就是of()方法中的this值)来确定正确的返回数据的类型
2、【Array.from()】
JS不支持直接将非数组对象转换为真实数组,arguments就是一种类数组对象,如果要把它当作数组使用则必须先转换该对象的类型。在ES5中,可能需要编写如下函数来把类数组对象转换为数组
- function makeArray(arrayLike) {
- var result = [];
- for (var i = , len = arrayLike.length; i < len; i++) {
- result.push(arrayLike[i]);
- }
- return result;
- }
- function doSomething() {
- var args = makeArray(arguments);
- // 使用 args
- }
这种方法先是手动创建一个result数组,再将arguments对象里的每一个元素复制到新数组中。尽管这种方法有效,但需要编写很多代码才能完成如此简单的操作。最终,开发者们发现了一种只需编写极少代码的新方法,调用数组原生的slice()方法可以将非数组对象转换为数组
- function makeArray(arrayLike) {
- return Array.prototype.slice.call(arrayLike);
- }
- function doSomething() {
- var args = makeArray(arguments);
- // 使用 args
- }
这段代码的功能等价于之前的示例,将slice()方法执行时的this值设置为类数组对象,而slice()对象只需数值型索引和length属性就能够正确运行,所以任何类数组对象都能被转换为数组
尽管这项技术不需要编写很多代码,但是我们调用Array.prototype.slice.call(arrayLike)时不能直觉地想到这是在将arrayLike转换成一个数组。所幸,ES6添加了一个语义清晰、语法简洁的新方法Array.from()来将对象转化为数组
Array.from()方法可以接受可迭代对象或类数组对象作为第一个参数,最终返回一个数组
- function doSomething() {
- var args = Array.from(arguments);
- // 使用 args
- }
Array.from()方法调用会基于arguments对象中的元素创建一个新数组,args是Array的一个实例,包含arguments对象中同位置的相同值
注意:Array.from()方法也是通过this来确定返回数组的类型的
二、映射转换
如果想要进一步转化数组,可以提供一个映射函数作为Array.from()的第二个参数,这个函数用来将类数组对象中的每一个值转换成其他形式,最后将这些结果储存在结果数组的相应索引中
- function translate() {
- return Array.from(arguments, (value) => value + 1);
- }
- let numbers = translate(, , );
- console.log(numbers); // 2,3,4
在这段代码中,为Array.from()方法传入映射函数(value)=>value+1,数组中的每个元素在储存前都会被加1。如果用映射函数处理对象,也可以给Array.from()方法传入第三个参数来表示映射函数的this值
- let helper = {
- diff: ,
- add(value) {
- return value + this.diff;
- }
- };
- function translate() {
- return Array.from(arguments, helper.add, helper);
- }
- let numbers = translate(, , );
- console.log(numbers); // 2,3,4
此示例传入helper.add()作为转换用的映射函数,由于该方法使用了this.diff属性,因此需要为Array.from()方法提供第三个参数来指定this的值,从而无须通过调用bind()方法或其他方式来指定this的值了
三、用Array.from()转换可迭代对象
Array.from()方法可以处理类数组对象和可迭代对象,也就是说该方法能够将所有含有Symbol.iterator属性的对象转换为数组
- let numbers = {
- *[Symbol.iterator]() {
- yield ;
- yield ;
- yield ;
- }
- };
- let numbers2 = Array.from(numbers, (value) => value + );
- console.log(numbers2); // 2,3,4
由于numbers是一个可迭代对象,因此可以直接将它传入Array.from()来转换成数组。此处的映射函数将每一个数字加1,所以结果数组最终包含的值为2、3和4
注意:如果一个对象既是类数组又是可迭代的,那么Array.from()方法会根据迭代器来决定转换哪个值
四、实例方法
ES6延续了ES5的一贯风格,也为数组添加了几个新的方法:
includes()方法返回一个布尔值,表示数组是否包含给定的值;
find()方法和findIndex()方法可以协助开发者在数组中查找任意值;
fill()方法和copyWithin()方法的灵感则来自于定型数组的使用过程,定型数组也是ES6中的新特性,是一种只包含数字的数组
1、【includes()】
Array.prototype.includes
方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes
方法类似。ES2016 引入了该方法
- [, , ].includes() // true
- [, , ].includes() // false
- [, , NaN].includes(NaN) // true
该方法的第二个参数表示搜索的起始位置,默认为0
。如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4
,但数组长度为3
),则会重置为从0
开始
- [, , ].includes(, ); // false
- [, , ].includes(, -); // true
没有该方法之前,我们通常使用数组的indexOf
方法,检查是否包含某个值。indexOf
方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1
,表达起来不够直观。二是,它内部使用严格相等运算符(===
)进行判断,这会导致对NaN
的误判
- [NaN].indexOf(NaN)// -1
includes
使用的是不一样的判断算法,就没有这个问题
- [NaN].includes(NaN)// true
下面代码用来检查当前环境是否支持该方法,如果不支持,部署一个简易的替代版本
- const contains = (() =>
- Array.prototype.includes
- ? (arr, value) => arr.includes(value)
- : (arr, value) => arr.some(el => el === value)
- )();
- contains(['foo', 'bar'], 'baz'); // => false
另外,Map 和 Set 数据结构有一个has
方法,需要注意与includes
区分
(1)Map 结构的has
方法,是用来查找键名的,比如Map.prototype.has(key)
、WeakMap.prototype.has(key)
、Reflect.has(target, propertyKey)
(2)Set 结构的has
方法,是用来查找值的,比如Set.prototype.has(value)
、WeakSet.prototype.has(value)
2、【find()和findIndex()】
由于没有内建的数组搜索方法,因此ES5正式添加了indexOf()和lastIndexOf()两个方法,可以用它们在数组中查找特定的值。虽然这是一个巨大的进步,但这两种方法仍有局限之处,即每次只能查找一个值,如果想在系列数字中查找第一个偶数,则必须自己编写代码来实现。于是ES6引入了find()方法和findIndex()方法来解决这个问题
find()方法和findIndex()方法都接受两个参数:一个是回调函数;另一个是可选参数,用于指定回调函数中this的值。执行回调函数时,传入的参数分别为数组中的某个元素、该元素在数组中的索引和数组本身,与传入map()和forEach()方法的参数相同。如果给定的值满足定义的标准,回调函数应返回true。一旦回调函数返回true,find()方法和findIndex()方法都会立即停止搜索数组剩余的部分
二者间唯一的区别是,find()方法返回查找到的值,findIndex()方法返回查找到的值的索引
- let numbers = [, , , , ];
- console.log(numbers.find(n => n > )); //
- console.log(numbers.findIndex(n => n > )); //
如果要在数组中根据某个条件查找匹配的元素,那么find()方法和findIndex()方法可以很好地完成任务;如果只想查找与某个值匹配的元素,则indexOf()方法和lastIndexOf()方法是更好的选择
3、【fill()】
fill()方法可以用指定的值填充一至多个数组元素。当传入一个值时,fill()方法会用这个值重写数组中的所有值
- let numbers = [, , , ];
- numbers.fill();
- console.log(numbers.toString()); // 1,1,1,1
在此示例中,调用numbers.fill(1)方法后numbers中所有的值会变成1,如果只想改变数组某一部分的值,可以传入开始索引和不包含结束索引(不包含结束索引当前值)这两个可选参数
- let numbers = [, , , ];
- numbers.fill(, );
- console.log(numbers.toString()); // 1,2,1,1
- numbers.fill(, , );
- console.log(numbers.toString()); // 1,0,0,1
注意:如果开始索引或结束索引为负值,那么这些值会与数组的length属性相加来作为最终位置。例如,如果开始位置为-1,那么索引的值实际为array.length-1,array为调用fill()方法的数组
4、【copyWithin()】
copyWithin()方法与fill()方法相似,其也可以同时改变数组中的多个元素。fill()方法是将数组元素赋值为一个指定的值,而copyWithin()方法则是从数组中复制元素的值。调用copyWithin()方法时需要传入两个参数:一个是该方法开始填充值的索引位置,另一个是开始复制值的索引位置
比如复制数组前两个元素的值到后两个元素
- let numbers = [, , , ];
- // 从索引 2 的位置开始粘贴
- // 从数组索引 0 的位置开始复制数据
- numbers.copyWithin(, );
- console.log(numbers.toString()); // 1,2,1,2
默认情况下,copyWithin()会一直复制直到数组末尾的值,但是可以提供可选的第三个参数来限制被重写元素的数量。第三个参数是不包含结束索引,用于指定停止复制值的位置
- let numbers = [, , , ];
- // 从索引 2 的位置开始粘贴
- // 从数组索引 0 的位置开始复制数据
- // 在遇到索引 1 时停止复制
- numbers.copyWithin(, , );
- console.log(numbers.toString()); // 1,2,1,4
注意:正如fill()方法一样,copyWithin()方法的所有参数都接受负数值,并且会自动与数组长度相加来作为最终使用的索引
fill()和copyWithin()这两个方法起源于定型数组,为了保持数组方法的一致性才添加到常规数组中的。如果使用定型数组来操作数字的比特,这些方法将大显身手。
ES里关于数组的拓展的更多相关文章
- ES里关于对象的拓展
一.对象类别 在浏览器这样的执行环境中,对象没有统一的标准,在标准中又使用不同的术语描述对象,ES6规范清晰定义了每一个类别的对象,对象的类别如下 1.普通(Ordinary)对象:具有JS对象所有的 ...
- ES6 数组方法拓展
ES6 数组方法拓展 1.Array.from() Array.from方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括E ...
- ES6 随记(3.3)-- 数组的拓展
上一章请见: 1. ES6 随记(1)-- let 与 const 2. ES6 随记(2)-- 解构赋值 3. ES6 随记(3.1)-- 字符串的拓展 4. ES6 随记(3.2)-- 正则的拓展 ...
- lucene IndexOptions可以设置DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS DOCS,ES里也可以设置
org.apache.lucene.index Enum Constants Enum Constant and Description DOCS_AND_FREQS Only documents ...
- jquery里把数组转换成json的方法
首先来看,jquery里自带的,和json相关的函数: 1.$.parseJSON : 用来解析JSON字符串,返回一个对象. 什么叫“JSON字符串”? 比如: var a={name:&quo ...
- php里input数组的应用
一般我们使用input传递表单数据时,可以使用<input name="xxx[]" value="1"><input name=" ...
- jquery里判断数组内是否包含了指定的值或元素的方法
本文讲的是在jquery里,如何判断一个数组里是否包含了指定的值,变量,或其它对象元素的方法. 在jquery里,我们可以用$.inArray来判断一个数组里是否包含了指定的值或其它对象元素,来看一个 ...
- ES6里关于类的拓展(一)
大多数面向对象的编程语言都支持类和类继承的特性,而JS却不支持这些特性,只能通过其他方法定义并关联多个相似的对象,这种状态一直延续到了ES5.由于类似的库层出不穷,最终还是在ECMAScript 6中 ...
- ES6里关于函数的拓展(三)
一.箭头函数 在ES6中,箭头函数是其中最有趣的新增特性.顾名思义,箭头函数是一种使用箭头(=>)定义函数的新语法,但是它与传统的JS函数有些许不同,主要集中在以下方面: 1.没有this.su ...
随机推荐
- 关系、关系模式、关系模型blablabla...
好久没碰<数据库系统概论>了,最近翻了翻发现好多专业名词都忘记了, 现在有空特地记录下. 名词解释 数据:数据就是数据库中存储的基本数据,比如学生的学号.学生的班级数据库:存放数据的仓库数 ...
- msvs命令行编译lua5.3.4
msvs命令行编译lua5.3.4 vslua.bat @echo off md bin md lib md include cd src cl /c /nologo /W3 /O2 /Ob1 /Oi ...
- [转]Linux下阅读源代码:(g)vim+Taglist+ctags
Linux下阅读源代码的方法很多,聪明人从标题应该就可以知道,需要(g)vim+Taglist+ctags.3者配合,真是珠联璧合,功力无限啊! vim/gvim什么是vim/gvim,如果看官连 ...
- 块元素包裹img空隙问题
解决方法 1.img设为block2.img对齐方式vertical-align:bottom(middle/top),不是baseline就行3.父容器font-size:0或line-height ...
- poj1679 次最小生成树 kruskal(暴力枚举)
Description Given a connected undirected graph, tell if its minimum spanning tree is unique. Definit ...
- jquerydom对象和字符串之间的转换
字符串转jquery对象:var tmp = $('<div>dd</div>').attr('id','bbq'); //用$符包裹起来即可 jquery对象转字符串: tm ...
- tips 前端 点击事件
新手总是时不时会纠结一下 点击事件 我们都知道这些小东西不难 但是偶尔难道不会想想我们可能对这些即使小kiss的问题的认知其实不够清晰 一个认识不清晰的东西使用时 总会有油然而生的不安感 从而用的不放 ...
- tips 前端 移动端 web iscroll 5 自译文档 api速查
iscroll 可以做的 1,模拟原生的ios 或者android等设备的元素滚动,app里的那种顺滑的滚动,仅仅使用一个轻量的js库实现(甚至更酷炫的视觉感受) 2,手机端流行的下拉刷新,ajax异 ...
- 让Vs2010支持 Css3+HTML5
第一步. 先到微软官方下载一个 Microsoft Visual Studio 2010 sp1 . 给传送门:.microsoft.com/downloads/zh-cn/details.aspx? ...
- webservice soapheader验证方法
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...