JavaScript数组

数组是值的有序集合。每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引

JavaScript数组的索引是基于0的32位数值:第一个元素的索引是0,最大可能为4294967294(2^32 - 2)。

通常,数组的实现是经过优化的,用数字索引来访问数组元素一般比访问常规的对象属性要快很多。

实际上,数组是对象的特殊形式,索引可理解为对象的属性,使用[]访问数组时,索引首先转换为字符串,然后作为属性使用。

JavaScript数组有以下特点:

  • 数组是无类型的:数组元素可以是任意类型,并且同一个数组中的不同元素也可能有同的类型。
  • 数组是动态的:可以根据需要增长或缩减。
  • 数组可以是稀疏的:数组元素的索引不一定是连续的,它们之间可以有空缺。
  • 数组继承自Array.prototype中的属性,它定义了一套丰富的数组操作方法。

创建数组

使用直接量创建

var empty = [];                 // 空数组
var misc  = [1.1, true, "a"];   // 包含3个不同类型的数组
var count = [1,,3];             // 包含2个元素的数组,中间的值为undefined

使用new创建

var a = new Array(10);          // 定义长度为10的数组,注:元素为空
var b = new Array(5, 4, 3, "a");// 定义包含4个元素的数组,长度也为4

数组元素的读写

所有的数组都是对象,可以为其创建任意名字的属性。但如果使用的属性是数组的索引,数组就会更新length属性值。

var a = [1];        // 数组的length为 1
a[-1.23] = true;    // 数组的length为 1,只是数组多了个属性"-1.23"
a["1000"] = 0;      // 数组的length为 1001
a[0.00];            // =>1,相当于a[0]

由于数组也是对象,所以JavaScript数组没有"越界"的情况。当试图查询不存在的属性时,不会报错,只会得到undefined值。

稀疏数组

稀疏数组就是包含不连续索引的数组,此时数组的length属性值大于元素的个数(undefined不计入元素个数)。

var a = [1,,3];         // 第2个元素为undefined
0 in a;                 // true
1 in a;                 // false,元素不存在
2 in a;                 // true
a.length;               // 3,实际元素为2个

var b = new Array(3);
0 in b;                 // false
1 in b;                 // false
2 in b;                 // false
b.length;               // 3,实际元素为0个

var c = [undefined];    // 显式定义,元素存在
0 in c;                 // true
c.length;               // 1,实际元素为1个

数组长度

JavaScript的数组长度length是可写的。

  • 当length被设置为小于当前值时,则多余的元素将被删除。
  • 当length被设置为大于当前值时,则会在数组后创建空的区域,但并不会添加元素值。
var a = [1,2,3,4,5];
a.length = 3;           // 现在a为[1,2,3]
a.length = 0;           // 现在a为[]
a.length = 5;           // 长度为5,但是没有元素,相当于new Array(5)

var b = [1,2,3,4,5];
b.length = 7;           // 增大数组长度
alert(6 in b);          // false, 并没有添加元素
alert(b.length);        // 7,但是只有5个元素

数组元素的添加和删除

元素的添加有3种方法:
直接量、push()、unshift(),这3个方法添加元素后都会影响数组的length属性值。

元素的删除有3种方法:
delete、pop()、shift(),这3个方法中只有delete不会影响数组的length属性值,pop()、shift()都会影响length属性值。

var a = [1, 2];
a[2] = 3;       // a.length = 3
a.push(4);      // a.length = 4
a.pop();        // a.length = 3
delete a[1];    // a.length = 3,注意:delete不改变length属性,但是a[1]元素变成了undefined

数组遍历

for(var i=0; i < a.length; i++) {
    // 跳过元素值为undefined的元素和不存在的元素
    if (a[i] == undefined) continue;
}

for(var i=0; i < a.length; i++) {
    // 跳过不存在的元素
    if (!(i in a)) continue;    
}

由于数组也是对象,所以可以使用for/in对数组进行遍历,但是遍历出的属性会包含继承的属性,所以一般不使用for/in对数组进行遍历。如果使用,则需要添加过滤条件:

for(var i in a) {
    // 跳过继承属性
    if (!a.hasOwnProperty(i)) continue;
}

或者

for(var i in a) {
    // 跳过不是非负整数的i
    if (String(Math.floor(Math.abs(Number(i)))) !== i) continue;
}

多维数组

JavaScript不支持真正的多维数组,但可以用数组的数组来近似。访问数组的数组中的元素,只要简单地使用两次[]操作符即可。

var table = new Array(10);
for(var i=0; i < table.length; i++)
    table[i] = new Array(10);

table[5][7] = 35;

数组方法

