博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第一个应在JavaScript数组的最后
阅读量:2520 次
发布时间:2019-05-11

本文共 11439 字,大约阅读时间需要 38 分钟。

by Thomas Barrasso

由Thomas Barrasso

第一个应在JavaScript数组的最后 (The first shall be last with JavaScript arrays)

So the last shall be [0], and the first [length — 1].

所以最后一个应该是[0] ,第一个[length_1]。

– Adapted from

–根据改编

I’ll skip the Malthusian Catastrophe and get to it: arrays are one of the simplest and most important data structures. While terminal elements (first and last) are frequently accessed, Javascript provides no convenient property or method for doing so and using indices can be redundant and prone to side effects and .

我将跳过马尔萨斯灾难,然后继续进行下去:数组是最简单也是最重要的数据结构之一。 虽然经常访问终端元素(第一个和最后一个),但是Javascript没有提供方便的属性或方法,使用索引可能是多余的,并且容易产生副作用和 。

A lesser-known, offers solace in the form of two “new” properties: Array.lastItem & Array.lastIndex.

鲜为人知的以两个“新”属性的形式提供了安慰: Array.lastItemArray.lastIndex

Javascript数组 (Javascript Arrays)

In many programming languages including Javascript, arrays are zero-indexed. The terminal elements–first and last– are accessed via the [0] and [length — 1] indices, respectively. We owe this pleasure to a , where an index represents an offset from the head of an array. That makes zero the first index because it is the array head. Also Dijkstra proclaimed “” So let it be written. So let it be done.

在包括Javascript在内的许多编程语言中,数组都是零索引的。 终端元素first和last分别通过[0][length — 1] length_1 [length — 1]索引进行访问。 我们将这种乐趣归功于的 ,其中索引表示距数组开头的偏移量。 这使第一个索引为零,因为它数组头。 迪克斯特拉还宣称“ ”所以就这样写吧。 因此,让它完成。

I suspect if you averaged access by index you would find that terminal elements are referenced most often. After all, arrays are commonly used to store a sorted collection and doing so places superlative elements (highest, lowest, oldest, newest, etc.) at the ends.

我怀疑如果按索引平均访问,您会发现终端元素被最频繁地引用。 毕竟,数组通常用于存储排序的集合,并且这样做会将最高级的元素(最高,最低,最旧,最新等)放置在末尾。

Unlike other scripting languages (say or ), Javascript does not provide convenient access to terminal array elements. Consider a trivial example of swapping the last elements in two arrays:

与其他脚本语言(例如或 )不同,Javascript无法提供对终端数组元素的便捷访问。 考虑一下在两个数组中交换最后一个元素的简单例子:

let faces = ["?", "?", "?", "?", "?"];let animals = ["?", "?", "?", "?", "?"];
let lastAnimal = animals[animals.length - 1];animals[animals.length - 1] = faces[faces.length - 1];faces[faces.length - 1] = lastAnimal;

The swapping logic requires 2 arrays referenced 8 times in 3 lines! In real-world code, this can quickly become very repetitive and difficult for a human to parse (though it is perfectly readable for a machine).

交换逻辑需要在3行中引用2次8的数组! 在现实世界的代码中,这可能很快就会变得非常重复且难以解析(尽管对于机器而言,这是完全可读的)。

What’s more, solely using indices, you cannot define an array and get the last element in the same expression. That might not seem important, but consider another example where the function, getLogins(), makes an asynchronous API call and returns a sorted array. Assuming we want the most recent login event at the end of the array:

而且,仅使用索引就无法定义数组并获得同一表达式中的最后一个元素。 这似乎并不重要,但请考虑另一个示例,其中的函数getLogins()进行异步API调用并返回已排序的数组。 假设我们希望在数组末尾有最新的登录事件:

let lastLogin = async () => {  let logins = await getLogins();  return logins[logins.length - 1];};

Unless the length is fixed and known in advance, we have to assign the array to a local variable to access the last element. One common way to address this in languages like and is to use negative indices. Then [length - 1] can be shortened to [-1], removing the need for local reference.

除非长度是固定的并且事先知道,否则我们必须将数组分配给局部变量以访问最后一个元素。 在和等语言中解决此问题的一种常见方法是使用负索引。 然后,可以将[length - 1]缩短为[-1] ,从而无需本地引用。

I find -1 only marginally more readable than length — 1, and while it is possible to approximate with or Array.slice(-1)[0], both come with for what should otherwise constitute simple random access.

我发现-1可读性仅比length — 1 Array.slice(-1)[0] ,虽然可以使用或Array.slice(-1)[0]来近似 ,但两者对于其他方面都具有 。构成简单的随机访问。

下划线和罗达斯 (Underscore & Lodash)

One of the most well-known principles in software development is Don’t Repeat Yourself (DRY). Since accessing terminal elements is so common, why not write a helper function to do it? Fortunately, many libraries like and already provide utilities for _.first & _.last.

软件开发中最著名的原则之一是“不要重复自己”(DRY)。 由于访问终端元素非常普遍,为什么不编写一个辅助函数来实现呢? 幸运的是,像许多图书馆和已经为公用事业_.first_.last

