第一个应在JavaScript数组的最后

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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391734.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鼠标移动到ul图片会摆动_我们可以从摆动时序分析中学到的三件事

鼠标移动到ul图片会摆动An opportunity for a new kind of analysis of Major League Baseball data may be upon us soon. Here’s how we can prepare.不久之后,我们将有机会对美国职棒大联盟数据进行新的分析。 这是我们准备的方法。 It is tempting to think t…

leetcode 1052. 爱生气的书店老板(滑动窗口)

今天,书店老板有一家店打算试营业 customers.length 分钟。每分钟都有一些顾客(customers[i])会进入书店,所有这些顾客都会在那一分钟结束后离开。 在某些时候,书店老板会生气。 如果书店老板在第 i 分钟生气&#xf…

回到网易后开源APM技术选型与实战

篇幅一:APM基础篇\\1、什么是APM?\\APM,全称:Application Performance Management ,目前市面的系统基本都是参考Google的Dapper(大规模分布式系统的跟踪系统)来做的,翻译传送门《google的Dappe…

持续集成持续部署持续交付_如何开始进行持续集成

持续集成持续部署持续交付Everything you need to know to get started with continuous integration: branching strategies, tests automation, tools and best practices.开始进行持续集成所需的一切:分支策略,测试自动化,工具和最佳实践。…

51nod 1073约瑟夫环

思路传送门 &#xff1a;http://blog.csdn.net/kk303/article/details/9629329 n里面挑选m个 可以递推从n-1里面挑m个 然后n-1里面的x 可以转换成 n里面的x 的公式 x &#xff08;xm&#xff09;%n; #include <bits/stdc.h> using namespace std;int main () {int n,m;s…

如何选择优化算法遗传算法_用遗传算法优化垃圾收集策略

如何选择优化算法遗传算法Genetic Algorithms are a family of optimisation techniques that loosely resemble evolutionary processes in nature. It may be a crude analogy, but if you squint your eyes, Darwin’s Natural Selection does roughly resemble an optimisa…

robot:截图关键字

参考&#xff1a; https://www.cnblogs.com/hong-fithing/p/9656221.html--python https://blog.csdn.net/weixin_43156282/article/details/87350309--robot https://blog.csdn.net/xiongzaiabc/article/details/82912280--截图指定区域 转载于:https://www.cnblogs.com/gcgc/…

leetcode 832. 翻转图像

给定一个二进制矩阵 A&#xff0c;我们想先水平翻转图像&#xff0c;然后反转图像并返回结果。 水平翻转图片就是将图片的每一行都进行翻转&#xff0c;即逆序。例如&#xff0c;水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。 反转图片的意思是图片中的 0 全部被 1 替换&#xff…

SVN服务备份操作步骤

SVN服务备份操作步骤1、准备源服务器和目标服务器源服务器&#xff1a;192.168.1.250目标服务器&#xff1a;192.168.1.251 root/rootroot 2、对目标服务器&#xff08;251&#xff09;装SVN服务器&#xff0c; 脚本如下&#xff1a;yum install subversion 3、创建一个新的仓库…

SpringCloud入门(一)

1. 系统架构演变概述 #mermaid-svg-F8dvnEDl6rEgSP97 .label{font-family:trebuchet ms, verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-F8dvnEDl6rEgSP97 .label text{fill:#333}#mermaid-svg-F8dvnEDl6rEgSP97 .node rect,#merm…

PullToRefreshListView中嵌套ViewPager滑动冲突的解决

PullToRefreshListView中嵌套ViewPager滑动冲突的解决 最近恰好遇到PullToRefreshListView中需要嵌套ViewPager的情况,ViewPager 作为头部添加到ListView中&#xff0c;发先ViewPager在滑动过程中流畅性太差几乎很难左右滑动。在网上也看了很多大神的介绍&#xff0c;看了ViewP…

神经网络 卷积神经网络_如何愚弄神经网络?

神经网络 卷积神经网络Imagine you’re in the year 2050 and you’re on your way to work in a self-driving car (probably). Suddenly, you realize your car is cruising at 100KMPH on a busy road after passing through a cross lane and you don’t know why.想象一下…

数据特征分析-分布分析

分布分析用于研究数据的分布特征&#xff0c;常用分析方法&#xff1a; 1、极差 2、频率分布 3、分组组距及组数 df pd.DataFrame({编码:[001,002,003,004,005,006,007,008,009,010,011,012,013,014,015],\小区:[A村,B村,C村,D村,E村,A村,B村,C村,D村,E村,A村,B村,C村,D村,E村…

开发工具总结(2)之全面总结Android Studio2.X的填坑指南

前言&#xff1a;好多 Android 开发者都在说Android Studio太坑了&#xff0c;老是出错&#xff0c;导致开发进度变慢&#xff0c;出错了又不知道怎么办&#xff0c;网上去查各种解决方案五花八门&#xff0c;有些可以解决问题&#xff0c;有些就是转来转去的写的很粗糙&#x…

无聊的一天_一人互联网公司背后的无聊技术

无聊的一天Listen Notes is a podcast search engine and database. The technology behind Listen Notes is actually very very boring. No AI, no deep learning, no blockchain. “Any man who must say I am using AI is not using True AI” :)Listen Notes是一个播客搜索…

如何在Pandas中使用Excel文件

From what I have seen so far, CSV seems to be the most popular format to store data among data scientists. And that’s understandable, it gets the job done and it’s a quite simple format; in Python, even without any library, one can build a simple CSV par…

Js实现div随鼠标移动的方法

HTML: <div id"odiv" style" COLOR: #666; padding: 2px 8px; FONT-SIZE: 12px; MARGIN-RIGHT: 5px; position: absolute; background: #fff; display: block; border: 1px solid #666; top: 50px; left: 10px;"> Move_Me</div>第一种&…

leetcode 867. 转置矩阵

给你一个二维整数数组 matrix&#xff0c; 返回 matrix 的 转置矩阵 。 矩阵的 转置 是指将矩阵的主对角线翻转&#xff0c;交换矩阵的行索引与列索引。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[[1,4,7],[2,5,8],[3,6,9]] …

数据特征分析-对比分析

对比分析是对两个互相联系的指标进行比较。 绝对数比较(相减)&#xff1a;指标在量级上不能差别过大&#xff0c;常用折线图、柱状图 相对数比较(相除)&#xff1a;结构分析、比例分析、空间比较分析、动态对比分析 df pd.DataFrame(np.random.rand(30,2)*1000,columns[A_sale…

Linux基线合规检查中各文件的作用及配置脚本

1./etc/motd 操作&#xff1a;echo " Authorized users only. All activity may be monitored and reported " > /etc/motd 效果&#xff1a;telnet和ssh登录后的输出信息 2. /etc/issue和/etc/issue.net 操作&#xff1a;echo " Authorized users only. All…