JS进阶:数组

学习自:JS权威指南

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

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

JavaScript数组是JavaScript对象的特殊形式,使用方括号访问数组元素就像用方括号访问对象的属性一样。JavaScript将指定的数字索引值转换成字符串——索引值1变成“1”——然后将其作为属性名来使用。

JavaScript数组的索引是基于零的32位数值:第一个元素的索引为0,最大可能的索引为4294967294(2^32-2),数组最大能容纳4294967295个元素。所有的索引都是属性名,但只有在0~2^32-2之间的整数属性名才是索引。

注意,可以使用负数或非整数来索引数组。这种情况下,数值转换为字符串,字符串作为属性名来用。这意味着JavaScript数组没有“越界”错误的概念。当试图查询任何对象中不存在的属性时,不会报错,只会得到undefined值。

因为对象是属性集合,使用hashmap结构来存储,不像静态语言中数组是一块连续的区域,因此不存在数组越界的情况。查找属性名时,遍历hashmap,如果找不到匹配的属性名,返回undefined。

JavaScript数组是动态的:根据需要它们会增长或缩减,并且在创建数组时无须声明一个固定的大小或者在数组大小变化时无须重新分配空间。

  • 如果为一个数组元素赋值,它的索引i大于或等于现有数组的长度时,length属性的值将设置为i+1。这时数组可能是稀疏的。

  • 设置length属性为一个小于当前长度的非负整数n时,当前数组中那些索引值大于或等于n的元素将从中删除

  • 在ECMAScript 5中,可以用Object.defineProperty()让数组的length属性变成只读的

    Object.defineProperty(a, ”length“, {writable: false});

数组继承自Array.prototype中的属性,它定义了一套丰富的数组操作方法

基本方法

简单方法

数组直接量中的值不一定要是常量;它们可以是任意的表达式:

var table = [base, base+1, base+2, base+3];

1
2
var count = [1,,3]; // 数组有3个元素,中间的那个元素值为undefined
var undefs = [,,]; // 数组有2个元素,都是undefined

数组直接量的语法允许有可选的结尾的逗号,故[,,]只有两个元素而非三个。

delete a[1]; 对一个数组元素使用delete不会修改数组的length属性,也不会将元素从高索引处移下来填充已删除属性留下的空白,会变成稀疏数组。

Array.join()方法是String.split()方法的逆向操作

当不带参数调用sort()时,数组元素以字母表顺序排序(如有必要将临时转化为字符串进行比较)

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

1
2
3
4
5
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]]

Array.slice()方法,如果只指定一个参数,返回的数组将包含从开始位置到数组结尾的所有元素。如参数中出现负数,它表示相对于数组中最后一个元素的位置。例如,参数-1指定了最后一个元素,而-3指定了倒数第三个元素。注意,slice()不会修改调用的数组。

1
2
3
4
5
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]

Array.splice()方法是在数组中插入或删除元素的通用方法。不同于slice()和concat(),splice()会修改调用的数组

splice()的前两个参数指定了需要删除的数组元素。第一个参数指定了插入和(或)删除的起始位置。第二个参数指定了应该从数组中删除的元素的个数。如果省略第二个参数,从起始点开始到数组结尾的所有元素都将被删除。紧随其后的任意个数的参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。

1
2
3
4
5
6
7
8
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 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]

toString()方法输出用逗号分隔的字符串列表。注意,输出不包括方括号或其他任何形式的包裹数组值的分隔符。与不使用任何参数调用join()方法返回的字符串是一样的。

[1, [2,'c']].toString() // 生成'1,2,c'

ES6新方法

大多数方法的第一个参数接收一个函数,并且对数组的每个元素(或一些元素)调用一次该函数。如果是稀疏数组,对不存在的元素不调用传递的函数。

调用提供的函数使用三个参数:数组元素、元素的索引和数组本身。

遍历常用forEach()方法:

1
2
var data = [1,2,3,4,5];
data.forEach(functionv, i, a{ a[i] = v + 1; });

要提前终止遍历,必须把forEach()方法放在一个try块中,并能抛出一个异常。

map()方法将调用的数组的每个元素传递给指定的函数,并返回一个数组,它包含该函数的返回值。map()返回的是新数组:它不修改调用的数组。如果是稀疏数组,返回的也是相同方式的稀疏数组:它具有相同的长度,相同的缺失元素。

b = a.map(function(x){ return x*x; });

fliter()方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的:该函数返回true或false。返回值为true或能转化为true的值,那么传递给判定函数的元素就是这个子集的成员,它将被添加到一个作为返回值的数组中。filter()会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的。为了压缩稀疏数组的空缺,可使用:

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

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

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

注意,一旦every()和some()确认该返回什么值它们就会停止遍历数组元素。——短路

reduce()多一个参数用于存储上一次化简函数的返回值,当不指定初始值调用reduce()时,它将使用数组的第一个元素作为其初始值。

var max = a.reduce(function(x,y){ return(x>y)?x:y; });

reduceRight()优先顺序是从右到左

indexOf()和lastIndexOf()搜索整个数组中具有给定值的元素,第一个参数是需要搜索的值,第二个参数是可选的:它指定数组中的一个索引,从那里开始搜索。返回找到的第一个元素的索引或者如果没有找到就返回-1

判断数组类型

typeof操作符在这里帮不上忙:对数组它返回“对象”

instanceof操作符只能用于简单的情形

ES6新增 Array.isArray()函数,实现原理:

1
2
3
4
var isArray = Function.isArray || functiono{
return typeof o === "object" &&
Object.prototype.toString.call(o)=== "[object Array]";
};

类数组对象

把拥有一个数值length属性和对应非负整数属性的对象看做一种类型的数组。

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