JavaScript 数组是如何实现的?

Posted

技术标签:

【中文标题】JavaScript 数组是如何实现的?【英文标题】:How are JavaScript arrays implemented? 【发布时间】:2010-09-26 19:54:40 【问题描述】:

即下面的代码怎么写:

var sup = new Array(5);
sup[0] = 'z3ero';
sup[1] = 'o3ne';
sup[4] = 'f3our';
document.write(sup.length + "<br />");

当你所做的只是设置各种元素时,输出'5'作为长度?

我对这段代码的“问题”是,如果不调用 getLength()setLength() 方法,我无法理解 length 的变化。当我执行以下任一操作时:

a.length
a['length']
a.length = 4
a['length'] = 5

在非数组对象上,它的行为类似于 dict / 关联数组。当我对数组对象执行此操作时,它具有特殊含义。 javascript 中的什么机制允许这种情况发生? JavaScript 是否有某种类型的属性系统可以翻译

a.length
a['length']

进入“get”方法和

a.length = 4
a['length'] = 5

进入“设置”方法?

【问题讨论】:

@some:我是 javascript 新手。我可以在 python 中实现这种行为,我知道如何重载 getitem 等,但我不知道 javascript 的内部结构是如何工作的。您似乎很熟悉,如果您认为这是微不足道的 - 请回复! 我很难理解你的问题。你到底想做什么? Javascript 中缺少什么? @Eran:在许多语言中,您可以创建可以使用类似数组的语法 foo[x] 的对象,而实际上不是数组(即在后台发生自己的逻辑) @Gareth:显然,但在 javascript 中,基本的关联字典是一个对象。除了作为哈希数组,它还可以有方法和属性 让我试着重新表述这个问题。 【参考方案1】:

JavaScript 中的一切都是对象。对于Arraylength 属性返回数组索引项的内部存储区域的大小。由于[] 运算符适用于数字和字符串参数,因此可能会产生一些混淆。对于数组,如果将其与数字索引一起使用,它将返回/设置预期的索引项。如果将它与字符串一起使用,它会返回/设置数组对象上的命名属性 - 除非字符串对应于数值,否则它会返回索引项。这是因为在 JavaScript 中,数组索引被隐式的 toString() 调用强制转换为字符串。坦率地说,这只是让你挠头说“JavaScript,这,这就是他们嘲笑你的原因。”的另一件事。

实际的底层表示可能会因浏览器而异(也可能不会)。除了使用它时提供的接口之外,我不会依赖任何其他东西。

您可以在at MDN 找到更多关于 JavaScript 数组的信息。

【讨论】:

啊这是我的问题:“在数组的情况下,长度属性返回数组索引项的内部存储区域的大小。”。所以我想我的问题是“属性如何运作?” - 我可以自己回答。我只是不知道去哪里找! (通过谷歌搜索回答我自己,我的意思是 - 但我可能仍然需要这部分的帮助) 有没有办法在我自己创建的 javascript 程序中使用这种重载,或者这只是数组的一种特殊情况? "对于一个数组,如果你将它与数字索引一起使用,它会返回/设置正确的索引项。如果你将它与字符串一起使用,它会返回/设置数组对象上的命名属性。”这是不正确的。试试 a[1] = "foo";打印(一个[“1”]);在 javascript 对象中每个键都是一个字符串。事实上,我认为数组根本不能保证被实现为数组(它们可能是早期实现中的哈希图)。 Everything in JavaScript is an object... 除了原语【参考方案2】:

JavaScript 数组的特征

    动态 - JavaScript 中的数组可以动态增长 .push 可以是稀疏的 - 例如,array[50000] = 2; 可以是密集的 - 例如,array = [1, 2, 3, 4, 5]

在 JavaScript 中,运行时很难知道数组是密集的还是稀疏的。所以它所能做的就是猜测。所有实现都使用启发式方法来确定数组是密集的还是稀疏的。

例如,上面第 2 点中的代码可以向 JavaScript 运行时表明这可能是一个稀疏数组实现。如果数组是用初始计数初始化的,这可能表明这可能是一个密集数组。

当运行时检测到数组是稀疏时,它的实现方式与对象类似。所以不是维护一个连续的数组,而是构建一个键/值映射。