join()

  • join()方法将数组中的所有元素转化为字符串并连接在一起,返回最后生成的字符串。
  • 不修改原始数组
var a = [1, 2, 3];
a.join();               // => "1,2,3",默认使用","隔开元素
a.join("&");            // => "1&2&3",指定分隔符

var b = new Array(10);  // 空数组
b.join("-");            // => "---------",9个分隔符

reverse()

  • 将数组中的元素颠倒顺序,返回逆序的原始数组引用
  • 修改原始数组
var a = [1, 2, 3];
a.reverse().join();     // => "3,2,1",此时的a是[3, 2, 1]

sort()

  • 将数组元素进行排序,返回排序后的原始数组引用。默认以字母表顺序进行排序。
  • 修改原始数组
var a = new Array("banana", "cherrry", "apple");
a.sort();
var s = a.join(", ");   // => "apple, banana, cherrry",数组a现在是["apple", "banana", "cherrry"]

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]]
a.join();               // "1,2,3",原始值不发生改变

slice()

  • 对数组进行切割,并返回一个子数组。第1个参数表示切割的起始索引,第2个参数表示切割结束的索引(返回数组不包含结束索引对应的元素值)。如果没有第2个参数,则表示取值到最后一个索引。正值表示正向索引,负值表示反向索引
  • 不修改原始数组
var a = [1,2,3,4,5];
a.slice(0, 3);          // 返回[1,2,3]
a.slice(3);             // 返回[4,5]

a.slice(-3, -1);        // 返回[3,4],反向索引,从倒数第3个到倒数第1个元素

splice()

  • 从数组中删除和插入一些元素。splice()的前2个参数指定了要删除的数组元素,后面的参数指定需要插入的元素。第1个参数表示需要删除元素的起始索引值,第2个参数表示需要删除的个数,如果省略则表示删除起始索引后的所有元素。
  • 修改原始数组
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]

var b = [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]

push()和pop()

  • push()在数组的尾部添加一个或多个元素,并返回数组新的长度。pop()则在数组的尾部删除一个元素,并返回弹出的元素值。
  • 修改原始数组
var stack = [];
stack.push(1,2);    // stack:[1,2],返回2
stack.push([1,2]);  // stack:[1,2, [1,2]],返回3

stack.pop();        // stack:[1,2],返回[1,2]

unshift()和shift()

  • 与push(),pop()不同,unshift()、shift()在数组的头部进行添加与删除操作。unshift()返回新的数组长度,shift()返回弹出的数组元素。
  • 修改原始数组
var a = [1];
a.unshift(2);           // a:[2, 1],返回2
a.unshift(3, [4,5]);    // a:[3, [4,5], 2,1],返回4。 注意:"3,[4,5]"是一次性插入的,顺序不变

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

toString()和toLocaleString()

  • toString()与不使用任何参数调用join()方法返回的字符串是一样的。
  • toLocaleString()是toString()的本地化版本,它调用元素的toLocaleString()方法将每个元素转化为字符串,并使用本地化分隔符将元素连接起来。
[1,2,3].toString();         // "1,2,3"
[1, [2, "c"]].toString();   // "1,2,c"

ECMAScript5中的数组方法

ECMAScript5中大部分数组方法的第1个参数接收一个函数,并且对数组的每个元素调用一次该函数。如果是稀疏数组,对不存在的元素不调用传递的函数。

在大多数情况下,传递的函数使用3个参数:数组元素、元素的索引和数组本身。通常,只需要第1个参数值,可以忽略后2个参数。

forEach()

  • forEach()方法从头至尾遍历数组,为每个元素调用指定的函数。
var data = [1,2,3,4,5];
var sum  = 0;
data.forEach(function(value) { sum += value;} );
sum;         // => 15
  • forEach()没有像for循环中使用的相应的break语句。如果要提前终止,可以做放到try/catch中处理:
function foreach(a, f, t) {
    try {
        a.forEach(f, t);
    }
    catch(e) {
        if(e === foreach.break) return; // 提前终止
        else throw e;
    }
}
foreach.break = new Error("StopIteration");

map()

  • map()方法为数组的每个元素调用指定的函数,并返回一个数组
a = [1,2,3];
b = a.map(function(x) {return x*x; });  // b是[1,4,9]

注意:map()返回的是新数组,它不修改原始数组,如果是稀疏数组,返回的也是相同方式的稀疏数月:它具有相同的长度,相同的缺失元素。

filter()

  • filter()方法为数组的每个元素调用指定的函数,并返回数组的一个子集
  • filter()会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的
a = [5,4,3,2,1];
smallvalues = a.filter(function(x) { return x < 3; });  // smallvalues是[2, 1]

