JS数组API
截止ES7规范,数组共包含33个标准的API方法和一个非标准的API方法,使用场景和使用方案纷繁复杂,其中有不少浅坑、深坑、甚至神坑。
push():向数组尾部添加元素。
pop():删除数组最后一个元素。不接受参数
shift():删除数组的第一个元素。不接受参数
unshift():向数组头部添加元素。
Array构造器
Array构造器用于创建一个新的数组。通常,我们推荐使用对象字面量创建数组,这是一个好习惯,但是总有对象字面量乏力的时候,比如说,我想创建一个长度为8的空数组。请比较如下两种方式:
// 使用Array构造器 var a = Array(8); // [undefined × 8] // 使用对象字面量 var b = []; b.length = 8; // [undefined × 8]
Array构造器明显要简洁一些。
如上,我使用了Array(8)
而不是new Array(8)
,这会有影响吗?实际上,并没有影响,这得益于Array构造器内部对this指针的判断,ELS5_HTML规范是这么说的:
When
Array
is called as a function rather than as a constructor, it creates and initialises a new Array object. Thus the function callArray(…)
is equivalent to the object creation expressionnew Array(…)
with the same arguments.
从规范来看,浏览器内部大致做了如下类似的实现:
function Array(){ // 如果this不是Array的实例,那就重新new一个实例 if(!(this instanceof arguments.callee)){ return new arguments.callee(); } }
Array构造器根据参数长度的不同,有如下两种不同的处理:
- new Array(arg1, arg2,…),参数长度为0或长度大于等于2时,传入的参数将按照顺序依次成为新数组的第0至N项(参数长度为0时,返回空数组)。
- new Array(len),当len不是数值时,处理同上,返回一个只包含len元素一项的数组;当len为数值时,根据如下规范,len最大不能超过32位无符号整型,即需要小于2的32次方(len最大为
Math.pow(2,32) -1
或-1>>>0
),否则将抛出RangeError。
If the argument len is a Number and ToUint32(len) is equal to len, then the
length
property of the newly constructed object is set to ToUint32(len). If the argument len is a Number and ToUint32(len) is not equal to len, a RangeError exception is thrown.
以上,请注意Array构造器对于单个数值参数的特殊处理,如果仅仅需要使用数组包裹若干参数,不妨使用Array.of。
ES6新增的构造函数方法
(1) Array.of():
用于将参数依次转化为数组中的一项,然后返回这个新数组,而不管这个参数是数字还是其它。它基本上与Array构造器功能一致,唯一的区别就在单个数字参数的处理上
Array.of(8.0); // [8]
Array(8.0); // [undefined × 8]
(2) Array.from:
语法:Array.from(arrayLike[, processingFn[, thisArg]])
arrayLike
想要转换成数组的伪数组对象或可迭代对象。
mapFn (可选参数)
如果指定了该参数,新数组中的每个元素会执行该回调函数。
thisArg (可选参数)
可选参数,执行回调函数 mapFn 时 this 对象。
I、arrayLike 是想要转换成数组的伪数组对象或可迭代对象。
II、mapFn 是新数组执行的 map() 方法,可选。
III、thisArg 是执行 map() 时的 this 对象,可选。
Array.from拥有3个形参,第一个为类似数组的对象,必选。第二个为加工函数,新生成的数组会经过该函数的加工再返回。第三个为this作用域,表示加工函数执行时this的值。后两个参数都是可选的。
快速便捷的基于其他对象创建新数组,准确来说就是从一个类似数组的可迭代对象创建一个新的数组实例,说人话就是,只要一个对象有迭代器,Array.from就能把它变成一个数组(当然,是返回新的数组,不改变原对象)。
注意,一旦使用加工函数,必须明确指定返回值,否则将隐式返回undefined,最终生成的数组也会变成一个只包含若干个undefined元素的空数组。
如果不需要指定this,加工函数完全可以是一个箭头函数。
除了上述obj对象以外,拥有迭代器的对象还包括这些:String,Set,Map,arguments等,Array.from统统可以处理。
还有一个重要的扩展场景必须提下。比如说生成一个从0到指定数字的新数组,Array.from就可以轻易的做到。
Array.from({length: 10}, (v, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
(3) Array.isArray
顾名思义,Array.isArray用来判断一个变量是否数组类型。JS的弱类型机制导致判断变量类型是初级前端开发者面试时的必考题,一般我都会将其作为考察候选人第一题,然后基于此展开。在ES6提供该方法之前,ES5至少有如下5种方式判断一个值是否数组:
注意:Array.isArray是ES 5.1推出的,不支持IE6~8,所以在使用的时候也应注意兼容问题。
var a = []; // 1.基于instanceof a instanceof Array; // 2.基于constructor a.constructor === Array; // 3.基于Object.prototype.isPrototypeOf Array.prototype.isPrototypeOf(a); // 4.基于getPrototypeOf Object.getPrototypeOf(a) === Array.prototype; // 5.基于Object.prototype.toString Object.prototype.toString.apply(a) === '[object Array]';
以上,除了Object.prototype.toString外,其它方法都不能正确判断变量的类型。
实际上,通过Object.prototype.toString去判断一个值的类型,也是各大主流库的标准。因此Array.isArray的polyfill通常长这样:
if (!Array.isArray){ Array.isArray = function(arg){ return Object.prototype.toString.call(arg) === '[object Array]'; }; };
注意:通过上面的实例,我们看到使用数组推导来创建新数组比forEach,map,filter等遍历方法更加简洁,只是非常可惜,它不是标准规范。
(https://www.jianshu.com/p/44ba37660b4a)
原型
ES6不仅新增了对Array构造器相关API,还新增了6个原型的方法。
继承的常识告诉我们,js中所有的数组方法均来自于Array.prototype,和其他构造函数一样,你可以通过扩展 Array 的 prototype 属性上的方法来给所有数组实例增加方法。
值得一说的是,Array.prototype本身就是一个数组。
方法
数组原型提供的方法非常之多,主要分为三种,一种是会改变自身值的,一种是不会改变自身值的,另外一种是遍历方法。
由于 Array.prototype 的某些属性被设置为[[DontEnum]],因此不能用一般的方法进行遍历,我们可以通过如下方式获取 Array.prototype 的所有方法:
Object.getOwnPropertyNames(Array.prototype); // ["length", "constructor", "toString", "toLocaleString", "join", "pop", "push", "reverse", "shift", "unshift", "slice", "splice", "sort", "filter", "forEach", "some", "every", "map", "indexOf", "lastIndexOf", "reduce", "reduceRight", "copyWithin", "find", "findIndex", "fill", "includes", "entries", "keys", "concat"]
改变自身值的方法(9个)
基于ES6,改变自身值的方法一共有9个,分别为pop、push、reverse、shift、sort、splice、unshift,以及两个ES6新增的方法copyWithin 和 fill。
注意:对于能改变自身值的数组方法,日常开发中需要特别注意,尽量避免在循环遍历中去改变原数组的项。
(4) pop()
pop() 方法删除一个数组中的最后的一个元素,并且返回这个元素。如果是栈的话,这个过程就是栈顶弹出。
(5) push()
push()方法添加一个或者多个元素到数组末尾,并且返回数组新的长度。如果是栈的话,这个过程就是栈顶压入。
(6) reverse()
reverse()方法颠倒数组中元素的位置,第一个会成为最后一个,最后一个会成为第一个,该方法返回对数组的引用。
(7) shift()
shift()方法删除数组的第一个元素,并返回这个元素。
(8) sort()
语法:arr.sort([compareFunction])
sort()方法对数组元素进行排序,并返回这个数组。sort方法比较复杂,这里我将多花些篇幅来讲这块。
comparefunction是可选的,如果省略,数组元素将按照各自转换为字符串的Unicode(万国码)位点顺序排序,例如"Boy"将排到"apple"之前。当对数字排序的时候,
25将会排到8之前,因为转换为字符串后,"25"将比"8"靠前。例如:
如果指明了compareFunction,数组将按照调用该函数的返回值来排序。若 a 和 b 是两个将要比较的元素:
- 若 comparefn(a, b) < 0,那么a 将排到 b 前面;
- 若 comparefn(a, b) = 0,那么a 和 b 相对位置不变;
- 若 comparefn(a, b) > 0,那么a , b 将调换位置;
如果数组元素为数字,则排序函数compareFn格式如下所示:
function compare(a, b){
return a-b;
}
如果数组元素为非ASCII字符的字符串(如包含类似 e、é、è、a、ä 或中文字符等非英文字符的字符串),则需要使用String.localeCompare。
为什么上面测试中需要重新给array赋值呢,这是因为sort每次排序时改变的是数组本身,并且返回数组引用。如果不这么做,经过连续两次排序后,array2 和 array3 将指向同一个数组,最终影响我们测试。array重新赋值后就断开了对原数组的引用。
compareFunction 可能需要对元素做多次映射以实现排序,尤其当 compareFunction 较为复杂,且元素较多的时候,某些 compareFunction 可能会导致很高的负载。使用map 辅助排序将会是一个好主意。基本思想是首先将数组中的每个元素比较的实际值取出来,排序后再将数组恢复。(使用映射改善排序)
注意:ECMAscript规范中并未规定具体的sort算法,这就势必导致各个浏览器不尽相同的sort算法。
(9) splice()
语法:arr.splice(start,deleteCount[, item1[, item2[, …]]])
splice()方法用新元素替换旧元素的方式来修改数组。splice是一个常用的方法,复杂的数组操作的场景通常都会有它的身影特别是需要维持原数组引用时,就地删除或者新增元素,splice是最适合的。
start 指定从哪一位开始修改内容。如果超过了数组长度,则从数组末尾开始添加内容;如果是负值,则其指定的索引位置等同于 length+start (length为数组的长度),表示从数组末尾开始的第 -start 位。
deleteCount 指定要删除的元素个数,若等于0,则不删除。这种情况下,至少应该添加一位新元素,若大于start之后的元素总和,则start及之后的元素都将被删除。
itemN 指定新增的元素,如果缺省,则该方法只删除数组元素。
返回值 由原数组中被删除元素组成的数组,如果没有删除,则返回一个空数组。
注意:如果类数组对象没有length属性,splice将为该类数组对象添加length属性,并初始化为0。
(10)unshift()
unshift() 方法用于在数组开始处插入一些元素(就像是栈底插入),并返回数组新的长度。
注意:如果类数组对象不指定length属性,shift会认为数组长度为0,此时将从对象下标为0的位置开始插入,相应位置属性将被替换,此时初始化类数组对象的length属性为插入元素个数。
(11)copyWithin()
语法:arr.copyWithin(target, start[, end = this.length])
copyWithin() 方法用于数组内的元素替换,即替换元素和被替换元素均是数组内的元素。
taget 指定被替换元素的索引,start 指定替换元素起始的索引,end 可选,指的是替换元素结束位置的索引。
如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。
var array = [1,2,3,4,5];
var array2 = array.copyWithin(0,3); console.log(array===array2,array2); // true [4, 5, 3, 4, 5] var array = [1,2,3,4,5]; console.log(array.copyWithin(0,3,4)); // [4, 2, 3, 4, 5] var array = [1,2,3,4,5]; console.log(array.copyWithin(0,-2,-1)); // [4, 2, 3, 4, 5]
注:目前只有Firefox(版本32及其以上版本)实现了该方法。如需在Firefox之外的浏览器使用copyWithin方法,请参考 Polyfill。
(12)fill()
语法:arr.fill(value, start[, end = this.length])
fill() 方法同样用于数组元素替换,它可将数组指定区间内的元素替换为某个值。
value 指定被替换的值,start 指定替换元素起始的索引,end 可选,指的是替换元素结束位置的索引。
如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。
var array = [1,2,3,4,5]; var array2 = array.fill(10,0,3); console.log(array===array2,array2); // true [10, 10, 10, 4, 5], 可见数组区间[0,3]的元素全部替换为10
注:目前只有Firefox(版本31及其以上版本)实现了该方法。如需在Firefox之外的浏览器使用fill方法,请参考 Polyfill。
不会改变自身的方法(9个)
基于ES7,不会改变自身的方法一共有9个,分别为concat、join、slice、toString、toLocateString、indexOf、lastIndexOf、未标准的toSource以及ES7新增的方法includes。
(13)concat()
语法:arr.concat(value1, value2, ..., valueN)
concat() 方法将传入的数组或者元素与原数组合并,组成一个新的数组并返回。
用于连接两个或多个数组。该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
var array = [1, 2, 3]; var array2 = array.concat(4,[5,6],[7,8,9]); console.log(array2); // [1, 2, 3, 4, 5, 6, 7, 8, 9] console.log(array); // [1, 2, 3], 可见原数组并未被修改
(14)join()
语法:arr.join([separator = ','])
separator可选,缺省默认为逗号。
join() 方法将数组中的所有元素连接成一个字符串。
(15)slice()
语法:arr.slice(start[, end])
slice() 方法将数组中一部分元素浅复制存入新的数组对象,并且返回这个数组对象。
参数 start 指定复制开始位置的索引,end如果有值则表示复制结束位置的索引(不包括此位置)。
如果 start 的值为负数,假如数组长度为 length,则表示从 length+start 的位置开始复制,此时参数 end 如果有值,只能是比 start 大的负数,否则将返回空数组。
var array = ["one", "two", "three","four", "five"]; var array2 = array.slice(2,3); console.log(array2); // ["three"]
浅复制 是指当对象的被复制时,只是复制了对象的引用,指向的依然是同一个对象。下面来说明slice为什么是浅复制。
var array = [{color:"yellow"}, 2, 3]; var array2 = array.slice(0,1); console.log(array2); // [{color:"yellow"}] array[0]["color"] = "blue"; console.log(array2); // [{color:"bule"}]
由于slice是浅复制,复制到的对象只是一个引用,改变原数组array的值,array2也随之改变。
同时,稍微利用下 slice 方法第一个参数为负数时的特性,我们可以非常方便的拿到数组的最后一项元素,如下:
console.log([1,2,3].slice(-1));//[3]
(16)toString()
语法: arr.toString()
toString() 方法返回数组的字符串形式,该字符串由数组中的每个元素的 toString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
var array = ['Jan', 'Feb', 'Mar', 'Apr']; var str = array.toString(); console.log(str); // Jan,Feb,Mar,Apr
当数组直接和字符串作连接操作时,将会自动调用其toString() 方法。
var str = ['Jan', 'Feb', 'Mar', 'Apr'] + ',May'; console.log(str); // "Jan,Feb,Mar,Apr,May"
(17)toLocaleString()
语法:arr.toLocaleString()
toLocaleString() 类似toString()的变型,该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
(18)indexOf()
语法:arr.indexOf(element, fromIndex=0)
indexOf() 方法用于查找元素在数组中第一次出现时的索引,如果没有,则返回-1。
element 为需要查找的元素。
fromIndex 为开始查找的位置,缺省默认为0。如果超出数组长度,则返回-1。如果为负值,假设数组长度为length,则从数组的第 length + fromIndex项开始往数组末尾查找,如果length + fromIndex<0 则整个数组都会被查找。
indexOf使用严格相等(即使用 === 去匹配数组中的元素)。
var array = ['abc', 'def', 'ghi','123']; console.log(array.indexOf('def')); // 1 console.log(array.indexOf('def',-1)); // -1 此时表示从最后一个元素往后查找,因此查找失败返回-1 console.log(array.indexOf('def',-4)); // 1 由于4大于数组长度,此时将查找整个数组,因此返回1 console.log(array.indexOf(123)); // -1, 由于是严格匹配,因此并不会匹配到字符串'123'
(19)lastIndexOf()
语法:arr.lastIndexOf(element, fromIndex=length-1)
lastIndexOf() 方法用于查找元素在数组中最后一次出现时的索引,如果没有,则返回-1。并且它是indexOf的逆向查找,即从数组最后一个往前查找。element 为需要查找的元素。
fromIndex 为开始查找的位置,缺省默认为数组长度length-1。如果超出数组长度,由于是逆向查找,则查找整个数组。如果为负值,则从数组的第 length + fromIndex项开始往数组开头查找,如果length + fromIndex<0 则数组不会被查找。
同 indexOf 一样,lastIndexOf 也是严格匹配数组元素。
(20)includes(ES7)
语法:arr.includes(element, fromIndex=0)
includes() 方法基于ECMAScript 2016(ES7)规范,它用来判断当前数组是否包含某个指定的值,如果是,则返回 true,否则返回 false。
element 为需要查找的元素。
fromIndex 表示从该索引位置开始查找 element,缺省为0,它是正向查找,即从索引处往数组末尾查找。
var array = [1, 2, NaN]; console.log(array.includes(1)); // true console.log(array.includes(NaN)); // true console.log(array.includes(2,-4)); // true
(21)toSource()
语法:arr.toSource()
toSource() 方法是非标准的,该方法返回数组的源代码,目前只有 Firefox 实现了它。
var array = ['a', 'b', 'c']; console.log(array.toSource()); // ["a", "b", "c"]
遍历方法(12个)
基于ES6,不会改变自身的方法一共有12个,分别为forEach、every、some、filter、map、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values。
(22)forEach()
语法:arr.forEach(fn, thisArg)
forEach() 方法指定数组的每项元素都执行一次传入的函数,返回值为undefined。
fn 表示在数组每一项上执行的函数,接受三个参数:
- value 当前正在被处理的元素的值
- index 当前元素的数组索引
- array 数组本身
thisArg 可选,用来当做fn函数内的this对象。
forEach 将为数组中每一项执行一次 fn 函数,那些已删除,新增或者从未赋值的项将被跳过(但不包括值为 undefined 的项)。
遍历过程中,fn会被传入上述三个参数。
var array = [1, 3, 5]; var obj = {name:'cc'}; var sReturn = array.forEach(function(value, index, array){ array[index] = value * value; console.log(this.name); // cc被打印了三次 },obj); console.log(array); // [1, 9, 25], 可见原数组改变了 console.log(sReturn); // undefined, 可见返回值为undefined
注:参考前面的文章 详解JS遍历 中 forEach的讲解,我们知道,forEach无法直接退出循环,只能使用return 来达到for循环中continue的效果,并且forEach不能在低版本IE(6~8)中使用,兼容写法请参考 Polyfill。
(23)every()
every() 方法使用传入的函数测试所有元素,只要其中有一个函数返回值为 false,那么该方法的结果为 false;如果全部返回 true,那么该方法的结果才为 true。因此 every 方法存在如下规律:
若需检测数组中存在元素大于100 (即 one > 100),那么我们需要在传入的函数中构造 "false" 返回值 (即返回 item <= 100),同时整个方法结果为 false 才表示数组存在元素满足条件;(简单理解为:若是单项判断,可用 one false ===> false)
若需检测数组中是否所有元素都大于100 (即all > 100)那么我们需要在传入的函数中构造 "true" 返回值 (即返回 item > 100),同时整个方法结果为 true 才表示数组所有元素均满足条件。(简单理解为:若是全部判断,可用 all true ===> true)
语法同上述forEach,具体还可以参考 详解JS遍历 中every的讲解。
(24)some()
some() 方法刚好同 every() 方法相反,some 测试数组元素时,只要有一个函数返回值为 true,则该方法返回 true,若全部返回 false,则该方法返回 false。some 方法存在如下规律:
若需检测数组中存在元素大于100 (即 one > 100),那么我们需要在传入的函数中构造 "true" 返回值 (即返回 item > 100),同时整个方法结果为 true 才表示数组存在元素满足条件;(简单理解为:若是单项判断,可用 one true ===> true)
若需检测数组中是否所有元素都大于100(即 all > 100),那么我们需要在传入的函数中构造 "false" 返回值 (即返回 item <= 100),同时整个方法结果为 false 才表示数组所有元素均满足条件。(简单理解为:若是全部判断,可用 all false ===> false)
你注意到没有,some方法与includes方法有着异曲同工之妙,他们都是探测数组中是否拥有满足条件的元素,一旦找到,便返回true。多观察和总结这种微妙的关联关系,能够帮助我们深入理解它们的原理。
some 的鸭式辨型写法可以参照every,同样它也不能在低版本IE(6~8)中使用,兼容写法请参考 Polyfill。
(25)filter()
语法:arr.filter(fn, thisArg)
filter() 方法使用传入的函数测试所有元素,并返回所有通过测试的元素组成的新数组。它就好比一个过滤器,筛掉不符合条件的元素。
var array = [18, 9, 10, 35, 80]; var array2 = array.filter(function(value, index, array){ return value > 20; }); console.log(array2); // [35, 80]
filter一样支持鸭式辨型,具体请参考every方法鸭式辨型写法。其在低版本IE(6~8)的兼容写法请参考 Polyfill。
(26)map()
语法:arr.map(fn, thisArg)
map() 方法遍历数组,使用传入函数处理每个元素,并返回函数的返回值组成的新数组。
参数介绍同 forEach 方法的参数介绍。
具体用法请参考 详解JS遍历 中 map 的讲解。
map 一样支持鸭式辨型, 具体请参考every方法鸭式辨型写法。
其在低版本IE(6~8)的兼容写法请参考 Polyfill。
(27)reduce()
语法:arr.reduce(fn, initialValue)
reduce() 方法接收一个方法作为累加器,数组中的每个值(从左至右) 开始合并,最终为一个值。
fn 表示在数组每一项上执行的函数,接受四个参数:
- previousValue 上一次调用回调返回的值,或者是提供的初始值
- value 数组中当前被处理元素的值
- index 当前元素在数组中的索引
- array 数组自身
initialValue 指定第一次调用 fn 的第一个参数。
当 fn 第一次执行时:
- 如果 initialValue 在调用 reduce 时被提供,那么第一个 previousValue 将等于 initialValue,此时 item 等于数组中的第一个值;
- 如果 initialValue 未被提供,那么 previousVaule 等于数组中的第一个值,item 等于数组中的第二个值。此时如果数组为空,那么将抛出 TypeError。
- 如果数组仅有一个元素,并且没有提供 initialValue,或提供了 initialValue 但数组为空,那么fn不会被执行,数组的唯一值将被返回。
var array = [1, 2, 3, 4]; var s = array.reduce(function(previousValue, value, index, array){ return previousValue * value; },1); console.log(s); // 24 // ES6写法更加简洁 array.reduce((p, v) => p * v); // 24
以上回调被调用4次,每次的参数和返回见下表:
(28)reduceRight()
reduceRight() 方法接收一个方法作为累加器,数组中的每个值(从右至左)开始合并,最终为一个值。除了与reduce执行方向相反外,其他完全与其一致,请参考上述 reduce 方法介绍。
其在低版本IE(6~8)的兼容写法请参考 Polyfill。
(29)entries(ES6)
语法:arr.entries()
entries() 方法基于ECMAScript 2015(ES6)规范,返回一个数组迭代器对象,该对象包含数组中每个索引的键值对。
var array = ["a", "b", "c"]; var iterator = array.entries(); console.log(iterator.next().value); // [0, "a"] console.log(iterator.next().value); // [1, "b"] console.log(iterator.next().value); // [2, "c"] console.log(iterator.next().value); // undefined, 迭代器处于数组末尾时, 再迭代就会返回undefined
(30,31)find&findIndex(ES6)
语法:arr.find(fn, thisArg),arr.findIndex(fn, thisArg)
find() 方法基于ECMAScript 2015(ES6)规范,返回数组中第一个满足条件的元素(如果有的话), 如果没有,则返回undefined。
findIndex() 方法则返回数组中第一个满足条件的元素的索引(如果有的话)否则返回-1。
我们发现它们的语法与forEach等十分相似,其实不光语法,find(或findIndex)在参数及其使用注意事项上,均与forEach一致。因此此处将略去 find(或findIndex)的参数介绍。下面我们来看个例子:
var array = [1, 3, 5, 7, 8, 9, 10]; function f(value, index, array){ return value%2==0; // 返回偶数 } function f2(value, index, array){ return value > 20; // 返回大于20的数 } console.log(array.find(f)); // 8 console.log(array.find(f2)); // undefined console.log(array.findIndex(f)); // 4 console.log(array.findIndex(f2)); // -1
(32)keys(ES6)
语法:arr.keys()
keys() 方法基于ECMAScript 2015(ES6)规范,返回一个数组索引的迭代器。(浏览器实际实现可能会有调整)
var array = ["abc", "xyz"]; var iterator = array.keys(); console.log(iterator.next()); // Object {value: 0, done: false} console.log(iterator.next()); // Object {value: 1, done: false} console.log(iterator.next()); // Object {value: undefined, done: false}
索引迭代器会包含那些没有对应元素的索引,如下:
var array = ["abc", , "xyz"]; var sparseKeys = Object.keys(array); var denseKeys = [...array.keys()]; console.log(sparseKeys); // ["0", "2"] console.log(denseKeys); // [0, 1, 2]
前面我们用Array.from生成一个从0到指定数字的新数组,利用keys也很容易实现。
[...Array(10).keys()]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [...new Array(10).keys()]; // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
由于Array的特性,new Array 和 Array 对单个数字的处理相同,因此以上两种均可行。
(33)values(ES6)
语法:arr.values()
values() 方法基于ECMAScript 2015(ES6)规范,返回一个数组迭代器对象,该对象包含数组中每个索引的值。其用法基本与上述 entries 方法一致。
遗憾的是,现在没有浏览器实现了该方法,因此下面将就着看看吧。
var array = ["abc", "xyz"]; var iterator = array.values(); console.log(iterator.next().value);//abc console.log(iterator.next().value);//x yz
(34)Symbol.iterator(ES6)
语法:arr[Symbol.iterator]()
该方法基于ECMAScript 2015(ES6)规范,同 values 方法功能相同。
var array = ["abc", "xyz"]; var iterator = array[Symbol.iterator](); console.log(iterator.next().value); // abc console.log(iterator.next().value); // xyz
小结
以上,Array.prototype 的各方法基本介绍完毕,这些方法之间存在很多共性。比如:
所有插入元素的方法, 比如 push、unshift,一律返回数组新的长度;
所有删除元素的方法,比如 pop、shift、splice 一律返回删除的元素,或者返回删除的多个元素组成的数组;
部分遍历方法,比如 forEach、every、some、filter、map、find、findIndex,它们都包含function(value,index,array){} 和 thisArg 这样两个形参。
Array.prototype 的所有方法均具有鸭式辨型这种神奇的特性。它们不止可以用来处理数组对象,还可以处理类数组对象。
例如 javascript 中一个纯天然的类数组对象字符串(String),像join方法(不改变当前对象自身)就完全适用,可惜的是 Array.prototype 中很多方法均会去试图修改当前对
象的 length 属性,比如说 pop、push、shift, unshift 方法,操作 String 对象时,由于String对象的长度本身不可更改,这将导致抛出TypeError错误。
还记得么,Array.prototype本身就是一个数组,并且它的长度为0。