Javascript数组

来源:互联网 发布:淘宝母婴节是哪一天 编辑:程序博客网 时间:2024/05/21 13:36

Javascript数组

对象允许存储键值集合,这非常好,但通常我们需要有序集合,通过它可以获得第一、第二、第三元素等。举例,我们需要存储一些列表:用户、货物以及HTML元素等。
这里使用对象则不方便,因为没有提供方法管理元素顺序。我们不能在已存在元素之间插入新的元素。对象不适合这样使用。
Javascript提供了一个特殊的Array数据结构,可以存储顺序集合。

申明数组

有两种语法可以创建空数组:
let arr = new Array();
let arr = [];

大多数时,第二种语法更常用。我们也可以在括号中初始化一些元素:

let fruits = ["Apple", "Orange", "Plum"];

数组元素是可数的,从0开始。通过在方括号中指定数值,我们可以获取元素。

let fruits = ["Apple", "Orange", "Plum"];alert( fruits[0] ); // Applealert( fruits[1] ); // Orangealert( fruits[2] ); // Plum

也可以覆盖数组元素。

fruits[2] = 'Pear'; // now ["Apple", "Orange", "Pear"]

或给数组增加新的元素:

fruits[3] = 'Lemon'; // now ["Apple", "Orange", "Pear", "Lemon"]

数组中所有元素的个数,通过length属性获取:

let fruits = ["Apple", "Orange", "Plum"];alert( fruits.length ); // 3

我们也可以使用alert显示整个数组.

let fruits = ["Apple", "Orange", "Plum"];alert( fruits ); // Apple,Orange,Plum

相同数组可以存储任意类型元素。举例:

// mix of valueslet arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];// get the object at index 1 and then show its namealert( arr[1].name ); // John// get the function at index 3 and run itarr[3](); // hello

结尾的逗号
数组和对象一样,可以以逗号结尾:

let fruits = [  "Apple",  "Orange",  "Plum",];

结尾逗号形式使得插入、删除元素更容易,因为所有行变得相似。

数组方法 pop/push, shift/unshift

队列是最常见的使用数组方式。计算机科学中,有序集合支持两种操作:
- push 在结尾追加一个元素。
- shift 获得开头第一个元素,推进后面所有元素,所以原来第二个元素变成第一个。

数组支持两种操作。

实际中,我们经常遇到,如一个消息队列需要显示在屏幕上。

还有其他数组使用场景,数据结构中称为堆栈。

栈支持两种操作:

  • push 在结尾增加元素。
  • pop 在结尾弹出元素。

所以新元素增加和弹出总是在结尾。
堆栈通常描绘为一堆卡片,新卡片增加到顶部,或从顶部拿走。

对堆栈来说,最新入栈元素首先出栈,也被称为LIFO(后进先出)原则。队列则为FIFO(先进先出)。

Javascript中数组可以同时表示队列和数组。允许增加、删除元素至开始或结尾位置。

在数组结尾操作的方法

pop
提前数组最后一个元素,并返回:

let fruits = ["Apple", "Orange", "Pear"];alert( fruits.pop() ); // remove "Pear" and alert italert( fruits ); // Apple, Orange

push
在数组结果增加元素:

let fruits = ["Apple", "Orange"];fruits.push("Pear");alert( fruits ); // Apple, Orange, Pear

调用 fruits.push(...) 方法相当于 fruits[fruits.length] = ....

在数组开始操作的方法

shift
提起数组第一个元素,并返回:

let fruits = ["Apple", "Orange", "Pear"];alert( fruits.shift() ); // remove Apple and alert italert( fruits ); // Orange, Pear

unshift
在数组开始增加元素:

let fruits = ["Orange", "Pear"];fruits.unshift('Apple');alert( fruits ); // Apple, Orange, Pear

方法push和unshift一次可以增加多个元素:

