使用JavaScript中的数组方括号表示法时,在幕后发生了什么?

4

我一般发现JavaScript很透明,因为很少有“魔法”发生的黑盒子,你只需接受并看向其他方面。然而,我并没有找到关于数组括号[]符号在底层实际是如何工作的答案。

let arr = [4, 5, 6, 7]
console.log(arr[3]) // <- How does this work?

JavaScript如何访问索引为3的项?它是否在Array.prototype上内部调用某些方法?

对于对象,[]是属性访问器的一种快捷方式。

let obj = {
  a: 'hello',
  b: 'world'
}
obj['a'] === obj.a // true

数组只是一个具有一长串基于整数的属性的对象吗?

let objArray = {
   0: 'hello',
   1: 'world'
}
let realArray = ['hello', 'world']

objArray[0] === 'hello' // true
realArray[0] === 'hello' // true

objArray.0 // SyntaxError: Unexpected number
realArray.0 // SyntaxError: Unexpected number

我看过很多关于如何真正子类化一个数组的网络讨论,最终得出的结论是无法重载括号符号,但我从未见过有关在幕后发生了什么神奇的事情,使数组能够以这种方式工作的解释。

显然的后续问题是是否有任何方法截取括号符号访问以定义自己的行为,但我认为我已经知道答案了。


1
[ ] is an operator. I imagine if you could overload operators, you could customize what's happening under the hood. I imagine objArray.'0' would work as the key is a string, but after trying, it does not. Using it as the key does work, however: objArray[0] === objArray['0'] - Maximilian Burszley
"JavaScript 如何访问索引 3 的项目。" 它只是访问索引,除此之外的一切都是底层 JS 引擎的一部分,并且可能因为该特定引擎用于存储该特定数组的数据结构而有所不同。因为即使是数组的底层数据结构也可能因数组中当前数据和该数组经历的更改而异。通过重载属性访问,您想要实现什么行为? - Thomas
@Thomas 一个例子是创建一个链表。在JavaScript中,我可以按照自己的喜好创建列表结构,比如说它是一个ES6类。然后,我可以定义一个Symbol.Iterator,以便在for...of循环中使用我的新列表实例,或者使用展开运算符...list或解构let [a, b, c] = list。就所有目的而言,我有了一个漂亮的新数据类型。唯一不能做的事情是使用方括号表示法访问单个桶。我希望能够做类似于Symbol.Iterator的事情,这样我就可以为list[0]等定义自己的行为。 - Michael Sutherland
2个回答

2
您可能需要查看实现代码才能准确了解正在发生的事情,但基本思想是数组实际上是在对象之上分层的。
这是颠倒的:
使用对象时,[]是属性访问器的快捷方式。
方括号表示法更为基础。因此,obj ['foo']和obj.foo的作用相同,但是对于obj ['foo&bar']没有等效项,如果obj具有名为“foo&bar”的键,则会响应值。
数组不完全是一个具有长整数属性列表的对象,但您离正确答案并不远。数组是带有Array原型的对象,并且通过一些额外的魔法来设置length属性,以在添加新键或设置该长度时删除键。
不,您不能覆盖[]运算符以自己的目的。

“一个数组只是一个具有长整数属性列表的对象吗?” -> “不完全是这样,但你离答案不远了。” 在最坏的情况下,一个稀疏数组可能包含混合类型的内容,此时它可能会被存储为一个带有长整数属性列表的字典。 - Thomas
嗯...它仍然需要访问 Array.prototype 方法,并且必须能够使用 length 进行操作。但是其底层结构可以是字典。 - Scott Sauyet

1

数组只是一个具有整数属性列表的对象吗?

是的,在其最简单的形式中,数组是一个基于数组原型的具有整数属性列表的对象(该原型提供了访问所有数组方法(如map、forEach等)的方式)。

至于拦截方括号表示法,除了创建自己需要的方法的对象(然后仅通过适当的方法访问该对象),我没有看到任何允许这样做的东西。

来自MDN的更多信息:

数组是类似列表的对象,其原型具有执行遍历和变异操作的方法。JavaScript 数组的长度和元素类型都不固定。由于数组的长度可以随时更改,并且数据可以存储在数组中非连续的位置上,因此 JavaScript 数组不能保证是密集的;这取决于程序员选择如何使用它们。总的来说,这些都是方便的特性;但是,如果这些特性不适合您的特定用途,则可以考虑使用类型化数组。
数组不能使用字符串作为元素索引(如关联数组),而必须使用整数。使用括号表示法(或点表示法)设置或访问非整数将不会设置或检索数组列表本身的元素,而是将设置或访问与该数组对象属性集相关联的变量。数组的对象属性和数组元素列表是分开的,不能将数组的遍历和变异操作应用于这些命名属性。

2
面向对象编程(OOP):在其核心,一切都只是一个对象。 - Maximilian Burszley

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