为什么在JavaScript中我无法执行array[-1]操作?

20

在Python中, 你可以这样做:

arr = [1,2,3]
arr[-1] // evaluates to 3

但在 JS 中,你不能:

let arr = [1,2,3];
arr[-1]; // evaluates to undefined

问题是:为什么?

我知道绕过它的技巧(arr[arr.length-1],修改数组原型等),但这不是重点。

我试图理解为什么在EcmaScript标准中仍然没有将负数组索引解释为从末尾开始的索引,尽管似乎很容易实现一个理解这一点的JS引擎(还有整个Python社区都对此表示欢迎)。

我错过了什么吗?


2
向后兼容性很重要。 - user2357112
2
因为他们没有选择让JavaScript以那种方式工作? - Herohtar
1
@Herohtar 这正是我的问题 - 他们为什么要这样做? - Nino Filiu
最好不要那样做。Python规定负整数键与正整数索引相关联,我认为这种抽象提供清晰度的假设是幼稚的。 - Rick
我更新了我的回答。现在,使用新的JavaScript .at()方法,这个问题有了一个官方的简单解决方案。请查看我的回答。也许你想把它接受为最佳答案。 - Mir-Ismaili
4个回答

31

你没有理解,数组是对象(奇异对象)且-1是一个有效的键。

var array = [1, 2, 3];

array[-1] = 42;

console.log(array);
console.log(array[-1]);


这解释了一切。但是在数组中使用负索引是一个好的编程模式吗?我看不出有任何非hack的用途。 - Nino Filiu
1
@NinoFiliu Javascript并不是以有很多防护栏杆而闻名的语言,而是以其简单、灵活和表达力强的语义而著称。它最初是作为一种脚本语言而开始的,让人们可以直接在标记中对其网站进行动画处理,在那个每个人都认为Web应用程序将被编写为Java小程序的时代。 - juanpa.arrivillaga
1
@NinoFiliu 如果你正在使用数组,使用负索引是没有意义的,而且你不应该这样做。例如,负索引不计入数组长度 - Herohtar

26
正如其他人所说,在Javascript中,array[-1]只是对由"-1"索引的array成员的引用(例如"length"),通常为undefined(因为array['-1']没有被评估为任何值)。

enter image description here

但是还有其他方法:

2021年更新(.at() 方法):

enter image description here

.at() 方法 现在可以在广泛的环境中使用:

检查您的浏览器是否支持它:

const array = [1, 2, 3]

try {
    console.log(array.at(-1))  // 3
    console.log(array.at(-2))  // 2
    console.log(array.at(-3))  // 1
    console.log(array.at(-4))  // undefined
} catch (e) {
    console.error("Sorry! Your browser doesn't support this feature yet!\nSee:\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at#browser_compatibility")
}

at() 方法接受整数值,并返回该索引处的项,允许使用正整数和负整数负整数从数组中的最后一项开始计数。

我可以使用吗?


使用.slice(-N)[0]

const array = [1, 2, 3]

console.log(array.slice(-1)[0])  // 3
console.log(array.slice(-2)[0])  // 2
console.log(array.slice(-3)[0])  // 1

在字符串中,你有另一种选择(而不是使用 [0])。

const string = 'ABC'

console.log(string.slice(-1))      // 'C'
console.log(string.slice(-2, -1))  // 'B'
console.log(string.slice(-3, -2))  // 'A'

或者使用.substr(-N, 1)

const string = 'ABC'

console.log(string.substr(-1))     // 'C'
console.log(string.substr(-2, 1))  // 'B'
console.log(string.substr(-3, 1))  // 'A'


效率有待商榷,就内存而言.. array.slice 会创建一个新的数组,因此在最坏情况下可能会使内存消耗翻倍。 - Lloyd
1
请注意,在桌面或移动版Safari浏览器上仍不支持Array.at - Isaac Moore

13

您可以使用arr[-1] - 它将尝试访问arr对象上的-1属性,当奇怪的代码分配给负索引时,这是可能的。例如:

const arr = [1,2,3]
arr[-1] = 'foo';
console.log(arr[-1]);

JavaScript属性访问一直以这种方式工作 - 因此,更改使[-1]引用数组中的最后一个项将是一种破坏性变化,标准非常努力地避免这种情况。(还记得它们因与极老且过时的MooTools版本不兼容而撤回了Array.prototype.flatten名称吗?只有少数网站仍在使用 - 这将更加糟糕)


4

因为大多数语言(例如像 indexOf 函数)会返回 -1 而不是一个无意义的异常。如果 -1 是一个有效的索引,那么以下代码将会返回 3 而不是 undefined

var arr = [1,2,3]
console.log(arr[arr.indexOf(4)])

在我看来,Python犯了一个错误,即使负数索引是有效的,这导致了许多不直接直观的奇怪后果。


1
这不是原因。数组索引启发式算法并非旨在促进特定函数的使用。如果有什么的话,恰恰相反 - 低级行为(如数组索引)首先被定义,然后在该规则集内编写函数。参考:"indexOf()在[ES5]中被添加到ECMA-262标准中" - rinogo
此外,indexOf() 方法可能会返回 undefined 或 null。 - siride

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