every()和some()

  • every()方法为数组的每个元素调用指定的函数,并返回true或false。当有一个调用返回false时,every()就会返回false;只有当所有调用都是true时,every()才返回true。
  • some()方法为数组的每个元素调用指定的函数,并返回true或false。当有一个调用返回true时,some()就会返回true;只有当所有调用都是false时,some()才返回false。
var a = [1,2,3,4,5];
a.every(function(x) { return x < 10; });    // => true,所有的值<10
a.every(function(x) { return x % 2 === 0}); // => false,不是所有的值都是偶数

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

reduce()和reduceRight()

  • reduce()、reduceRight()对数组的元素对调用指定的函数,并返回最终生成的单个值
  • reduce()第1个参数指定调用的函数,第2个参数为可选参数,指定传递给函数的初始值,当不指定值时,使用数组的第1个元素作为初始值。
  • reduceRight()的工作原理和reduce()一样,只是它按照数组索引从高到低处理数组。
var a   = [1,2,3,4,5];
var sum = a.reduce(function(x,y) { return x+y; }, 0);       // 数组求和
var max = a.reduce(function(x,y) { return (x>y)?x:y; });    // 求最大值

在求和的调用中,第1次传递的参数为0(初始值)和1(数组的第1个元素),返回1;第2次调用使用参数1(返回值)和2(数组的第2个元素),返回3;然后计算3+3=6、6+4=10、10+5=15。

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

indexOf()和lastIndexOf()

  • indexOf()和lastIndexOf()搜索整个数组中给定值的元素,返回找到的第1个元素的索引,如果找不到,则返回-1。
  • 第1个参数为需要搜索的值,第2个参数是可选的,指定开始搜索的索引位置。
var a = [0,1,2,1,0];
a.indexOf(1);           // =>1: a[1] = 1
a.lastIndexOf(1);       // =>3: a[3] = 1
a.indexOf(3);           // =>-1: a中没有值为3的元素

数组类型判断

在ECMAScript5中,可以使用Array.isArray()来判断一个对象是否是数组。

Array.isArray([]);      // true
Array.isArray({});      // false

在ECMAScript3中isArray()函数的代码可以这样写:

var isArray = Function.isArray || function(o) {
    return typeof o === "object" &&
            Object.prototype.toString.call(o) === "[objet Array]";
};

类数组对象

数组有一些特性是其他对象所有没有的:

  • 当添加元素时,自动更新length属性
  • 设置length为较小值时,将截断数组。
  • 从Array.prototype中继承了一些有用的方法。
  • 类属性为"Array"

这些特性让数组与常规的对象有明显的区别。但是它们不是定义数组的本质特性。

可以把拥有数值length属性和对应非负整数属性的对象看作是一种类型的数组
很多数组算法针对类数组对象工作得很好,就像针对真正的数组一样。
定义一个类数组对象,如下所示:

var a = {};
var i = 0;
while(i < 10) {
    a[i] = i*i;
    i++;
}
a.length = i;

// 现在,可以当真正的数组进行遍历操作
var total = 0;
for(var j=0; j < a.length; j++)
    total += a[j];

Arguments对象就是一个类数组对象;在客户端JavaScript中,一些DOM方法(如document.getElementsByTagName())也返回类数组对象。

作为数组的字符串

  • 在ECMAScript5中,字符串的行为类似于只读的数组。除了使用charAt()方法访问单个字符外,还可以使用方括号访问。
  • 字符串的类似数组的特性使得通用的数组方法也可以应用到字符串上。
s = "JavaScript";
Array.prototype.join.call(s, " ");  // => "J a v a S c r i p t"

注意:不能使用会修改数组原始值的算法(如,reverse(), sort())操作字符串,否则会抛出类型错误异常。

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

推荐阅读更多精彩内容

  • 数组的基本特点 数组元素是任意类型的 可能的最大索引是2^32-2,默认起始索引是 0 Javascript的数组...
    dooze阅读 461评论 0 1
  • 什么使你意识到 这句话我没有讲完,因为我不知道是不是能在几个字中表达清楚。 大概我想说的是什么事情让你意识到你应该...
    小骄傲hhl阅读 336评论 0 1
  • 《第一行代码》作为Android开发入门的经典书籍,是我们非常好的阅读选择,初学者可以迅速了解相关知识,老司机也可...
    骑小猪看流星阅读 1,274评论 5 42
  • 假如大脑很单一,不会制造情愫,不会留住悲欢,不会记下你 ...也就不会有此刻矫情的瞬间。 清静经说内观其心,心无其...
    Dashabiii阅读 102评论 0 1