JS淬炼: Array进阶

在Javascript中,array是一个类数组的object。顾名思义,它能够在一个变量上存储多个值。

数组是值的有序集合。每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。JavaScript数组是无类型:数组元素可以是任意类型,并且同一个数组中的不同元素也可能有不同的类型。 --《JavaScript权威指南(第六版)》

array在一般Javascript object基础上,有自己额外的属性。它采用numbered index作为的key,有一个length property跟踪数组长度,还有如push/popshift/unshift等数组特有操作。

本质是object

作为一个object,我们可以在array上面进行所有object的合法操作,比如设置一个named key。

var test = [];
test.fruit = "APPLE";
console.log(test.fruit); // APPLE

上面仅仅把array作为一个普通的Javascript object使用,等价于var test = {}。在这种场合,我们应该选择普通的Javascirpt object,而非array。

Numbered index

Javascript中的 array object 采用了一个很朴素的思想来实现数组 —— 用数字来充当object的keys。这样在表面上延续了我们在C++/Java上使用数组的编程体验。

{ 
    0 : item0, 
    1 : item1, 
    2 : item2
}

array的这种实现方式导致了一些尴尬问题,比如删除元素、元素遍历。我们会在后面谈到这些问题。

删除元素

delete operator(不推荐)

和一般Javascript object一样,我们可以使用delete来删除object中的property。

var arr = ['a', 'b', 'c', 'd'];
delete arr[1];
console.log(arr);
console.log(arr[1]);
// [ 'a', , 'c', 'd' ]
// undefined

当我们使用delete来删除某一个元素arr[1]时,arr中key 1 和value b之间的连接被切断,key 1对应的值被重置为undefined。注意,这时候,array中其他元素并没有改变自身的index来填补这key 1这个空洞,而是保持原值。这样,key 1就变成了arr中的一个洞了!

除非有特殊需求,比如需要用到sparse array,一般情况下并不推荐使用delete operator来删除array中的元素。

splice() (推荐)

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(0, 1); // Removes the first element of fruits

第一个参数 (0) 定义新元素要从哪个位置插入。第二个参数 (1) 定义新元素插入位置开始有多少元素要被删除。后面的参数被忽略掉了,表示并没有要插入的元素。

filter()(推荐)

var fruits = ["Banana", "Orange", "Apple", "Mango"];
var toDelete = "Apple";
fruits = fruits.filter(function(value) { 
    return value != toDelete;
});
// [ 'Banana', 'Orange', 'Mango' ]

理解length

length是array object的一个property, 根据名字来看,似乎是记录array的长度。其实,length的本质是跟踪array中的max_index,并始终保持值是max_index + 1

这和记录长度有什么区别呢?

还记得使用delete来删除元素的情形吧?中间元素被删了,但是其他元素没有改变自身的index来填补空间。这时候即使delete了多个元素,数组的length可能并没有发生变化。

var array_object = [1, 2, 3];
console.log(array_object.length); // 3
delete array_object[1];
console.log(array_object.length); // 3!

Array 遍历

如果array中没有“洞”, for loop和forEach两种遍历方式区别不大。在array有洞的情况下,两者略有不同,其中forEach会跳过这些洞,而传统的for loop并不会。

var array_object = [1, 2, 3];
delete array_object[1];

// 不跳过洞
for(var i=0; i<array_object.length; i++) {     
    console.log(array_object[i]);
};
//1 undefined 3

//跳过洞
array_object.forEach(function(s) { 
    console.log(s);
});
// 1 3

遍历array的方法还有其他,更多方法可以参考这篇StackOverflow问答

[] vs. new Array( )

当新建一个array时候,我们有下面两种方式: array literal [] 或 array constructor new Array(arg)

var arrayA = [];
var arrayB = new Array();

当使用[]时候, JS engine会直接调用global Array的constructor新建一个array变量并返回。当使用new Array()时候, JS engine会沿着Execution Context往上追溯到名为Array的constructor function,并据此生成一个object。这时候,虽然不大可能,Array可能会在中间某个Execution Context中被用户重新定义。下面是coderjoe在StackOverflow中提出的例子。在例子中,我们最后得到的并不是我们期待的原生Array。

function Array() { 
    this.is = 'SPARTA';
}
var a = new Array();
var b = [];
alert(a.is); 
// => 'SPARTA'
alert(b.is); 
// => undefineda.push('Woa'); 
// => TypeError: a.push is not a functionb.push('Woa'); 
// => 1 (OK)

大部分Javascirpt社区推荐使用[]来新建array。

You never need to use new Object() in JavaScript. Use the object literal {} instead. Similarly, don’t use new Array(), use the array literal [] instead. Arrays in JavaScript work nothing like the arrays in Java, and use of the Java-like syntax will confuse you. LINK

Array 复制

浅度复制

var arr1 = ['a', 'b', 'c'];
var arr2 = arr1; 
arr2[0] = 1; // 对数组arr2的元素进行修改
console.log(arr1); // [1, 'b', 'c']

深度复制

var arr1 = ['a', 'b', 'c', 'd', 'e'];
var arr2 = arr1.concat(); // 使用concat()方法,返回新的数组
arr2[0] = 1; 
console.log(arr1); // => ['a', 'b', 'c', 'd', 'e']:数组arr1的元素没变更
console.log(arr2); // => [ 1, 'b', 'c', 'd', 'e']:数组arr2的元素发生了变更

Associate Array

在计算机科学中,采用named index而非numbered index的数组被称为Associative Array

var associative_array = new Array();
associative_array["one"] = "Lorem";
associative_array["two"] = "Ipsum";
associative_array["three"] = "dolor";

for (i in associative_array) { 
    console.log(i);
};
// one two three 

上面操作可以应用在任何Javascript object上,array object也不例外。但是在Javascript array上进行这种操作是很糟糕的。当需要使用named string作为key时候,我们应该使用一般的object,而非array。

Javascript里面并不支持named index的array。

If you use a named index, JavaScript will redefine the array to a standard object. After that, all array methods and properties will produce incorrect results... In JavaScript, arrays always use numbered indexes. LINK

Reference

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

推荐阅读更多精彩内容