This offers a big improvement in the lastLogin() example above:

这在上面的lastLogin()示例中提供了很大的改进:

let lastLogin = async () => _.last(await getLogins());

But when it comes to the example of swapping last elements, the improvement is less significant:

但是,以交换最后一个元素为例,改进的意义并不大:

let faces = ["?", "?", "?", "?", "?"];let animals = ["?", "?", "?", "?", "?"];
let lastAnimal = _.last(animals);animals[animals.length - 1] = _.last(faces);faces[faces.length - 1] = lastAnimal;

These utility functions removed 2 of the 8 references, only now we introduced an external dependency that, oddly enough, does not include a function for setting terminal elements.

这些实用程序功能删除了8个引用中的2个,只是现在我们引入了一个外部依赖关系,奇怪的是,它不包含用于设置端子元素的功能。

Most likely such a function is deliberately excluded because its API would be confusing and hard to readable. Early versions of Lodash provided a method where n was the number of items from the end but it was ultimately removed in favor of (array, n).

很有可能故意排除了此类功能,因为其API会令人困惑且难以阅读。 Lodash的早期版本提供了方法其中n是末尾的项目数,但最终由于 (array, n)而被删除。

Assuming nums is an array of numbers, what would be the expected behavior of _.last(nums, n)? It could return the last two elements like _.take, or it could set the value of the last element equal to n.

假设nums是一个数字数组,则_.last(nums, n)的预期行为是什么? 它可以返回最后两个元素,例如_.take ,也可以将最后一个元素的值设置为n

If we were to write a function for setting the last element in an array, there are only a few approaches to consider using pure functions, method chaining, or using prototype:

如果我们要编写一个用于设置数组中最后一个元素的函数,则只有几种方法可以考虑使用纯函数,方法链接或原型:

let nums = ['d', 'e', 'v', 'e', 'l']; // set first = last
_.first(faces, _.last(faces));        // Lodash style
$(faces).first($(faces).last());      // jQuery style
faces.first(faces.last());            // prototype

I do not find any of these approaches to be much of an improvement. In fact, something important is lost here. Each performs an assignment, but none use the assignment operator (=).This could be made more apparent with naming conventions like getLast and setFirst, but that quickly becomes overly verbose. Not to mention the is full of programmers forced to navigate “self-documenting” legacy code where the only way to access or modify data is through getters and setters.

我认为这些方法都没有太大的改进。 实际上,这里丢失了一些重要的东西。 每个=都执行一个赋值,但是没有一个使用赋值运算符( = ),这可以通过诸如getLastsetFirst这样的命名约定变得更加明显,但是很快就会变得过于冗长。 更不用说的满是程序员被迫浏览“自我记录”的旧代码,而访问或修改数据的唯一方法是通过getter和setter。

Somehow, it looks like we are stuck with [0] & [length — 1]

不知何故,似乎我们陷入了[0][length — 1]困境……

Or are we? ?

还是我们? ?

提案 (The Proposal)

As mentioned, an ECMAScript Technical Candidate (TC39) proposal attempts to address this problem by defining two new properties on the Array object: lastItem & lastIndex. This proposal is in and usable today in Babel 7 & TypeScript. Even if you are not using a transpiler, this proposal includes a .

如前所述,ECMAScript技术候选人(TC39)提案试图通过在Array对象上定义两个新属性来解决此问题: lastItemlastIndex 。 该建议在 ,并且今天可以在Babel 7和TypeScript中使用。 即使你不使用transpiler,这一建议包括 。

Personally, I do not find much value in lastIndex and prefer Ruby’s shorter naming for , although this was ruled out because of . I am also surprised that this proposal does not suggest a firstItem property for consistency and symmetry.

就个人而言,我没有在lastIndex找到太多价值,并且更喜欢Ruby的 较短的命名,尽管由于而将其排除在外。 我也感到惊讶的是,该提议并未建议使用firstItem属性来保持一致性和对称性。

In the interim, I can offer a no-dependency, Ruby-esque approach in ES6:

在此期间,我可以在ES6中提供一种不依赖Ruby风格的方法:

第一和最后 (First & Last)

We now have two new Array properties–first & last–and a solution that:

我们现在有两个新的磁盘阵列属性- firstlast -和一个解决方案:

✓ Uses the assignment operator

✓使用赋值运算符

✓ Does not clone the array

✓不克隆阵列

✓ Can define an array and get a terminal element in one expression

✓可以定义一个数组并在一个表达式中获取一个终端元素

✓ Is human-readable

✓易于阅读

✓ Provides one interface for getting & setting

✓提供一个获取和设置的界面

We can rewrite lastLogin() again in a single line:

我们可以在一行中再次重写lastLogin()

let lastLogin = async () => (await getLogins()).last;

But the real win comes when we swap the last elements in two arrays with half the number of references:

但是,当我们用两个引用数的一半交换两个数组中的最后一个元素时,真正的胜利就来了:

let faces = ["?", "?", "?", "?", "?"];let animals = ["?", "?", "?", "?", "?"];
let lastAnimal = animals.last;animals.last = faces.last;faces.last = lastAnimal;