let fruits = ["Apple"];fruits.push("Orange", "Peach");fruits.unshift("Pineapple", "Lemon");// ["Pineapple", "Lemon", "Apple", "Orange", "Peach"]alert( fruits );

内部构件

数组是一种特殊的对象,方括号语法可以访问属性arr[0],实际是来自对象的语法,数字作为键。

继承对象,提供特殊的方法实现有序数据集合,也有length属性,但是其核心仍是对象。

记住,Javascript有7个基本数据类型,数组是对象,表现的也象对象。

举例,通过引用拷贝:

let fruits = ["Banana"]let arr = fruits; // copy by reference (two variables reference the same array)alert( arr === fruits ); // truearr.push("Pear"); // modify the array by referencealert( fruits ); // Banana, Pear - 2 items now

但是,使数组真正特别的是其内部表示。Javascript引擎尝试存储它的元素在连续的内存区域,一个接着一个,如下面的插图所示,还有其他一些优化,使其工作非常快。

如果我们停止使用数组作为有序集合,而作为普通对象使用,则上述优势被则不复存在。

举例,技术上我们可以这样:

let fruits = []; // make an arrayfruits[99999] = 5; // assign a property with the index far greater than its lengthfruits.age = 25; // create a property with an arbitrary name

这时可能的,因为数组本质上是对象,我们能给他们增加属性。
但是Javascript引擎看到我们把数组作为普通对象使用,数组特定的优化则不适合这种场景被取消,优势消失。

这种方式是乱用数组:
- 增加非数值属性,如: arr.test = 5 .
- 留空隙,如: 增加 arr[0] ,然后增加 arr[1000] (他们之间保留为空)
- 使用相反的顺序填充: 如: arr[1000],arr[999] 等。

请考虑数组作为特殊的数据结构,作为顺序数据工作,并为之提供了特殊的方法。Javascript引擎对数组作为连续顺序数据集合有细心的优化,请用正确的方式使用。如果你需要任意键,很可能你需要普通对象 {} 。

性能

方法 push/pop 更快,而 shift/unshift 则慢。

为什么在数组结尾操作比在开头操作更快?让我们看看执行过程中发生了什么?

fruits.shift(); // take 1 element from the start

不仅只是删除或提取第一个元素,其他元素也需要重新编号。 shift 操作需要做三件事情:

  1. 删除索引为0的元素。
  2. 向左移动所有元素,重新编号,依次把1变成0,2变成1等。
  3. 更新length属性。

数组元素越多,占用更多内存移动越耗时。

类似的事情发生在unshift方法上:在数组的开始处增加元素,我们首先向右移动原有元素,增加他们的索引。

push/pop怎么运行的呢?他们不需要做任何移动操作。从结尾处提取元素,pop方法清除索引并缩短length属性。

pop方法动作如下:
fruits.pop(); // take 1 element from the end

pop方法无需任何移动操作,因为其他元素保持其索引不变。所以相对操作更快,类似的push方法也一样。

循环

最古老的方法之一是使用for 循环遍历索引项:
let arr = [“Apple”, “Orange”, “Pear”];

for (let i = 0; i < arr.length; i++) {  alert( arr[i] );}

但数组有另外的循环方法,for..of:

let fruits = ["Apple", "Orange", "Plum"];// iterates over array elementsfor(let fruit of fruits) {  alert( fruit );}

for..of方式不能访问当前元素的索引,只是值,但大多数场景下可以满足,且代码简洁。
技术上,因为数组是对象,也可以使用 for..in :

