《JavaScript权威指南》读书笔记5 数组方法

join()

Array.join()方法将数组中所有元素都转化为字符串并连接在一起,返回最后生成的字符串。可以指定一个可选的字符串在生成的字符串中来分隔数组的各个元素。如果不指定分隔符,默认使用逗号。如以下代码所示:

var a = [1,2,3];  //创建一个包含三个元素的数组
a.join();  //=>"1,2,3"
a.join(" ");  //=>"1 2 3"
a.join("");  //=>"123"
var b = new Array(10);  //长度为10的空数组
b.join('-')  //=>'---------':9个连字号组成的字符串

Array.join()方法是String.split()方法的逆向操作,后者是将字符串分割成若干块来创建一个数组。

reverse()

Array.reverse()方法将数组中的元素颠倒顺序,返回逆序的数组。它采取了替换;换句话说,它不通过重新排列的元素创建新的数组,而是在原先的数组中重新排列它们。例如,下面的代码使用reverse()和join()方法生成字符串“3,2,1”:

var a = [1,2,3];
a.reverse().join();  //=>"3,2,1",并且现在的a是[3,2,1]

sort()

Array.sort()方法将数组中的元素排序并返回排序后的数组。当不带参数调用sort()时,数组元素以字母表顺序排序(如有必要将临时转化为字符串进行比较):

var a = new Array("banana", "cherry", "apple");
a.sort();
var s = a.join(",");  //s=="apple,banana,cherry"

如果数组包含undefined元素,它们会被排到数组的尾部。

为了按照其他方式而非字母表顺序进行数组排序,必须给sort()方法传递一个比较函数。该函数决定了它的两个参数在排好序的数组中的先后顺序。假设第一个参数应该在前,比较函数应该返回一个小于0的数值。反之,假设第一个参数应该在后,函数应该返回一个大于0的数值。并且,假设两个值相等(也就是说,它们的顺序无关紧要),函数应该返回0。因此,例如,用数值大小而非字母表顺序进行数组排序,代码如下:

var a = [33, 4, 1111, 222];
a.sort();  //字母表顺序:1111,222,33,4
a.sort(function(a, b) {   //数值顺序:4,33,222,1111
    return a - b;  //根据顺序,返回负数、0、正数
});

a.sort(function(a, b) {return b-a});//数值大小相反的顺序

注意,这里使用匿名函数表达式非常方便。既然比较函数只使用一次,就没必要给它们命名了。

另外一个数组元素排序的例子,也许需要对一个字符串数组执行不区分大小写的字母表排序,比较函数首先将参数都转化为小写字符串(使用toLowerCase()方法),再开始比较:

a = ['ant', 'Bug', 'cat', 'Dog'];
a.sort();  //区分大小写的排序:['Bug','Dog','ant',cat']

a.sort(function(s, t) {    //不区分大小写的排序
    var a = s.toLowerCase();
    var b = t.toLowerCase();

    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
});  //=>['ant','Bug','cat','Dog']

concat()

Array.concat()方法创建并返回一个新数组,它的元素包括调用concat()的原始数组的元素和concat()的每个参数。如果这些参数中的任何一个自身是数组,则连接的是数组的元素,而非数组本身。但要注意,concat()不会递归扁平化数组的数组。concat()也不会修改调用的数组。下面有一些示例:

var a = [1, 2, 3];
a.concat(4, 5);  //返回[1,2,3,4,5]
a.concat([4, 5]);  //返回[1,2,3,4,5]
a.concat([4, 5], [6, 7]);  //返回[1,2,3,4,5,6,7]
a.concat(4, [5, [6, 7]]);  //返回[1,2,3,4,5,[6,7]]

slice()

Array.slice()方法返回指定数组的一个片段或子数组。它的两个参数分别指定了片段的开始和结束的位置。返回的数组包含第一个参数指定的位置和所有到但不含第二个参数指定的位置之间的所有数组元素。如果只指定一个参数,返回的数组将包含从开始位置到数组结尾的所有元素。如参数中出现负数,它表示相对于数组中最后一个元素的位置。例如,参数-1指定了最后一个元素,而-3指定了倒数第三个元素。注意,slice()不会修改调用的数组。下面有一些示例:

var a = [1, 2, 3, 4, 5];
a.slice(0, 3);  //返回[1,2,3]
a.slice(3);  //返回[4,5]
a.slice(1, -1);  //返回[2,3,4]
a.slice(-3, -2);  //返回[3]

splice()

Array.splice()方法是在数组中插入或删除元素的通用方法。不同于slice()和concat(),splice()会修改调用的数组。注意,splice()和slice()拥有非常相似的名字,但它们的功能却有本质的区别。

