💜《JavaScript 语言精粹》之数组篇

52 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情

你这被披着羊皮的狼,我要把你赶走。 —— 威廉 · 莎士比亚,《亨利六世上篇》(The First Part of enry the Sixth)

  • 数组是一段线性分配的内存,它通过整数计算偏移并访问其中的元素。
  • 数组是一种性能出色的数据结构。
  • 作为替代,JavaScript 提供了一种拥有一些类数组特性的对象。它把数组的下标转变成字符串,用其作为属性。

6.1 数字字面量

  • 数组字面量提供了一种非常方便地创建新数组的表示法。
  • 一个数组字面量是在一堆方括号中包围零个或多个用逗号分隔的值的表达式。
  • 数组字面量允许出现在任何表达式可以出现的地方。
  • JavaScript 允许数组包含任意混合类型的值

6.2 长度

  • 每个数组都有一个 length 属性。
  • length是没有上界的。
  • length 属性的值是这个数组的最大整数属性名加上 1。它不一定等于数组里属性的个数。
var myArray = [];
myArray.length  // 0
myArray[10000] = true;
myArray.length  // 10001
// myArray 只包含一个属性

[ ] 后置下标运算符把它所含的表达式转换成一个字符串,如果该表达式有 toString 方法,就是用该方法的值。这个字符串将被用作属性名。

你可以设置 length 的值。设置更大的 length 不会给数组分配更多的空间。而把 length 设小将导致所有下标大于等于新 length 的属性被删除:

numbers.length = 3;  // numbers 是 ['zero', 'one', 'two' ]

通过把下标指定为一个数组的当前 length,可以附加一个新元素到该数组的尾部:

numbers[numbers.length] = 'shi';  // numbers 是 ['zero', 'one', 'two', 'shi' ]

有时用 push 方法更方便地完成同样的事情:

numbers.push('go');  // numbers 是 ['zero', 'one', 'two', 'shi', 'go' ]

6.3 删除

JavaScript 的数组就是对象,所以 delete 运算符可以用来从数组中移除元素:

delete numbers[2]  // numbers 是 ['zero', 'one', undefined, 'shi', 'go' ]

数组中留下一个空洞,因为排在被删除元素之后的元素保留着它们最初的属性。

JavaScript 数组有一个 splice 方法。它删除一些元素并将他们替换为其他的元素。第 1 个参数是数组中的一个序号,第 2 个参数是要删除的元素个数。任何额外的参数会在序号那个点的位置被插入到数组中。

numbers.splice(2, 1)  // numbers 是 ['zero', 'one', 'shi', 'go' ]

6.4 枚举

  • for in 语句可以用来遍历一个数组的所有属性。但 for in 无法保证属性的顺序。
  • 常规的 for 语句可以避免这些问题。

6.5 容易混淆的地方

  • JavaScript 本身对于数组和对象的区别是混乱的。
  • typeof 运算符报告数组的类型是 'object'
  • JavaScript 没有一个好的机制来区别数组和对象。我们可以通过自己的 is_array 函数去判断一个对象是否为数组:
var is_array = function(value) {
	return Object.prototype.toString.apply(value) === '[Object Array]';
}

6.6 方法

  • 在第 3 章里,我们看到 Object.prototype 是可以被扩充的。同样,Array.prototype也可以被扩充。

举例来说,假设我们想要给 Array 增加一个方法,它允许我们对数组进行计算:

Array.method('reduce', function(f, value){
    var i;
    for(i=0; i<this.length; i+=1) {
            value = f(this[i], value)
    }
    return value;
});

通过给 Array.prototype 扩充一个函数,每个数组都继承了这个方法。如果我们传入把两个数字相加的函数,它会计算出一个新值。

var data = [4, 8, 15, 16, 23, 42];
var add = function(a, b) {
    return a + b;
}
// 调用 data 的 reduce 方法,传入 add 函数
var sum = data.reduce(add, 0)  // sum is 108
// 因为数组就是对象,我们直接给一个单独的数组添加方法
data.total = function() {
    return this.reduce(add, 0)
}
total = data.total() // total is 108

因为字符串 'total' 不是整数,所以给数组增加一个 total 属性不会改变它的 length。当属性名是整数时,数组才是最有用的,但它们依旧是对象,并且对象可以接受任何字符串作为属性名。

来自第 3 章的 Object.create 方法用在数组是没有意义的,因为它产生一个对象,而不是一个数组。产生的对象继承这个数组的值和方法,但它没有那个特殊的 length 属性。

6.7 指定初始值

  • JavaScript 的数组通常不会预置值。
  • 如果你用 [ ] 得到一个新数组,它将是空的。
  • 如果你访问一个不存在的元素,得到的值则是 undefined
  • JavaScript 没有多维数组,但就像大多数类C语言一样,它支持元素为数组但数组:
var matrix = [
	[0, 1, 2],
	[3, 4, 5],
	[6, 7, 8]
];
matrix[2][1]  // 7
  • 一个空的矩阵的每个单元会拥有一个初始值 undefined。如果你希望她们有不同的初始值,你必须明确地设置它们。