Everything is perfect and we have solved one of CS’ most difficult problems. There are no evil covenants hiding in this approach…

一切都很完美,我们已经解决了CS最棘手的问题之一。 这种方法没有隐藏邪恶的盟约……

原型偏执狂 (Prototype Paranoia)

Surely there is no one [programmer] on earth so righteous as to do good without ever sinning.– Adapted from

当然,在地球上,没有人[程序员]如此正义,以至没有做任何事都不会犯罪。–改编自

Many consider and a crime punishable by 100 years of programming in Java. Prior to the introduction of the property, could change the behavior of for in loops. It could also lead to conflict between various libraries, frameworks, and third-party dependencies.

许多人认为 ,是一种使用Java进行100年编程应受惩罚的罪行。 在引入属性之前, 可能会更改for in循环的行为。 它还可能导致各种库,框架和第三方依赖项之间的冲突。

Perhaps the most insidious issue is that, without compile-time tools, a simple spelling mistake could .

也许最阴险的问题是,如果没有编译时工具,一个简单的拼写错误可能会 。

let faces = ["?", "?", "?", "?", "?"];let ln = faces.length
faces.lst = "?"; // (5) ["?", "?", "?", "?", "?", lst: "?"]
faces.lst("?");  // Uncaught TypeError: faces.lst is not a function
faces[ln] = "?"; // (6) ["?", "?", "?", "?", "?", "?"]

This concern is not unique to our approach, it applies to all native Object prototypes (including arrays). Yet this offers safety in a different form. Arrays in Javascript are not fixed in length and consequently, there are no IndexOutOfBoundsExceptions. Using Array.last ensures we do not accidentally try to access [length] and unintentionally enter undefined territory.

这种担忧并非我们的方法所独有,它适用于所有本机Object原型(包括数组)。 但这以另一种形式提供了安全性。 Javascript中的数组长度不固定,因此没有IndexOutOfBoundsExceptions 。 使用Array.last确保我们不会意外尝试访问[length]并无意间输入undefined区域。

No matter which approach you take, there are pitfalls. Once again, software proves to be an .

无论您采用哪种方法,都有陷阱。 再次证明,软件是的 。

Continuing with the extraneous biblical reference, assuming we do not believe extending Array.prototype is an eternal sin, or we’re willing to take a bite of the forbidden fruit, we can use this concise and readable syntax today!

继续使用无关紧要的圣经参考,假设我们不认为扩展Array.prototype是永恒的罪过,或者我们愿意咬一口禁果,那么今天就可以使用这种简洁易懂的语法!

最后的话 (Last Words)

Programs must be written for people to read, and only incidentally for machines to execute. –

必须编写程序供人们阅读,并且只能偶然地使机器执行。 – (

In scripting languages like Javascript, I prefer code that is functional, concise, and readable. When it comes to accessing terminal array elements, I find the Array.last property to be the most elegant. In a production front-end application, I might favor Lodash to minimize conflict and cross-browser concerns. But in Node back-end services where I control the environment, I prefer these custom properties.

在像Javascript这样的脚本语言中,我更喜欢功能性,简洁性和可读性的代码。 在访问终端数组元素时,我发现Array.last属性是最优雅的。 在生产前端应用程序中,我可能会喜欢Lodash以最大程度地减少冲突和跨浏览器的问题。 但是在我控制环境的Node后端服务中,我更喜欢这些自定义属性。

I am certainly , nor will I be the last, to appreciate the value (or caution about the implications) of properties like Array.lastItem, which is hopefully coming soon to a version of ECMAScript near you.

当然,我 ,也不是最后一个欣赏Array.lastItem之类的属性的价值(或谨慎暗示)的Array.lastItem ,希望该属性很快会出现在您附近的ECMAScript版本中。

Follow me on · ·

在 关注我· ·

翻译自:

转载地址:http://vhrwd.baihongyu.com/

你可能感兴趣的文章
什么是blob,mysql blob大小配置介绍
查看>>
模运算的规则
查看>>
CSS样式布局入门介绍,非常详尽
查看>>
android app崩溃日志收集以及上传
查看>>
3、VS2010+ASP.NET MVC4+EF4+JqueryEasyUI+Oracle项目开发之——用户登录
查看>>
面试记-(1)
查看>>
压力测试 相关
查看>>
android update automatically ( android 自动升级)
查看>>
session cookie
查看>>
POJ 1222 EXTENDED LIGHTS OUT(翻转+二维开关问题)
查看>>
【BZOJ-4059】Non-boring sequences 线段树 + 扫描线 (正解暴力)
查看>>
几种简单的负载均衡算法及其Java代码实现
查看>>
TMS3705A PCF7991AT 线路图
查看>>
白盒测试实践(小组作业)day4
查看>>
为什么学sail.js
查看>>
pythen创建cocos2dx项目
查看>>
js调用.net后台事件,和后台调用前台等方法总结
查看>>
Vert.x 之 HelloWorld
查看>>
太阳能路灯项目背景知识
查看>>
Objec类和final关键字的用法
查看>>