splice()能够从数组中删除元素、插入元素到数组中或者同时完成这两种操作。在插入或删除点之后的数组元素会根据需要增加或减小它们的索引值,因此数组的其他部分仍然保持连续的。splice()的第一个参数指定了插入和(或)删除的起始位置。第二个参数指定了应该从数组中删除的元素的个数。如果省略第二个参数,从起始点开始到数组结尾的所有元素都将被删除。splice()返回一个由删除元素组成的数组,或者如果没有删除元素就返回一个空数组。例如:

var a = [1, 2, 3, 4, 5, 6, 7, 8];
a.splice(4);  //返回[5,6,7,8];a是[1,2,3,4]
a.splice(1, 2);  //返回[2,3];a是[1,4]
a.splice(1, 1);  //返回[4];a是[1]

splice()的前两个参数指定了需要删除的数组元素。紧随其后的任意个数的参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。例如:

var a = [1, 2, 3, 4, 5];
a.splice(2, 0, 'a', 'b');  //返回[];a是[1,2,'a','b',3,4,5]
a.splice(2, 2, [1, 2], 3);  //返回['a','b'];a是[1,2,[1,2],3,3,4,5]

注意,区别于concat(),splice()会插入数组本身而非数组的元素。

push()和pop()

push()和pop()方法允许将数组当做栈来使用。push()方法在数组的尾部添加一个或多个元素,并返回数组新的长度。pop()方法则相反:它删除数组的最后一个元素,减小数组长度并返回它删除的值。注意,两个方法都修改并替换原始数组而非生成一个修改版的新数组。组合使用push()和pop()能够用JavaScript数组实现先进后出的栈。例如:

var stack = [];  //stack:[]
stack.push(1, 2);  //stack:[1,2] 返回2
stack.pop();  //stack:[1] 返回2
stack.push(3);  //stack:[1,3] 返回2
stack.pop();  //stack:[1] 返回3
stack.push([4, 5]);  //stack:[1,[4,5]] 返回2
stack.pop();  //stack:[1] 返回[4,5]
stack.pop();  //stack:[] 返回1

unshift()和shift()

unshift()和shift()方法的行为非常类似于push()和pop(),不一样的是前者是在数组的头部而非尾部进行元素的插入和删除操作。unshift()在数组的头部添加一个或多个元素,并将已存在的元素移动到更高索引的位置来获得足够的空间,最后返回数组新的长度。shift()删除数组的第一个元素并将其返回,然后把所有随后的元素下移一个位置来填补数组头部的空缺。例如:

var a = [];  //a:[]
a.unshift(1);  //a:[1]返回:1
a.unshift(22);  //a:[22,1]返回:2
a.shift();  //a:[1]返回:22
a.unshift(3, [4, 5]);  //a:[3,[4,5],1]返回:3
a.shift();  //a:[[4,5],1]返回:3
a.shift();  //a:[1]返回:[4,5]
a.shift();  //a:[]返回:1

注意,当使用多个参数调用unshift()时它的行为令人惊讶。参数是一次性插入的(就像splice()方法)而非一次一个地插入。这意味着最终的数组中插入的元素的顺序和它们在参数列表中的顺序一致。而假如元素是一次一个地插入,它们的顺序应该是反过来的。

toString()和toLocaleString()

数组和其他JavaScript对象一样拥有toString()方法。针对数组,该方法将其每个元素转化为字符串(如有必要将调用元素的toString()方法)并且输出用逗号分隔的字符串列表。注意,输出不包括方括号或其他任何形式的包裹数组值的分隔符。例如:

[1,2,3].toString();  //生成'1,2,3'
["a","b","c"].toString();  //生成'a,b,c'
[1,[2,'c']].toString();  //生成'1,2,c'

注意,这里与不使用任何参数调用join()方法返回的字符串是一样的。

toLocaleString()是toString()方法的本地化版本。它调用元素的toLocaleString()方法将每个数组元素转化为字符串,并且使用本地化(和自定义实现的)分隔符将这些字符串连接起来生成最终的字符串。

forEach()

forEach()方法从头至尾遍历数组,为每个元素调用指定的函数。如上所述,传递的函数作为forEach()的第一个参数。然后forEach()使用三个参数调用该函数:数组元素、元素的索引和数组本身。如果只关心数组元素的值,可以编写只有一个参数的函数——额外的参数将忽略:

var data = [1, 2, 3, 4, 5];  //要求和的数组

//计算数组元素的和值
var sum = 0;  //初始为0
data.forEach(function(value) {sum += value;});  //将每个值累加到sum上
sum;  //=>15//每个数组元素的值自加1

data.forEach(function(v, i, a) {a[i] = v + 1;});
data;  //=>[2,3,4,5,6]

map()

map()方法将调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。例如:

a = [1, 2, 3];
b = a.map(function(x) {return x * x;});  //b是[1,4,9]

