为什么JavaScript没有last方法?

125

JavaScript的Array类没有提供一个last方法来检索数组的最后一个元素,这有点奇怪。我知道解决方法很简单(Ar [Ar.length-1]),但是这太常用了。

有什么严重的原因还没有纳入到Array类中吗?


39
对于那些不介意通过副作用修改数组(即该数组只是临时使用)的情况,最常用的方法是item = array.pop() - bobince
7
以下是各种方法的性能基准测试:http://jsperf.com/get-last-item-from-array - Web_Designer
5
天哪,在看了那个性能页面之后,似乎array[array.length-1]比其他方法快多了 - Jondlm
@JondIm 但是如果你在函数中创建一个数组,你需要为其发明本地名称(这会导致出现诸如arr2之类的名称),而且你需要两行代码而不是一行代码。 - Danubian Sailor
1
对我来说,(Ar[Ar.length-1]) 的性能提高了20倍。 - Evgeni Sergeev
显示剩余2条评论
10个回答

265
您可以像这样做:
[10, 20, 30, 40].slice(-1)[0]

console.log([10, 20, 30, 40].slice(-1)[0])

可以添加到语言中的辅助方法数量是无限的。我想他们可能没有考虑添加这个。


11
有意想要为“无限接近”而投下反对票。 - Kenan Banks
11
为什么?你可以尽可能靠近,没有限制 :) - Álvaro González
35
这个应该是答案。 - Giampaolo Rodolà
7
@UpTheCreek的意思是因为你不需要将数组存储到一个变量中。 - rmobis
5
这对于引用数组是可行的,但在您的示例中,您正在使用数字。根据MDN文档:“slice将字符串和数字复制到新数组中。对一个数组中的字符串或数字的更改不会影响另一个数组。”如果开发人员想要获取引用“array.last”以便对其原始数组中的最后一个元素的值执行某些操作,并且数组值恰好是字符串或数字文字,则此方法将无法使用。 - 1nfiniti
显示剩余6条评论

83

您可以很容易地自定义一个。这就是JavaScript的力量。

if(!Array.prototype.last) {
    Array.prototype.last = function() {
        return this[this.length - 1];
    }
}

var arr = [1, 2, 5];
arr.last(); // 5

然而,这可能会对第三方代码造成问题,这些代码(错误地)使用for..in循环来迭代数组。

但是,如果你不受浏览器支持的限制,那么使用新的ES5语法来定义属性就可以解决这个问题,方法是将函数设置为不可枚举,如下所示:

Object.defineProperty(Array.prototype, 'last', {
    enumerable: false,
    configurable: true,
    get: function() {
        return this[this.length - 1];
    },
    set: undefined
});

var arr = [1, 2, 5];
arr.last; // 5

11
请注意,向数组的原型添加属性可能会破坏使用for..in迭代数组的代码。使用for..in迭代数组是一种不良做法,但它被广泛使用,因此改变Array的原型也是不良做法。通常情况下,如果您的脚本需要与其他未知代码一起工作,则不应更改Object、Array、String、Number、Boolean和Date的原型。 - Dagg Nabbit
7
谢谢你的建议。我应该提到添加 ES5 语法并将其可枚举性设置为 false 的原因是为了解决 for..in 问题。虽然我们还没有广泛实现 ES5,但现在已经足够好了,因为各大浏览器包括 IE9 都在快速跟进。 - Anurag
4
对于空列表,this.length - 1 的值为 -1,由于它是一个负数,因此被视为数组的属性而不是元素索引。 - claymation

38

由于 JavaScript 变化缓慢。这是因为人们升级浏览器的速度很慢。

许多 JavaScript 库都实现了自己的 last() 函数。使用其中一个!


