数组是从索引(自然数,从零开始)到任意值的映射。值(映射的范围)称为数组的元素。创建数组最方便的方法是通过数组字面量。这样的字面量枚举数组元素;元素的位置隐式地指定其索引。
在本章中,我将首先介绍 基本的数组机制,例如索引访问和 length 属性,然后介绍数组方法。
本节简要概述了数组。详细信息 将在后面解释。
作为第一个示例,我们通过数组字面量(参见 创建数组)创建一个数组 arr 并访问元素(参见 数组索引):
> var arr = [ 'a', 'b', 'c' ]; // array literal > arr[0] // get element 0 'a' > arr[0] = 'x'; // set element 0 > arr [ 'x', 'b', 'c' ]
我们可以使用数组属性 length(参见 length)来 删除和追加元素:
> var arr = [ 'a', 'b', 'c' ]; > arr.length 3 > arr.length = 2; // remove an element > arr [ 'a', 'b' ] > arr[arr.length] = 'd'; // append an element > arr [ 'a', 'b', 'd' ]
数组方法 push() 提供了另一种追加元素的方法:
> var arr = [ 'a', 'b' ];
> arr.push('d')
3
> arr
[ 'a', 'b', 'd' ]ECMAScript 标准将数组 指定为从索引到值的映射(字典)。 换句话说,数组可能不是连续的,并且其中可能存在空洞。例如:
> var arr = []; > arr[0] = 'a'; 'a' > arr[2] = 'b'; 'b' > arr [ 'a', , 'b' ]
前面的数组有一个空洞:索引 1 处没有元素。数组中的空洞 更详细地解释了空洞。
请注意,大多数 JavaScript 引擎在内部优化没有空洞的数组,并将它们连续存储。
数组 仍然是对象,并且可以具有对象属性。这些属性不被视为实际数组的一部分;也就是说,它们不被视为数组元素:
> var arr = [ 'a', 'b' ]; > arr.foo = 123; > arr [ 'a', 'b' ] > arr.foo 123
您可以通过数组字面量创建数组:
varmyArray=['a','b','c'];
数组中的尾随逗号将被忽略:
> [ 'a', 'b' ].length 2 > [ 'a', 'b', ].length 2 > [ 'a', 'b', ,].length // hole + trailing comma 3
有 两种使用构造函数 Array 的方法:您可以创建一个具有给定长度的空数组,或者创建一个元素为给定值的数组。对于此构造函数,new 是可选的:将其作为普通函数调用(不带 new)与将其作为构造函数调用相同。
具有给定长度的空 数组中只有空洞!因此,使用此版本的构造函数很少有意义:
> var arr = new Array(2); > arr.length 2 > arr // two holes plus trailing comma (ignored!) [ , ,]
某些引擎可能会在您以这种方式调用 Array() 时预先分配连续内存,这可能会稍微提高性能。但是,请确保增加的冗长度和冗余是值得的!
// The same as ['a', 'b', 'c']:vararr1=newArray('a','b','c');
问题是您不能创建只有一个数字的数组,因为这会被解释为创建一个 length 为该数字的数组:
> new Array(2) // alas, not [ 2 ]
[ , ,]
> new Array(5.7) // alas, not [ 5.7 ]
RangeError: Invalid array length
> new Array('abc') // ok
[ 'abc' ]如果元素需要多个维度,则必须嵌套数组。 创建此类嵌套数组时,最里面的数组可以根据需要增长。但是,如果要直接访问元素,则至少需要创建外部数组。在以下示例中,我创建了一个用于井字游戏的 3x3 矩阵。该矩阵完全填充了数据(而不是让行根据需要增长):
// Create the Tic-tac-toe boardvarrows=[];for(varrowCount=0;rowCount<3;rowCount++){rows[rowCount]=[];for(varcolCount=0;colCount<3;colCount++){rows[rowCount][colCount]='.';}}// Set an X in the upper right cornerrows[0][2]='X';// [row][column]// Print the boardrows.forEach(function(row){console.log(row.join(' '));});
以下是输出:
. . X . . . . . .
我希望该示例演示一般情况。显然,如果矩阵很小且维度固定,则可以通过数组字面量设置它:
varrows=[['.','.','.'],['.','.','.'],['.','.','.']];
使用数组索引时, 必须牢记以下限制:
超出范围的索引将被视为普通属性键(字符串!)。它们不会显示为数组元素,也不会影响属性 length。例如:
> var arr = []; > arr[-1] = 'a'; > arr [] > arr['-1'] 'a' > arr[4294967296] = 'b'; > arr [] > arr['4294967296'] 'b'
in 运算符 检测对象是否具有给定键的属性。但它也可以用来确定数组中是否存在给定的元素索引。例如:
> var arr = [ 'a', , 'b' ]; > 0 in arr true > 1 in arr false > 10 in arr false
除了删除属性外,delete 运算符 还会删除数组元素。删除元素会创建空洞(length 属性不会更新):
> var arr = [ 'a', 'b' ]; > arr.length 2 > delete arr[1] // does not update length true > arr [ 'a', ] > arr.length 2
您还可以通过减少数组的长度来删除尾随数组元素(有关详细信息,请参阅 length)。要删除元素而不创建空洞(即,后续元素的索引递减),请使用 Array.prototype.splice()(请参阅 添加和删除元素(破坏性))。在本例中,我们删除了索引 1 处的两个元素:
> var arr = ['a', 'b', 'c', 'd']; > arr.splice(1, 2) // returns what has been removed [ 'b', 'c' ] > arr [ 'a', 'd' ]
这是一个高级部分。通常,您不需要了解此处解释的详细信息。
数组索引并非表面上那样。 到目前为止,我一直假装数组 索引是数字。这就是 JavaScript 引擎在内部实现数组的方式。但是,ECMAScript 规范对索引的看法不同。改述 第 15.4 节:
P(字符串)是数组索引,当且仅当 ToString(ToUint32(P)) 等于 P 且 ToUint32(P) 不等于 232−1。稍后将解释这意味着什么。换句话说,在规范的世界中,括号中的所有值都将转换为字符串并解释为属性键,甚至是数字。以下交互演示了这一点:
> var arr = ['a', 'b']; > arr['0'] 'a' > arr[0] 'a'
要成为数组索引,属性键 P(字符串!)必须等于以下计算的结果:
P 转换为数字。这意味着数组索引必须是 32 位范围内 0 ≤ i < 232−1 的字符串化整数 i。规范中明确排除了上限(如前所述)。它保留用于最大长度。要查看此定义的工作原理,让我们使用 通过按位运算符实现 32 位整数 中的函数 ToUint32()。
首先,不包含数字的字符串始终转换为 0,字符串化后,该字符串不等于该字符串:
> ToUint32('xyz')
0
> ToUint32('?@#!')
0其次,超出范围的字符串化整数也会转换为完全不同的整数,字符串化后该整数不等于该字符串:
> ToUint32('-1')
4294967295
> Math.pow(2, 32)
4294967296
> ToUint32('4294967296')
0第三,字符串化的非整数数字将转换为整数,这些整数又是不同的:
> ToUint32('1.371')
1请注意,规范还强制数组索引没有指数:
> ToUint32('1e3')
1000并且它们没有前导零:
> var arr = ['a', 'b']; > arr['0'] // array index 'a' > arr['00'] // normal property undefined
length 属性的基本功能是 跟踪数组中的最高索引:
> [ 'a', 'b' ].length 2 > [ 'a', , 'b' ].length 3
因此,length 不会计算元素的数量,因此您必须编写自己的函数来执行此操作。例如:
functioncountElements(arr){varelemCount=0;arr.forEach(function(){elemCount++;});returnelemCount;}
为了计算元素(非空洞),我们利用了 forEach 跳过空洞的事实。以下是交互:
> countElements([ 'a', 'b' ]) 2 > countElements([ 'a', , 'b' ]) 2
手动增加数组的长度 对数组的影响非常小;它只会创建空洞:
> var arr = [ 'a', 'b' ]; > arr.length = 3; > arr // one hole at the end [ 'a', 'b', ,]
最后一个结果末尾有两个逗号,因为尾随逗号是可选的,因此始终会被忽略。
我们刚才所做的并没有添加任何元素:
> countElements(arr) 2
但是,length 属性确实充当指针,指示在何处插入新元素。例如:
> arr.push('c')
4
> arr
[ 'a', 'b', , 'c' ]因此,通过 Array 构造函数设置数组的初始长度会创建一个完全空的数组:
> var arr = new Array(2); > arr.length 2 > countElements(arr) 0
如果减少数组的 长度,则新长度及以上的所有元素都将被删除:
> var arr = [ 'a', 'b', 'c' ]; > 1 in arr true > arr[1] 'b' > arr.length = 1; > arr [ 'a' ] > 1 in arr false > arr[1] undefined
如果将数组的长度设置为 0,则它将变为空。 这允许您为其他人清空数组。例如:
functionclearArray(arr){arr.length=0;}
以下是交互:
> var arr = [ 'a', 'b', 'c' ]; > clearArray(arr) > arr []
但是请注意,这种方法可能很慢,因为每个数组元素都被显式删除。具有讽刺意味的是,创建一个新的空数组通常更快:
arr=[];
您需要注意 将数组的长度设置为零会影响共享该数组的所有人:
>vara1=[1,2,3];>vara2=a1;>a1.length=0;>a1[]>a2[]
相反,分配一个空数组则不会:
>vara1=[1,2,3];>vara2=a1;>a1=[];>a1[]>a2[1,2,3]
最大数组 长度为 232−1:
> var arr1 = new Array(Math.pow(2, 32)); // not ok
RangeError: Invalid array length
> var arr2 = new Array(Math.pow(2, 32)-1); // ok
> arr2.push('x');
RangeError: Invalid array length数组是从索引到值的映射。这意味着数组可以有空洞,即数组中缺少小于长度的索引。 读取这些索引之一处的元素将返回 undefined。
建议您避免数组中的空洞。JavaScript 处理它们的方式不一致(即,某些方法忽略它们,而其他方法则不忽略)。值得庆幸的是,您通常不需要知道如何处理空洞:它们很少有用,并且会对性能产生负面影响。
您可以通过 赋值给数组索引来创建空洞:
> var arr = []; > arr[0] = 'a'; > arr[2] = 'c'; > 1 in arr // hole at index 1 false
您也可以通过在数组字面量中省略值来创建空洞:
> var arr = ['a',,'c']; > 1 in arr // hole at index 1 false
您需要两个尾随逗号才能创建尾随空洞,因为最后一个逗号始终会被忽略:
> [ 'a', ].length 1 > [ 'a', ,].length 2
本节将研究空洞和undefined 作为元素之间的区别。鉴于读取空洞会返回 undefined,因此两者非常相似。
具有空洞的数组称为稀疏数组。 没有空洞的数组称为密集数组。密集数组是连续的,并且在每个索引处都有一个元素,从零开始,到 length − 1 结束。让我们比较以下两个数组,一个稀疏数组和一个密集数组。两者非常相似:
varsparse=[,,'c'];vardense=[undefined,undefined,'c'];
空洞几乎就像在同一索引处具有元素 undefined。两个数组的长度相同:
> sparse.length 3 > dense.length 3
但是稀疏数组在索引 0 处没有元素:
> 0 in sparse false > 0 in dense true
通过 for 进行迭代 对于两个数组都是相同的:
> for (var i=0; i<sparse.length; i++) console.log(sparse[i]); undefined undefined c > for (var i=0; i<dense.length; i++) console.log(dense[i]); undefined undefined c
通过 forEach 进行迭代会跳过 空洞,但不会跳过 undefined 元素:
> sparse.forEach(function (x) { console.log(x) });
c
> dense.forEach(function (x) { console.log(x) });
undefined
undefined
c一些涉及 数组的操作会忽略空洞,而其他操作则会考虑空洞。本节将解释详细信息。
forEach() 会跳过 空洞:
> ['a',, 'b'].forEach(function (x,i) { console.log(i+'.'+x) })
0.a
2.bevery() 也 会跳过空洞(some() 也是如此):
> ['a',, 'b'].every(function (x) { return typeof x === 'string' })
truemap() 会跳过空洞,但会保留空洞:
> ['a',, 'b'].map(function (x,i) { return i+'.'+x })
[ '0.a', , '2.b' ]filter() 可以消除 空洞:
> ['a',, 'b'].filter(function (x) { return true })
[ 'a', 'b' ]join() 会将空洞、undefined 和 null 转换为 空字符串:
> ['a',, 'b'].join('-')
'a--b'
> [ 'a', undefined, 'b' ].join('-')
'a--b'sort() 在排序时会保留空洞
> ['a',, 'b'].sort() // length of result is 3 [ 'a', 'b', , ]
apply() 会将每个空洞转换为值为 undefined 的参数。以下交互演示了这一点:函数 f() 返回其参数组成的数组。当我们传递一个包含三个空洞的数组给 apply() 以调用 f() 时,后者会收到三个 undefined 参数
> function f() { return [].slice.call(arguments) }
> f.apply(null, [ , , ,])
[ undefined, undefined, undefined ]这意味着我们可以使用 apply() 创建一个包含 undefined 的数组:
> Array.apply(null, Array(3)) [ undefined, undefined, undefined ]
apply() 会将空数组中的空洞转换为 undefined,但它不能用于填充任意数组(可能包含或不包含空洞)中的空洞。以任意数组 [2] 为例
> Array.apply(null, [2]) [ , ,]
该数组不包含任何空洞,因此 apply() 应该返回相同的数组。相反,它返回一个长度为 2 的空数组(它只包含两个空洞)。这是因为 Array() 将单个数字解释为数组长度,而不是数组元素。
正如我们所见,filter() 可以移除 空洞:
> ['a',, 'b'].filter(function (x) { return true })
[ 'a', 'b' ]使用自定义函数将任意数组中的空洞转换为 undefined
functionconvertHolesToUndefineds(arr){varresult=[];for(vari=0;i<arr.length;i++){result[i]=arr[i];}returnresult;}
使用该函数
> convertHolesToUndefineds(['a',, 'b']) [ 'a', undefined, 'b' ]
Array.isArray(obj)
obj 是数组,则返回 true。 它可以正确处理跨 域(窗口或框架)的对象,这与 instanceof 不同(请参阅 陷阱:跨域(框架或窗口))。Array.prototype.shift()
> var arr = [ 'a', 'b' ]; > arr.shift() 'a' > arr [ 'b' ]
Array.prototype.unshift(elem1?, elem2?, ...)
> var arr = [ 'c', 'd' ];
> arr.unshift('a', 'b')
4
> arr
[ 'a', 'b', 'c', 'd' ]Array.prototype.pop()
> var arr = [ 'a', 'b' ]; > arr.pop() 'b' > arr [ 'a' ]
Array.prototype.push(elem1?, elem2?, ...)
将给定元素 添加到数组的末尾。它返回新的长度:
> var arr = [ 'a', 'b' ];
> arr.push('c', 'd')
4
> arr
[ 'a', 'b', 'c', 'd' ]apply()(请参阅 Function.prototype.apply(thisValue, argArray))允许 您将数组 arr2 破坏性地附加到另一个数组 arr1:
> var arr1 = [ 'a', 'b' ]; > var arr2 = [ 'c', 'd' ]; > Array.prototype.push.apply(arr1, arr2) 4 > arr1 [ 'a', 'b', 'c', 'd' ]
Array.prototype.splice(start, deleteCount?, elem1?, elem2?, ...)
> var arr = [ 'a', 'b', 'c', 'd' ]; > arr.splice(1, 2, 'X'); [ 'b', 'c' ] > arr [ 'a', 'X', 'd' ]
特殊参数值
start 可以是负数,在这种情况下,它会添加到长度中以确定起始索引。因此,-1 指的是最后一个元素,依此类推。deleteCount 是可选的。如果省略它(以及所有后续参数),则移除索引 start 处及其后的所有元素。在本例中,我们移除了倒数第二个索引处及其后的所有元素
> var arr = [ 'a', 'b', 'c', 'd' ]; > arr.splice(-2) [ 'c', 'd' ] > arr [ 'a', 'b' ]
这些方法也是破坏性的
Array.prototype.reverse()
> var arr = [ 'a', 'b', 'c' ]; > arr.reverse() [ 'c', 'b', 'a' ] > arr // reversing happened in place [ 'c', 'b', 'a' ]
Array.prototype.sort(compareFunction?)
> var arr = ['banana', 'apple', 'pear', 'orange']; > arr.sort() [ 'apple', 'banana', 'orange', 'pear' ] > arr // sorting happened in place [ 'apple', 'banana', 'orange', 'pear' ]
请记住,排序是通过将值转换为字符串来比较值的,这意味着数字不是按数字顺序排序的
> [-1, -20, 7, 50].sort() [ -1, -20, 50, 7 ]
您可以通过提供可选参数 compareFunction 来解决此问题,该参数控制排序方式。它具有以下签名
functioncompareFunction(a,b)
此函数比较 a 和 b 并返回
a 小于 b,则返回小于零的整数(例如,-1)a 等于 b,则返回零a 大于 b,则返回大于零的整数(例如,1)对于数字,您可以 简单地返回 a-b,但这可能会导致数字溢出。为了防止这种情况发生,您需要更详细的代码:
functioncompareCanonically(a,b){if(a<b){return-1;}elseif(a>b){return1;}else{return0;}}
我不喜欢嵌套的条件运算符。但是 在这种情况下,代码简洁得多,我倾向于推荐它:
functioncompareCanonically(a,b){returna<b?-1:(a>b?1:0);}
使用该函数
> [-1, -20, 7, 50].sort(compareCanonically) [ -20, -1, 7, 50 ]
对于字符串,您可以 使用 String.prototype.localeCompare(请参阅 比较字符串)
> ['c', 'a', 'b'].sort(function (a,b) { return a.localeCompare(b) })
[ 'a', 'b', 'c' ]参数 compareFunction 对于排序对象也很有用:
vararr=[{name:'Tarzan'},{name:'Cheeta'},{name:'Jane'}];functioncompareNames(a,b){returna.name.localeCompare(b.name);}
使用 compareNames 作为比较函数,arr 按 name 排序
> arr.sort(compareNames)
[ { name: 'Cheeta' },
{ name: 'Jane' },
{ name: 'Tarzan' } ]Array.prototype.concat(arr1?, arr2?, ...)
创建一个 新数组,其中包含接收者的所有元素,后跟数组 arr1 的所有元素,依此类推。如果其中一个参数不是数组,则将其作为元素添加到结果中(例如,此处的第一个参数 'c'):
> var arr = [ 'a', 'b' ];
> arr.concat('c', ['d', 'e'])
[ 'a', 'b', 'c', 'd', 'e' ]调用 concat() 的数组不会更改
> arr [ 'a', 'b' ]
Array.prototype.slice(begin?, end?)
将数组 元素复制到一个新数组中,从 begin 开始,直到但不包括 end 处的元素:
> [ 'a', 'b', 'c', 'd' ].slice(1, 3) [ 'b', 'c' ]
如果缺少 end,则使用数组长度
> [ 'a', 'b', 'c', 'd' ].slice(1) [ 'b', 'c', 'd' ]
如果缺少两个索引,则复制数组
> [ 'a', 'b', 'c', 'd' ].slice() [ 'a', 'b', 'c', 'd' ]
如果任一索引为负数,则将数组长度添加到其中。因此,-1 指的是最后一个元素,依此类推
> [ 'a', 'b', 'c', 'd' ].slice(1, -1) [ 'b', 'c' ] > [ 'a', 'b', 'c', 'd' ].slice(-2) [ 'c', 'd' ]
Array.prototype.join(separator?)
通过 对所有数组元素应用 toString() 并将字符串放在结果之间的 separator 中来创建一个字符串。如果省略 separator,则使用 ',':
> [3, 4, 5].join('-')
'3-4-5'
> [3, 4, 5].join()
'3,4,5'
> [3, 4, 5].join('')
'345'join() 会将 undefined 和 null 转换为空字符串
> [undefined, null].join('#')
'#'数组中的空洞也会转换为空字符串
> ['a',, 'b'].join('-')
'a--b'以下方法在 数组中搜索值:
Array.prototype.indexOf(searchValue, startIndex?)
从 startIndex 开始在数组中搜索 searchValue。它返回第一次出现的索引,如果未找到则返回 -1。如果 startIndex 为负数,则将数组长度添加到其中;如果缺少它,则搜索整个数组:
> [ 3, 1, 17, 1, 4 ].indexOf(1) 1 > [ 3, 1, 17, 1, 4 ].indexOf(1, 2) 3
搜索时使用严格相等(请参阅 相等运算符:=== 与 ==),这意味着 indexOf() 找不到 NaN:
> [NaN].indexOf(NaN) -1
Array.prototype.lastIndexOf(searchElement, startIndex?)
从 startIndex 开始向后搜索数组中的 searchElement。它返回第一次出现的索引,如果未找到则返回 -1。如果 startIndex 为负数,则将数组长度添加到其中;如果缺少它,则搜索整个数组。搜索时使用严格相等(请参阅 相等运算符:=== 与 ==)
> [ 3, 1, 17, 1, 4 ].lastIndexOf(1) 3 > [ 3, 1, 17, 1, 4 ].lastIndexOf(1, -3) 1
迭代 方法使用函数迭代数组。我区分了三种迭代方法,所有这些方法都是非破坏性的:检查方法 主要观察数组的内容;转换方法 从接收者派生一个新数组;归约方法 根据接收者的元素计算结果。
本节中描述的每种方法 如下所示:
arr.examinationMethod(callback,thisValue?)
这种方法采用以下参数
callback 是它的第一个参数,它调用的函数。根据检查方法的不同,回调函数返回布尔值或不返回任何值。它具有以下签名
functioncallback(element,index,array)
element 是供 callback 处理的数组元素,index 是元素的索引,array 是调用 examinationMethod 的数组。
thisValue 允许您在 callback 内部配置 this 的值。现在介绍我刚刚描述的 签名的检查方法:
Array.prototype.forEach(callback, thisValue?)
迭代数组的元素
vararr=['apple','pear','orange'];arr.forEach(function(elem){console.log(elem);});
Array.prototype.every(callback, thisValue?)
本例检查数组中的每个数字是否都是偶数
> function isEven(x) { return x % 2 === 0 }
> [ 2, 4, 6 ].every(isEven)
true
> [ 2, 3, 4 ].every(isEven)
false如果数组为空,则结果为 true(并且不调用 callback)
> [].every(function () { throw new Error() })
trueArray.prototype.some(callback, thisValue?)
本例检查数组中是否存在偶数
> function isEven(x) { return x % 2 === 0 }
> [ 1, 3, 5 ].some(isEven)
false
> [ 1, 2, 3 ].some(isEven)
true如果数组为空,则结果为 false(并且不调用 callback)
> [].some(function () { throw new Error() })
falseforEach() 的一个潜在缺陷是它不支持 break 或类似的东西来提前中止循环。如果您需要这样做,可以使用 some()
functionbreakAtEmptyString(strArr){strArr.some(function(elem){if(elem.length===0){returntrue;// break}console.log(elem);// implicit: return undefined (interpreted as false)});}
some() 如果发生中断则返回 true,否则返回 false。这允许您根据迭代是否成功完成(使用 for 循环很难做到的事情)做出不同的反应。
转换 方法接受一个输入数组并生成一个输出数组,而回调函数控制输出的生成方式。回调函数具有与检查相同的签名:
functioncallback(element,index,array)
Array.prototype.map(callback, thisValue?)
每个输出数组元素都是将 callback 应用于输入元素的结果。例如
> [ 1, 2, 3 ].map(function (x) { return 2 * x })
[ 2, 4, 6 ]Array.prototype.filter(callback, thisValue?)
输出数组 仅包含 callback 返回 true 的输入元素。例如:
> [ 1, 0, 3, 0 ].filter(function (x) { return x !== 0 })
[ 1, 3 ]对于归约,回调函数 具有不同的签名:
functioncallback(previousValue,currentElement,currentIndex,array)
参数 previousValue 是回调函数先前返回的值。当首次调用回调函数时,有两种可能性(描述适用于 Array.prototype.reduce();括号中提到了与 reduceRight() 的区别)
initialValue。然后 previousValue 是 initialValue,而 currentElement 是第一个数组元素(reduceRight:最后一个数组元素)。initialValue。然后 previousValue 是第一个数组元素,而 currentElement 是第二个数组元素(reduceRight:最后一个数组元素和倒数第二个数组元素)。有两种归约方法
Array.prototype.reduce(callback, initialValue?)
从左到右迭代,并按前面所述调用回调函数。该方法的结果是回调函数返回的最后一个值。此示例计算所有数组元素的总和:
functionadd(prev,cur){returnprev+cur;}console.log([10,3,-1].reduce(add));// 12
如果在只有一个元素的数组上调用 reduce,则返回该元素
> [7].reduce(add) 7
如果在空数组上调用 reduce,则必须指定 initialValue,否则会引发异常
> [].reduce(add) TypeError: Reduce of empty array with no initial value > [].reduce(add, 123) 123
Array.prototype.reduceRight(callback, initialValue?)
reduce() 相同,但从右到左迭代。在许多函数式编程语言中,reduce 被称为 fold 或 foldl(左折叠),而 reduceRight 被称为 foldr(右折叠)。
查看 reduce 方法的另一种方法是,它通过一系列二元运算符 op2 的应用来实现 n 元运算符 OP
OP1≤i≤n xi
(...(x1 op2 x2) op2 ...) op2 xn
这就是上一个代码示例中发生的情况:我们通过 JavaScript 的二元加法运算符为数组实现了一个 n 元求和运算符。
例如,让我们通过以下函数来检查两个迭代方向
functionprintArgs(prev,cur,i){console.log('prev:'+prev+', cur:'+cur+', i:'+i);returnprev+cur;}
正如预期的那样,reduce() 从左到右迭代
> ['a', 'b', 'c'].reduce(printArgs) prev:a, cur:b, i:1 prev:ab, cur:c, i:2 'abc' > ['a', 'b', 'c'].reduce(printArgs, 'x') prev:x, cur:a, i:0 prev:xa, cur:b, i:1 prev:xab, cur:c, i:2 'xabc'
而 reduceRight() 从右到左迭代
> ['a', 'b', 'c'].reduceRight(printArgs) prev:c, cur:b, i:1 prev:cb, cur:a, i:0 'cba' > ['a', 'b', 'c'].reduceRight(printArgs, 'x') prev:x, cur:c, i:2 prev:xc, cur:b, i:1 prev:xcb, cur:a, i:0 'xcba'
JavaScript 中的某些对象 看起来像数组,但它们不是数组。这通常意味着它们具有索引访问和 length 属性,但没有数组方法。示例包括特殊变量 arguments、DOM 节点列表和字符串。类数组对象和泛型方法 提供了处理类数组对象的技巧。