JavaScript数组是原始类型吗?字符串?对象?

38

数组只是伪装成对象的吗?为什么?它们在哪些方面是(相同/不同)的?

我一直认为JS中的数组和对象本质上是相同的,主要原因是访问它们的方式是相同的。

var obj = {'I': 'me'};
var arr = new Array();
arr['you'] = 'them';

console.log(obj.I);
console.log(arr.you);
console.log(obj['I']);
console.log(arr['you']);

我是否被误导/错误/错了?关于JS的字面量、原始值、字符串/对象/数组等,我需要知道什么?

数组/对象是否只是伪装成字符串?为什么/为什么不是?它们在哪些方面是这样的/不是这样的?


4
var arr = ['you':'them']; 这不是有效的语法。 翻译: 这行代码不符合有效的语法。 - Matti Virkkunen
2
@Robert Harvey - 我正在努力澄清我的理解。符号表示重要性吗?在Javascript中,“数组”与对象、字符串等是否根本不同?我认为这是一个公平的询问。 - Jared Farrish
@Robert Harvey - 我很感激。我正在寻求指导,希望得到一个“这是最好的理解方式”的答案。虽然标准有用于理解,但我不确定我能在标准中找到这个答案。我还想指出,我认为这个问题在SO上没有得到充分的回答。我希望有人受到启发,并真正努力回答它。 - Jared Farrish
@Robert - 不是的,这非常特定于Javascript。我正在寻找一个规范的响应来澄清这里的含义。 "事实"是标准;帮助我理解真相 - Jared Farrish
该语言还有一个奇妙的怪异之处,即 typeof [] == 'object'。这是一个糟糕的设计决策,会导致更多的混淆。 - leebriggs
显示剩余3条评论
7个回答

52

数组是对象。

