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将导致类型错误异常。
  2. 如果数组只有一个元素并且没有指定初始值,或者有一个空数组并且指定一个初始值,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())操作字符串,否则会抛出类型错误异常。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值