21
最起码,您可能希望考虑建议提供实现的库。例如,Underscore.js是一个不错的选择。请参见http://documentcloud.github.com/underscore/#last - Sean Lynch
11
@Sean - 我认为我应该避免推荐特定的JavaScript库给原始作者。谷歌在评估网络上哪个库最好使用方面做得很好。为什么我要推荐一个特定的库呢?事实上,为什么你会推荐underscore.js,对我来说这似乎只是一时的潮流? - Kenan Banks
7
我更喜欢下面的答案,即在库外实现该函数。话虽如此,作为通过谷歌偶然发现这个问题的人,我建议顶部答案可以帮助其他人继续寻找解决方案,而不是让他们返回按钮。 - Sean Lynch
5
问题是“为什么JavaScript没有内置这个功能”,而不是“如何实现此功能”。没有理由认为原作者正在寻找如何编写自己的last()函数。问题涉及JavaScript核心语言本身的开发性质。 - Kenan Banks
3
但是如果@Nikhil想知道如何实现它-underscore具有精彩的带注释源代码。这个last()的实现非常健壮,可能比作者需要的还要多,但对于一个库来说非常棒。http://documentcloud.github.com/underscore/docs/underscore.html#section-36 - reconbot
感谢 @SeanLynch 提供的 underscore.js。到目前为止,我一直在自己实现许多函数。 - Pujan

26
i = [].concat(loves).pop(); //corn 图标猫最喜欢爆米花。

16
警告:这样做会复制整个列表,仅仅为了弹出一个元素。这可能非常浪费时间和空间。不要这样做。 - Kenan Banks

13

另一个选择,特别是如果您已经在使用UnderscoreJS,那么可以考虑:

_.last([1, 2, 3, 4]); // Will return 4

5
Array.prototype.last = Array.prototype.last || function() {
    var l = this.length;
    return this[l-1];
}

x = [1,2];
alert( x.last() )

[].last()的适当输出应该是什么? null 还是 undefined - Álvaro González
1
可能未定义,以保持语言一致。 - meder omuraliev
我认为是null,因为你有一个定义良好的数组,只是其中没有有效的对象。当被调用的容器上最后一个“属性”未定义时,应仅使用undefined。 - Nikhil Garg
3
仅仅返回this[l-1]会使得你得到undefined,因为访问不存在的属性通常会得到这个结果。个人而言,我更希望 JavaScript 抛出一个异常而不是返回 undefined,但是 JavaScript 更喜欢隐藏错误。 - bobince

4

我自己也在寻找这个问题的答案。最好的方法可能是使用"slice",但我还是创建了一个"last"函数来练习扩展原型,所以我想分享一下。与其他一些方法相比,它有额外的好处,可以让你选择向后计数,并获取倒数第二或倒数第三个项目。如果您没有指定计数,它将默认为1并提取最后一个项目。

Array.prototype.last = Array.prototype.last || function(count) {
    count = count || 1;
    var length = this.length;
    if (count <= length) {
        return this[length - count];
    } else {
        return null;
    }
};

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr.last(); // returns 9
arr.last(4); // returns 6
arr.last(9); // returns 1
arr.last(10); // returns null

当您使用 last(4)last(9) 时,您会失去函数名称的含义,即 last - Prashanth Shyamprasad

3

这里有一种更简单的方法来切片最后几个元素。

 var tags = [1, 2, 3, "foo", "bar", "foobar", "barfoo"];
 var lastObj = tags.slice(-1);

lastObj现在是["barfoo"]

Python也是这样做的,当我尝试使用JS时它也起作用了。我猜脚本语言中的字符串操作方式是相同的。

同样地,如果你想要一个数组中最后两个对象,

var lastTwoObj = tags.slice(-2)

会给你["foobar", "barfoo"] 等等。


@Jakub,感谢你的编辑。我错过了至关重要的负号。 - Prashant
不会,lastObj 将包含 ["barfoo"]。无论如何,为什么我要使用 Array.prototype.slice.call(tags) 而不是直接使用 tags.slice - user663031

2

pop() 方法将弹出数组中的最后一个值。但问题在于您将失去数组中的最后一个值。


-1

是的,或者只是:

var arr = [1, 2, 5];
arr.reverse()[0]

如果你想要的是值,而不是一个新列表。


10
不确定,但这似乎相当缓慢。 - Jonathan
3
除了速度慢之外,它还有一个不好的副作用,即原始数组被颠倒。 - Stephen Quan

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