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 Matthew 20:16

–根据马太福音20:16改编

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 off-by-one errors.

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

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

最近鲜为人知的JavaScript TC39提案以两个“新”属性的形式提供了安慰: 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 precedent set by C, 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 “zero as a most natural number.” So let it be written. So let it be done.

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

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 PHP or Elixir), Javascript does not provide convenient access to terminal array elements. Consider a trivial example of swapping the last elements in two arrays:

与其他脚本语言(例如PHP或Elixir )不同,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 Python and Ruby is to use negative indices. Then [length - 1] can be shortened to [-1], removing the need for local reference.

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

I find -1 only marginally more readable than length — 1, and while it is possible to approximate negative array indices in Javascript with ES6 Proxy or Array.slice(-1)[0], both come with significant performance implications for what should otherwise constitute simple random access.

我发现-1可读性仅比length — 1 Array.slice(-1)[0] ,虽然可以使用ES6 Proxy或Array.slice(-1)[0]来近似Javascript中的负数组索引 ,但两者对于其他方面都具有重大的性能影响 。构成简单的随机访问。

下划线和罗达斯 (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 Underscore and Lodash already provide utilities for _.first & _.last.

软件开发中最著名的原则之一是“不要重复自己”(DRY)。 由于访问终端元素非常普遍,为什么不编写一个辅助函数来实现呢? 幸运的是,像许多图书馆下划线和Lodash已经为公用事业_.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 _.last(array, n) where n was the number of items from the end but it was ultimately removed in favor of _.take(array, n).

很有可能故意排除了此类功能,因为其API会令人困惑且难以阅读。 Lodash的早期版本提供了_.last(array, n)方法_.last(array, n)其中n是末尾的项目数,但最终由于_.take (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 fifth circle of hell 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 already supported in core-js 3 and usable today in Babel 7 & TypeScript. Even if you are not using a transpiler, this proposal includes a polyfill.

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

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

就个人而言,我没有在lastIndex找到太多价值,并且更喜欢Ruby的firstlast较短的命名,尽管由于潜在的Web兼容性问题而将其排除在外。 我也感到惊讶的是,该提议并未建议使用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 Ecclesiastes 7:20

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

Many consider extending a native Object’s prototype an anti-pattern and a crime punishable by 100 years of programming in Java. Prior to the introduction of the enumerable property, extending Object.prototype could change the behavior of for in loops. It could also lead to conflict between various libraries, frameworks, and third-party dependencies.

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

Perhaps the most insidious issue is that, without compile-time tools, a simple spelling mistake could inadvertently create an associative array.

也许最阴险的问题是,如果没有编译时工具,一个简单的拼写错误可能会无意间创建一个关联数组 。

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 art of making tradeoffs.

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

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. – Harold Abelson

必须编写程序供人们阅读,并且只能偶然地使机器执行。 – 哈罗德·阿伯森 ( Harold Abelson)

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 not the first, 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 LinkedIn · GitHub · Medium

在领英 上关注我· GitHub · 中

翻译自: https://www.freecodecamp.org/news/the-first-shall-be-last-with-javascript-arrays-11172fe9c1e0/

第一个应在JavaScript数组的最后相关推荐

  1. JavaScript 数组拼接打印_JavaScript 数组方法

    JavaScript 数组方法 JS 数组 JS 数组排序 JavaScript 数组的力量隐藏在数组方法中. 把数组转换为字符串 JavaScript 方法 toString() 把数组转换为数组值 ...

  2. 创建零填充JavaScript数组的最有效方法?

    在JavaScript中创建任意长度的零填充数组的最有效方法是什么? #1楼 使用对象符号 var x = []; 零填充? 喜欢... var x = [0,0,0,0,0,0]; 充满" ...

  3. splice方法_[7000字]JavaScript数组所有方法基础总结

    基础决定一个人的上限,很多时候我们感叹别人在实现一个功能时使用方法的精妙,并且反思,为什么别人想的出来自己却想不出来?我觉得主要是因为对于基础的掌握上有很大的差距.本文总结数组的所有方法的基础使用,希 ...

  4. JavaScript数组常用方法解析和深层次js数组扁平化

    前言 数组作为在开发中常用的集合,除了for循环遍历以外,还有很多内置对象的方法,包括map,以及数组筛选元素filter等. 注:文章结尾处附深层次数组扁平化方法操作. 作为引用数据类型的一种,在处 ...

  5. JavaScript 数组操作大全

    1. pop() pop() 方法从数组中删除最后一个元素: 实例 var fruits = ["Banana", "Orange", "Apple& ...

  6. 这是如何更好地利用JavaScript数组的方法

    by pacdiv 由pacdiv 这是如何更好地利用JavaScript数组的方法 (Here's how you can make better use of JavaScript arrays) ...

  7. 判断javascript数组的方法

    2019独角兽企业重金招聘Python工程师标准>>> 判断javascript数组的方法 var is_array=function(){ return value &&a ...

  8. 深入浅出 JavaScript 数组 v0.5

    有一段时间不更新博客了,今天分享给大家的是一篇关于JS数组的,数组其实比较简单,但是用法非常灵活,在工作学习中应该多学,多用,这样才能领会数组的真谛. 以下知识主要参考<JS 精粹>和&l ...

  9. javascript 数组对象中的迭代方法

    /* javascript 数组对象中的迭代方法 * ECMAScript5为数组定义了5个迭代方法.每个方法都接受两个参数,第一个是进行迭代的函数,第二个是该函数的作用域对象[可选]. * 进行迭代 ...

最新文章

  1. 【Codeforces】1104C Grid game (变异的俄罗斯方块)
  2. ARP***原理与解决方法《一》
  3. ubuntu 16.04 mysql5.7.17 开放远程3306端口
  4. 【Hibernate】JDBC操作与hibernate的区别
  5. 程序员江湖鄙视链大全,看看你处于链条的哪一级?
  6. 【数据结构】对快速排序原理的理解(图解,通俗易懂)
  7. 一些jquery的使用方法
  8. keil4怎么移植其他人的程序_【调试笔记】韦东山:在100ask_imx6ull上移植使用六轴传感器ICM20608...
  9. c#获取ssl证书有效性_c# – 获取网站SSL证书的公钥
  10. 阿里火力全开 IoT!
  11. ZOJ 3430 Detect the Virus
  12. mac idea 快捷键
  13. 内网安全“小迪安全课堂笔记”域横向
  14. 制造型企业呼叫中心搭建-SDCC呼叫中心
  15. 20172328的结对编程四则运算第二周-整体总结
  16. 申请CSDN博客专家认证成功
  17. 如何用自签名证书给.Sis文件签名
  18. 【HDLBits刷题】Dff8r
  19. K2P刷机教程转自恩山磨人的小妖精
  20. python中的魔法方法__new___Python魔法方法会调用new方法吗?

热门文章

  1. Bloom Filter算法
  2. 使用Tomcat+MyEclipse开发Java Web配置
  3. java高分面试指南:java定时删除文件
  4. 详解getchar()函数与缓冲区
  5. 2015/12/15--Document对象
  6. 使用labview读取一副位图,并进行BCG校准(或修改其BCG)程序解析
  7. 并行开发 —— 第六篇 异步编程模型
  8. 解决Warning: Cannot modify header information - headers already sent b...
  9. 一、策略模式(Strategy Pattern)
  10. 快速理解binary cross entropy 二元交叉熵