然而,与普通对象不同,数组具有某些特殊功能。

  1. 数组在其原型链中有一个额外的对象,即Array.prototype。该对象包含所谓的数组方法,可以在数组实例上调用。(方法列表在此处:http://es5.github.io/#x15.4.4

  2. 数组具有length属性(它是动态的,因此会自动更新)(阅读此处:http://es5.github.io/#x15.4.5.2

  3. 数组有一个特殊的算法来定义新属性(阅读此处:http://es5.github.io/#x15.4.5.1)。如果您将一个新属性设置为数组,并且该属性的名称是可以强制转换为整数数字的字符串(例如'1''2''3'等),则应用特殊算法(在规范的第123页中定义)

除了这三个东西,数组就像普通对象一样。

在规范中了解数组:http://es5.github.io/#x15.4


“数组就像普通对象一样” - 这很有趣。除了一些装饰它们的方法之外,数组就是对象,而对象是什么呢?特别是如果关联数组真的只是通过神奇地“转换”成对象。在这个意义上,关联数组还是数组吗?(如果我使用的“转换”不正确,我提前道歉。) - Jared Farrish
@Jared 在 JavaScript 中,所有的对象都是关联数组。 - Šime Vidas
@Šime Vidas - 那么为什么要有一个数组对象呢?我只是想知道,是为了暴露.length属性吗?它们看起来非常相似。 - Jared Farrish
1
@Jared:当成员名称为连续整数时,请使用数组。当成员名称为任意字符串或名称时,请使用对象。 - Šime Vidas
1
@Jared 1. JavaScript不会将数组转换为对象。数组可以包含非索引属性,就像对象一样。实际上,数组可以做到常规对象所能做的一切(以及更多)。2. 在JavaScript领域中,只存在对象和原始值。只有这两个东西。而对象具有关联数组的特性。3. 顺便说一句,我找到了一个关于JavaScript对象的好定义:对象是属性的集合。 - Šime Vidas
显示剩余15条评论

13

对象是一个从字符串键到值的无序映射,数组是一个值的有序列表(带整数键)。这是它们之间的主要区别。它们都是非原始类型,因为它们由多个值组成,这也意味着在JavaScript中传递引用。

不过,数组也是一种对象,因此您可以附加额外的属性,访问它们的原型等。

在您修改后的示例中,您只利用了数组实际上是一个对象的事实,即您可以在它们上设置任何属性。您不应该那样做。如果您不需要一个值的有序列表,请使用普通对象。


@Matti - 是的,但我正在寻找上下文。帮助我理解为什么。这是开销吗?这是惯例吗?这是好的常识吗?放弃数组并使用对象有什么价值?这有关系吗?假设我不了解区别。我相信你有知识,但请帮助我理解为什么它很重要 - Jared Farrish
1
@Jared:当你说“数组”时,任何程序员都会认为你需要一个有序列表。如果你不需要一个有序列表,那么说“数组”只会让阅读你的代码的人感到困惑。从技术角度来看,这并不会产生太大的影响,如果有的话,开销应该是微不足道的。另外,还有一件事:你经常使用PHP吗? - Matti Virkkunen
1
@Jared:很抱歉,我不明白你想让我帮你理解什么。 - Matti Virkkunen
1
@Jared:不了,我宁愿去睡觉。不过有一件事;你应该清楚地区分变量和值。它们是两个不同的东西。如果你想了解关于对象如何工作的所有细节,请阅读ECMAScript标准(我认为我在上面的评论中看到了一个链接)。 - Matti Virkkunen
1
@Matti Stack Overflow有时候很可怕。 - ClosureCowboy
显示剩余8条评论

7

字符串可以是基本类型或对象类型,这取决于它们的声明方式。

var str = 'yes';

给你一个原始的,然而,
var str = new String('yes');

将会给你一个字符串对象。


所有的数组都是相同的(无论它们是否使用 [] 或 new Array() 定义),都属于类型为 object 并继承自“Array”对象原型的对象。在 Javascript 中没有真正的类,一切都是对象,并且有一个系统定义的名为 Array 的对象。它有一个名为“prototype”的属性(类型为 object),当你在具有 prototype 属性的对象上使用 new 关键字时,它会创建一个实例并引用原型内容并将其存储在变量中。因此,在 Javascript 中使用过的所有数组都是对象,都是 Array 原型属性的实例。

无论如何,虽然数组确实是对象,但由于它们有用的属性和函数(例如 length、slice、push 等),它们表现得像数组。

另一个注意点,虽然我说没有类,但当你这样做时:

console.log(Object.prototype.toString.call(your_object));

它会给你一个形式为 [object Object] 的字符串。但有用的是,当你使用数组调用它时,你会得到 [object Array],同样适用于函数,它们会给出 [object Function] 和其他一些系统定义的类型,这有助于区分普通对象和数组(因为 typeof 运算符总是返回字符串 'object')。
试试这个。
var a = Array;

并进入firebug检查a的内容,特别是它的 'prototype' 属性。
编辑:稍微修改了措辞,更加准确。实际上,当使用new关键字时,它会创建一个实例,该实例引用原型对象。因此,在实例声明之后对原型所做的任何更改仍将影响该实例。
编辑:针对您最新修改的问题(数组/对象是否实际上是伪装成字符串的)的回答:不是。它们是对象,正如我所解释的那样。字符串可以是原始类型,也可以是对象类型(String对象的实例),其中包含其属性之一作为原始等价物。

2
@aiham ...当您在具有原型属性的对象上使用new关键字时,它会创建原型内容的克隆并将其存储在您的变量中。 这是不正确的。原型对象的内容并没有被克隆,而只是创建的实例对象具有对该原型(及其方法)的引用。 - Šime Vidas
我为我的措辞不当道歉。你是对的,它引用了原型的内容。因此,如果在创建实例对象之后修改原型,则实例对象也会受到影响。我会修改我的答案。 - aiham
确实是这样。最近我才对Javascript原型感兴趣,玩弄它真的很有趣。通过利用对象和原型,您可以创建一些非常强大的应用程序。您应该阅读一些Douglas Crockford关于Javascript的观点(http://javascript.crockford.com/),其中很多都非常有用(有些有点过头了)。 - aiham
我不确定对象在内存中是如何存储的,但我猜这取决于浏览器。Javascript开发人员不需要了解内存就可以开发Javascript。这是一个比Javascript处理的更低级别的问题。您无法像其他低级语言那样访问内存或指针。(您可以使用“delete”设置变量以进行垃圾回收)。这一切都为您完成。如果您对浏览器如何在内存中存储对象感兴趣,那么最好向开源浏览器开发社区(例如Mozilla)提问。 - aiham
首先,让我重新表述一下,“不需要了解指针和内存存储方式”。当然,你必须关注内存问题,避免占用过多的内存。 - aiham
显示剩余16条评论

6

在Javascript中,数组不是原始类型,而是对象。关键区别在于,当你将数组传递给函数时,它是按引用传递的,而不是按值传递。

所以没错!在javascript中,数组是对象,拥有完整的Array.prototype和所有内容(但最好不要去改变它...)

混淆的原因在于javascript允许你以两种方式访问对象属性:

myObj.attribute 或 myObj["attribute"]

实际上,将数据存储到数组中的方式并不是使其成为数组的原因——任何对象都可以使用存储数组的语法来存储值——将一个数组定义为数组的原因是Array.prototype定义了数组方法(例如shift()和sort())。


这与JavaScript中的对象有什么不同吗? - Jared Farrish
更正:数组的引用是按值传递的。 - The Scrum Meister
1
@Scrum Master 当人们说按引用传递时,他们肯定是指这个吧...对吧? - slifty
1
真正使数组成为数组的原因与您存储数据的方式无关。我喜欢这个答案,因为它回答了我对JS中对象/数组为什么令人困惑的疑问。 - Jared Farrish

1

尝试简要说明我认为最重要的内容:数组有许多对象没有的方法,包括:

  • length
  • push
  • pop

一个声明为var x = {foo:bar}的对象无法访问.length()方法。它们都是对象,但数组作为一种超集具有上述方法。

我觉得这个解释离Crockford标准还差得远,但我正在努力简洁明了。

如果你想快速获得一些结果,请打开Firebug或你的JavaScript控制台并尝试Array.prototype和Object.prototype以查看一些细节。

更新:在你的示例中,你声明了一个数组,然后执行:

foo['bar'] = 'unexpectedbehaviour';

这将产生意想不到的结果,并且在简单循环中将无法使用,例如:

var foo=[0,1];
foo['bar'] = 2;

for(var i=0;i<foo.length;i++){
    console.log(foo[i]);
}

//outputs: 
//0
//1

一个数组可以像对象一样接受foo['bar']=x或者foo.bar=y, 但是并不一定能够像上面所强调的那样被循环遍历。

并不是我说你不能迭代对象的属性,只是当你在使用数组时,你正在利用它独特的功能,应该记住不要混淆。


@connrs 不是真的。数组可以像任何其他对象一样具有属性:var arr = []; arr.foo = '完全没问题!'; - Šime Vidas
小心啊,Connrs。数组拥有这些方法是因为它们本身就是对象,其原型已经被赋予了这些函数。 - slifty
@Šime Vidas:确实如此,但是对于初学者的JavaScript开发人员来说,var foo=[0,1]; foo.bar=2; for(var i=0;i<foo.length;i++){console.log(foo[i]);}将无法按预期工作。 - connrs
啊,我想指出它会产生意外的结果可能是更好的角度。现在进行编辑。 - connrs
@connrs - 我发誓,在没有先声明foo ['var']的情况下使用foo.var时,我曾经会出现错误。我发誓。 - Jared Farrish
显示剩余2条评论

1
在JavaScript中,你有几种类型,其他所有都是对象。JavaScript中的类型包括:布尔值、数字和字符串。还有两个特殊值,"null"和"undefined"。
因此,“JavaScript数组是一个对象吗?”这个问题有点模糊。是的,JavaScript数组是一个“对象”,但它不是“Object”的实例。JavaScript数组是“Array”的实例。尽管如此,所有对象都继承自Object;你可以在MDC上查看继承链。此外,数组具有与对象略有不同的属性。数组具有.length属性。它们还有.slice().join()等方法。
Douglas Crockford提供了一份关于该语言特性的很好的调查报告。他的调查报告讨论了你所问的差异。此外,你可以在问题#4559207中阅读更多关于字面量和构造函数之间的区别

现在看,这与我的理解相符。三种类型。但是如何解释“Object”,其中除了数组之外的所有内容都是“constructed”?我们只谈论默认方法原型吗?“是的,JavaScript数组是一个“对象”,但它不是“Object”的实例。这触及了我所询问的核心问题。 - Jared Farrish
是的,我认为Crockford是JavaScript受到重视的原因。他在JavaScript领域已经长期组织理智(至少自2004年以来,更早?)。 - Jared Farrish
@Jared 你知道Crockford,但还没有看过他最基础的JavaScript视频系列吗?现在就去看吧! :) - Šime Vidas
@jsummers - 所以数组是带有自定义特殊方法的花哨字符数组? - Jared Farrish
@Šime Vidas - 是的,我知道。Crockford是神。这里的重点是尽可能地提取意义,而不是我的随机学习过程。我希望有一种意义对我来说是有意义的,那时候(感谢Thau!)。 - Jared Farrish
ES6 包含了一种新的原始数据类型:Symbol。 - Hart Simha

0

数组是对象,但具有特殊的性质。对象是由键索引的值集合(在Javascript符号中,{'key': 'value'}),而数组是对象,其键是数字(带有一些函数和属性)。它们之间的关键区别在于当您使用for each循环时显而易见--对象将遍历其属性中的所有值,而数组将返回键。这里是一个JSFiddle演示差异的链接--请注意,使用数组的第一个for each返回索引而不是值;相反,第二个for each返回这些键上的实际值。


我相信 for each 语句只在 Firefox 中实现了。 - Šime Vidas
它的演示目的仍然非常重要。我曾经发誓除了IE之外的所有浏览器都支持它,但我可能记错了。:\ - Jeff Hubbard
是的,我也不确定,但我知道for(var i in obj)语法被普遍支持。 - Jared Farrish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接