let arr = ["Apple", "Orange", "Pear"];for (let key in arr) {  alert( arr[key] ); // Apple, Orange, Pear}

但这实际上是一个坏注意,因为有潜在的问题:

  1. for..in 循环迭代所有的属性,不仅是数值索引。
    在浏览器或其他环境中,有所谓的“类数组”对象,看上去很象数组,有length和索引属性,但他们也可能有其他非数值属性和方法,通常这些不是我们所需的,但 for..in 循环将全部列出他们。所以如果我们循环类数组对象,那些额外的属性可能是问题。

  2. for..in 循环对普通对象有优化,不是数组,因为速度会慢10~100倍。当然,仍会很快。速度仅在瓶颈处会有问题,而不是无关紧要的事情上,但我们最好还是要知道两者差异。

一般情况下,我们不应该使用 for ..in 循环数组。

length

当我们修改数组是,length属性自动更新。准确的说,确实不是数组值的个数,而是数组索引最大值加一。

举例,给数组一个很大索引长度值,仅指定单个元素:

let fruits = [];fruits[123] = "Apple";alert( fruits.length ); // 124

注意,我们通常不这样使用数组。
另一个有趣的事情是length属性是可写的。

如果我们手动增加,没有什么会发生。但如果我们减少,数组元素被删除,这个过程是不可逆的,示例如下:

let arr = [1, 2, 3, 4, 5];arr.length = 2; // truncate to 2 elementsalert( arr ); // [1, 2]arr.length = 5; // return length backalert( arr[3] ); // undefined: the values do not return

所以,最简单的清除数组方式为: arr.length = 0 ;

new Array()

另一个创建数组的语句为:
let arr = new Array(“Apple”, “Pear”, “etc”);

这很少使用,因为使用[]更简洁。同时这种方式有隐蔽特性。

如果调用 new Array 带一个数值参数,那么创建数组,没有该数值元素,而是只数组的长度。

让我们看看搬石头砸自己脚的示例:

let arr = new Array(2); // will it create an array of [2] ?alert( arr[0] ); // undefined! no elements.alert( arr.length ); // length 2

上面的代码, new Array(number) 所有元素值为 undefined.
为了避免这种奇怪的事情,通常我们使用[]方式,除非你知道自己正在做什么?

多维数组

数组的元素也可以是数组。这样我们能使用多维数组,存储矩阵:

let matrix = [  [1, 2, 3],  [4, 5, 6],  [7, 8, 9]];alert( matrix[1][1] ); // the central element

toString

数组的toString有自己的实现,返回使用逗号分割所有元素的字符串。举例:

let arr = [1, 2, 3];alert( arr ); // 1,2,3alert( String(arr) === '1,2,3' ); // true

也可以这样:

alert( [] + 1 ); // "1"alert( [1] + 1 ); // "11"alert( [1,2] + 1 ); // "1,21"

数组没有 Symbol.toPrimitive,也没有 valueOf, 仅实现toString规范。所以[] 返回空字符串,[1] 为 “1” ,[1,2] 为 “1,2” 。
当二元运算符 + 操作 增加什么至字符串,则转换至字符串,所以看起来如下:

alert( "" + 1 ); // "1"alert( "1" + 1 ); // "11"alert( "1,2" + 1 ); // "1,21"

总结

数组是一种特殊对象,适合存储管理有序数据项。
申明:

// square brackets (usual)let arr = [item1, item2...];// new Array (exceptionally rare)let arr = new Array(item1, item2...);

调用 new Array(number) 创建指定长度的数组,但没有元素。

  • length 属性可以是数组的长度,更精确的说,是最大数值索引加一,其根据数据操作方法自动调整。
  • 如果手动缩短length属性,数组元素被彻底删除。

我们能使用数据作为队列,提供了下面一些方法:

  • push(…items) 在结尾增加元素.
  • pop() 从结尾删除元素,并返回该元素.
  • shift() 从开始处删除元素,并返回该元素.
  • unshift(…items) 在数据开头增加元素.

循环数组元素方式有:

  • for(let i=0; i<arr.length; i++) – 操作最快,兼容旧版本浏览器.
  • for(let item of arr) – 新的语法,仅能获得元素.
  • for(let i in arr) – 永远不要使用.

后面我们继续讲解数据的方法,关于增加、删除、抽取元素、排序方法等。

原创粉丝点击