如需更多参考资料,请参阅How are JavaScript arrays implemented internally?

【讨论】:

【参考方案3】:

这真的取决于你打算用它做什么。

[].length 是“神奇的”。 它实际上并不返回数组中的项目数。它返回数组中最大的 instated 索引。

var testArr = [];  testArr[5000] = "something";  testArr.length; // 5001

但是setter背后的方法隐藏在引擎本身中。 某些浏览器中的某些引擎将使您可以访问它们对这些魔术方法的实现。 其他人将完全锁定所有内容。

所以不要依赖 defineGetter 和 defineSetter 方法,甚至真的是 __proto__ 方法,除非你知道你知道你的目标是哪些浏览器,而你不是。

这将在未来发生变化,使用 ECMAScript Next/6 编写的选择加入应用程序将可以访问更多。

兼容 ECMAScript 5 的浏览器已经开始在对象中提供 getset 魔术方法,而且还会有更多……但可能还需要一段时间才能放弃对 oldIE 和吨智能手机等等......

【讨论】:

【参考方案4】:

重要的是要知道,当您执行sup['look'] = 4; 时,您不是使用关联数组,而是修改对象sup 的属性。

它等同于sup.look = 4;,因为您可以随时在 JavaScript 对象上动态添加属性。 sup['length'] 在您的第一个示例中将作为实例输出 5。

【讨论】:

属性访问/变异可以变成方法调用吗?如果没有,如果我只修改了“0”和“1”属性,“长度”属性将如何变化? 数组始终是数组,但是在 sup['look'] 中,您正在修改数组对象的属性,但是当您执行 sup[0] 时,您正在访问数组索引,不是对象属性。我认为 tvanfosson 解释得很好:)【参考方案5】:

要添加到tvanfosson's answer:在 ECMA-262(我相信是 3.0 规范)中,数组被简单地定义为具有这种设置属性的行为(参见 15.4.5.1)。它没有基本的通用机制(至少目前是这样)——这只是它的定义方式,以及 JavaScript 解释器的行为方式。

【讨论】:

【参考方案6】:

正如其他人所提到的,JavaScript 中的属性基本上可以充当数组(或字符串或其他输入)的 getter 和 setter。

事实上,你可以自己试试这个:

const test = [1, 2, 3, 4, 5]
test.length = 3
console.log(test) // [1, 2, 3]
test.length = 5
console.log(test) // Guess what happens here!

据我所知,JavaScript 中的数组与关联数组的工作方式不完全相同,并且您的元素被放入内存 as contiguously as possible(假设您可以拥有混合对象的数组),具体取决于您使用的 JavaScript 引擎考虑。

作为旁注,我有点困惑的是,投票最多的答案一直在传播“一切都是 JavaScript 中的对象”的过于简单的神话(或半真半假); that is not exactly true,否则你将永远不会学习基元,例如。

尝试这样做:

const pippi = "pippi"
pippi.cat = "cat"
console.log(pippi.cat) // Will it work? Throw an error? Guess why again

剧透:字符串被包装在第二行的特定操作的一次性对象中,然后在下一行中,您将访问不存在的原语的属性(前提是您没有玩过String.prototype 或类似的),所以你得到undefined

【讨论】:

【参考方案7】:

Array 对象从Function.prototype 继承callerconstructorlengthname 属性。

【讨论】:

【参考方案8】:

JavaScript 数组是一个对象,就像任何其他对象一样,但 JavaScript 赋予了它特殊的语法。

arr[5] = "yo"

以上是语法糖

arr.insert(5,"yo")

这就是您将东西添加到常规对象的方式。 insert 方法内部的内容改变了 arr.length 的值

在此处查看我的 customArray 类型的实现:http://jsfiddle.net/vfm3vkxy/4/

【讨论】:

以上是关于JavaScript 数组是如何实现的?的主要内容,如果未能解决你的问题,请参考以下文章

如何实现数组深拷贝和浅拷贝?

javascript如何遍历数组中的每一个元素

如何检查数组是否包含JavaScript中的对象?

如何使用 filter()、map() 处理数组的值? JavaScript

如何在javascript中从数组制作二叉树?

arcgis api for javascript如何遍历graphics