传递给map()的函数的调用方式和传递给forEach()的函数的调用方式一样,但传递给map()的函数应该有返回值。注意,map()返回的是新数组:它不修改调用的数组。如果是稀疏数组,返回的也是相同方式的稀疏数组:它具有相同的长度,相同的缺失元素。

filter()

filter()方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的:该函数返回true或false,调用判定函数就像调用forEach()和map()一样。如果返回值为true或能转化为true的值,那么传递给判定函数的元素就是这个子集的成员,它将被添加到一个作为返回值的数组中。例如:

a = [5, 4, 3, 2, 1];
smallvalues = a.filter(function(x) {return x < 3});  //[2,1]
everyother = a.filter(function(x, i) {return i % 2 == 0});  //[5,3,1]

注意,filter()会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的。为了压缩稀疏数组的空缺,代码如下:

var dense = sparse.filter(function() {return true;});

甚至,压缩空缺并删除undefined和null元素,可以这样使用filter():

a = a.filter(function(x) {return x !== undefined && x != null;});

every()和some()

every()和some()方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回true或false。

every()方法就像数学中的“针对所有”的量词:当且仅当针对数组中的所有元素调用判定函数都返回true,它才返回true:

a = [1, 2, 3, 4, 5];
a.every(function(x) {return x < 10;});  //=>true:所有的值<10
a.every(function(x) {return x % 2 === 0;});  //=>false:不是所有的值都是偶数

some()方法就像数学中的“存在”的量词:当数组中至少有一个元素调用判定函数返回true,它就返回true;并且当且仅当数值中的所有元素调用判定函数都返回false,它才返回false:

a = [1, 2, 3, 4, 5];
a.some(function(x) {return x % 2 === 0;});  //=>true:a含有偶数值
a.some(isNaN);  //=>false:a不包含非数值元素

注意,一旦every()和some()确认该返回什么值它们就会停止遍历数组元素。some()在判定函数第一次返回true后就返回true,但如果判定函数一直返回false,它将会遍历整个数组。every()恰好相反:它在判定函数第一次返回false后就返回false,但如果判定函数一直返回true,它将会遍历整个数组。注意,在空数组上调用时,every()返回true,some()返回false。

reduce()和reduceRight()

reduce()和reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值。这在函数式编程中是常见的操作,也可以称为“注入”和“折叠”。举例说明它是如何工作的:

var a = [1, 2, 3, 4, 5]
var sum = a.reduce(function(x, y) {return x + y}, 0);  //数组求和
var product = a.reduce(function(x, y) {return x * y},1);  //数组求积
var max = a.reduce(function(x, y) {return(x > y) ? x : y;});  //求最大值

reduce()需要两个参数。第一个是执行化简操作的函数。化简函数的任务就是用某种方法把两个值组合或化简为一个值,并返回化简后的值。在上述例子中,函数通过加法、乘法或取最大值的方法组合两个值。第二个(可选)的参数是一个传递给函数的初始值。

reduce()使用的函数与forEach()和map()使用的函数不同。比较熟悉的是,数组元素、元素的索引和数组本身将作为第2~4个参数传递给函数。第一个参数是到目前为止的化简操作累积的结果。第一次调用函数时,第一个参数是一个初始值,它就是传递给reduce()的第二个参数。在接下来的调用中,这个值就是上一次化简函数的返回值。在上面的第一个例子中,第一次调用化简函数时的参数是0和1。将两者相加并返回1。再次调用时的参数是1和2,它返回3。然后它计算3+3=6、6+4=10,最后计算10+5=15。最后的值是15,reduce()返回这个值。

可能已经注意到了,上面第三次调用reduce()时只有一个参数:没有指定初始值。当不指定初始值调用reduce()时,它将使用数组的第一个元素作为其初始值。这意味着第一次调用化简函数就使用了第一个和第二个数组元素作为其第一个和第二个参数。在上面求和与求积的例子中,可以省略初始值参数。

在空数组上,不带初始值参数调用reduce()将导致类型错误异常。如果调用它的时候只有一个值——数组只有一个元素并且没有指定初始值,或者有一个空数组并且指定一个初始值——reduce()只是简单地返回那个值而不会调用化简函数。

reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低(从右到左)处理数组,而不是从低到高。

indexOf()和lastIndexOf()

indexOf()和lastIndexOf()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回-1。indexOf()从头至尾搜索,而lastIndexOf()则反向搜索。

a = [0, 1, 2, 1, 0];
a.indexOf(1);  //=>1:a[1]是1
a.lastIndexOf(1);  //=>3:a[3]是1
a.indexOf(3);  //=>-1:没有值为3的元素

第一个参数是需要搜索的值,第二个参数是可选的:它指定数组中的一个索引,从那里开始搜索。如果省略该参数,indexOf()从头开始搜索,而lastIndexOf()从末尾开始搜索。第二个参数也可以是负数,它代表相对数组末尾的偏移量。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容