编写更好 JavaScript 的实用方法

目录

 

使用类型脚本

类型脚本强制执行类型安全

类型描述类型使重构更大的应用程序成为可能

类型脚本使团队架构通信更轻松

使用现代功能

同步和等待

让和康斯特

箭头 ==功能

传播操作员...

模板字体(模板字符串)

对象构造

始终假设您的系统已分发

修改您的代码并强制执行样式

测试您的代码

测试驱动程序–艾娃

间谍和斯图布斯–西诺恩

莫克斯–诺克

网络自动化–铀

永无止境的旅程


使用类型脚本

改善JS的第一件事就是不写JS。对于未启动的,TypeScript (TS)是 JS 的"编译"超级集(任何在 JS 中运行的 TS)。TS 在香草 JS 体验的基础上增加了一个全面的可选打字系统。长期以来,整个生态系统的 TS 支持不一致,以至于我不愿意推荐它。值得庆幸的是,那些日子早已过去,大多数框架支持 TS 开箱即用。现在,我们都在同一页上关于什么是 TS,让我们来谈谈为什么你会想使用它。


类型脚本强制执行类型安全

类型安全性描述了编译器验证所有类型在整个代码中以合法方式使用的流程。换句话说,如果您创建一个需要数字的功能:foo

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">foo</span>(someNum: number): <span style="color:var(--highlight-literal)">number</span> {
  <span style="color:var(--highlight-keyword)">return</span> someNum + <span style="color:var(--highlight-namespace)">5</span>;
}</code></span>

该功能仅应用数字调用:foo

 

<span style="color:#3c4146"><em>good</em></span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-symbol)">console</span>.log(foo(<span style="color:var(--highlight-namespace)">2</span>)); // prints <span style="color:var(--highlight-variable)">"7"</span></code></span>
<span style="color:#3c4146"><em>no good</em></span>
<span style="color:var(--highlight-color)"><code>console.<span style="color:var(--highlight-literal)">log</span>(foo(<span style="color:var(--highlight-variable)">"two"</span>)); <span style="color:var(--highlight-comment)">// invalid TS code</span></code></span>

除了在代码中添加类型之外,类型安全执行没有缺点。另一方面,好处太大,不容忽视。类型安全提供了额外的保护级别,防止常见的错误/错误,这是像JS这样的无法无天语言的祝福。

 

 

类型描述类型使重构更大的应用程序成为可能

重构大型JS应用程序可能是一场真正的噩梦。重构 JS 的大部分痛苦是由于它不强制执行函数签名。这意味着 JS 功能永远不会被真正滥用。例如,如果我有一个功能,用于1000个不同的服务:myAPI

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">myAPI</span>(someNum, someString) {
  <span style="color:var(--highlight-keyword)">if</span> (someNum > <span style="color:var(--highlight-namespace)">0</span>) {
    leakCredentials();
  } <span style="color:var(--highlight-keyword)">else</span> {
    <span style="color:var(--highlight-literal)">console</span>.log(someString);
  }
}</code></span>

我更改了一下呼叫签名:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">myAPI</span>(someString, someNum) {
  <span style="color:var(--highlight-keyword)">if</span> (someNum > <span style="color:var(--highlight-namespace)">0</span>) {
    leakCredentials();
  } <span style="color:var(--highlight-keyword)">else</span> {
    <span style="color:var(--highlight-literal)">console</span>.log(someString);
  }
}</code></span>

我必须100%确定,使用此功能的每个地方(数千个位置),我正确地更新了使用情况。如果我甚至错过了一个我的凭据可能会泄漏。以下是 TS 的相同方案:

<span style="color:#3c4146"><em>before</em></span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">myAPITS</span>(someNum: number, someString: <span style="color:var(--highlight-keyword)">string</span>) { ... }</code></span>
<span style="color:#3c4146"><em>after</em></span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">myAPITS</span>(someString: <span style="color:var(--highlight-keyword)">string</span>, someNum: number) { ... }</code></span>

如您所见,该函数与 JavaScript 对应体经历了相同的更改。但是,此代码不会导致有效的 JavaScript,而是会导致"类型脚本"无效,因为它使用的数千个位置现在提供了错误的类型。由于我们之前讨论过的类型安全性,这一千个案例将阻止汇编,您的凭据不会泄露(这总是不错的)。myAPITS


类型脚本使团队架构通信更轻松

当 TS 设置正确时,如果不首先定义界面和类,将很难编写代码。这提供了一种分享简洁、沟通性架构建议的方法。在 TS 之前,存在此问题的其他解决方案,但没有一个解决方案在不使您做额外工作的情况下本地解决。例如,如果我想为我的后端提出一种新类型,我可以使用 TS 向队友发送以下类型。Request

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">interface</span> <span style="color:var(--highlight-keyword)">BasicRequest</span> {
  <span style="color:var(--highlight-symbol)">body</span>: Buffer;
  <span style="color:var(--highlight-symbol)">headers</span>: { [header: string]: string | string[] | undefined; };
  <span style="color:var(--highlight-keyword)">secret</span>: <span style="color:var(--highlight-keyword)">Shhh</span>;
}</code></span>

我已经不得不编写代码,但现在我可以分享我的增量进度,并获得反馈,而无需投入更多的时间。我不知道 TS 本身是否比 JS 容易发生错误。我坚信,强制开发人员定义界面和API首先会导致更好的代码。

总的来说,TS已经发展成为香草JS的成熟和更可预测的替代品。开发人员肯定仍然需要对香草JS感到满意,但这些天我开始的大多数新项目都是从一开始的TS。
 

 


使用现代功能

JavaScript 是世界上最流行的(如果不是最)编程语言之一。你可能会想到,数以亿计的人使用的20多年前的语言现在大部分都会被弄明白,但事实恰恰相反。最近,JS(是的,我知道,技术上的 ECMAScript)进行了许多更改和添加,从根本上改变了开发人员体验。作为一个在过去两年才开始写JS的人,我的优势是没有偏见或期望。这导致在使用语言的哪些功能和避免哪些功能方面做出更加务实的选择。


同步等待

长期以来,异步的、事件驱动的回调是 JS 开发中不可避免的一部分:

<span style="color:#3c4146"><em>traditional callback</em></span>
<span style="color:var(--highlight-color)"><code>makeHttpRequest(<span style="color:var(--highlight-variable)">'google.com'</span>, <span style="color:var(--highlight-keyword)">function</span> (err, result) {
  <span style="color:var(--highlight-keyword)">if</span> (err) {
    <span style="color:var(--highlight-literal)">console</span>.log(<span style="color:var(--highlight-variable)">'Oh boy, an error'</span>);
  } <span style="color:var(--highlight-keyword)">else</span> {
    <span style="color:var(--highlight-literal)">console</span>.log(result);
  }
});</code></span>

我不会花时间解释为什么上面有问题 (但我以前有) 。为了解决回调问题,JS中增加了一个新概念——承诺。承诺允许您编写异步逻辑,同时避免以前困扰基于回调代码的嵌套问题。

<span style="color:#3c4146"><em>Promises</em></span>
<span style="color:var(--highlight-color)"><code>makeHttpRequest(<span style="color:var(--highlight-variable)">'google.com'</span>).then(<span style="color:var(--highlight-keyword)">function</span> (result) {
  <span style="color:var(--highlight-literal)">console</span>.log(result);
}).catch(<span style="color:var(--highlight-keyword)">function</span> (err) {
  <span style="color:var(--highlight-literal)">console</span>.log(<span style="color:var(--highlight-variable)">'Oh boy, an error'</span>);
});</code></span>

承诺比回调的最大优势是可读性和可链性。

虽然承诺是伟大的,他们仍然留下一些需要改进的东西。对许多人来说,承诺体验仍然让人想起回调。具体来说,开发人员要求替代承诺模式。为了解决这个问题,ECMAScript委员会决定增加一种新的利用承诺的方法,并:asyncawait

<span style="color:#3c4146">
<em><code>async</code> and <code>await</code></em>
</span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">try</span> {
  <span style="color:var(--highlight-keyword)">const</span> result = <span style="color:var(--highlight-keyword)">await</span> makeHttpRequest(<span style="color:var(--highlight-variable)">'google.com'</span>);
  <span style="color:var(--highlight-literal)">console</span>.log(result);
} <span style="color:var(--highlight-keyword)">catch</span> (err) {
  <span style="color:var(--highlight-literal)">console</span>.log(<span style="color:var(--highlight-variable)">'Oh boy, an error'</span>);
}</code></span>

一个警告是,任何你必须被宣布:awaitasync

<span style="color:#3c4146"><em>required definition of makeHttpRequest in prev example</em></span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">async</span> <span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">makeHttpRequest</span>(url) {
  <span style="color:var(--highlight-comment)">// ...</span>
}</code></span>

它也可以直接承诺,因为一个功能真的只是一个花哨的承诺包装。这也意味着代码和承诺代码在功能上是等价的。因此,请随意使用,而不会感到内疚。awaitasyncasync/awaitasync/await


康斯特
 

在 JS 存在的大部分时间里,只有一个可变范围限定符: 。 有一些非常独特/有趣的规则,关于它如何处理范围。范围行为是不一致的和混乱的,并导致意想不到的行为,因此在整个JS的一生的错误。但截至ES6,有一个替代:和。几乎没有需要再使用,所以不要再使用。使用的任何逻辑都可以转换为等效和基于代码。varvarvarvarconstletvarvarconstlet



 

至于何时使用与,我总是从宣布一切开始。 是更严格的和"即时",这通常会导致更好的代码。没有一吨的"真实场景",使用是必要的,我会说1/20变量,我宣布与。其余的都是。constletconstconstletletconst



 

我说是"不动的",因为它的工作方式与C/C++不同。对 JavaScript 运行时间的意义在于,该变量的引用永远不会更改。这并不意味着存储在该参考的内容永远不会更改。对于原始类型(数字、布尔等),确实会转化为不可变性(因为它是一个单一的内存地址)。但对于所有对象(类、阵列、听写),并不能保证不可变性。constconstconstconstconstconst



头 ==功能
 

箭头函数是 JS 中宣布匿名函数的简洁方法。匿名函数描述没有明确命名的函数。通常,匿名功能会作为回调或事件挂钩传递。

<span style="color:#3c4146">
<em>vanilla anonymous function</em>
</span>
<span style="color:var(--highlight-color)"><code>someMethod(<span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-keyword)">function</span> () { <span style="color:var(--highlight-comment)">// has no name</span>
  <span style="color:var(--highlight-literal)">console</span>.log(<span style="color:var(--highlight-variable)">'called'</span>);
});</code></span>

在大多数情况下,这种风格没有任何"错误"。香草匿名功能在范围方面表现得"有趣",这可能导致许多意想不到的错误。由于箭头功能,我们不必再担心这个问题了。以下是通过箭头功能实现的相同代码:

<span style="color:#3c4146"><em>anonymous arrow function</em></span>
<span style="color:var(--highlight-color)"><code>someMethod(<span style="color:var(--highlight-namespace)">1</span>, () => { <span style="color:var(--highlight-variable)">//</span> has <span style="color:var(--highlight-literal)">no</span> name
  <span style="color:var(--highlight-literal)">console</span>.log(<span style="color:var(--highlight-variable)">'called'</span>);
});</code></span>

箭头功能除了更加简洁外,还有更实际的界定行为。箭头函数从所定义的范围中继承。this

在某些情况下,箭头功能可以更加简洁:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">added</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">0</span>, <span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>, <span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>]<span style="color:var(--highlight-variable)">.map((item)</span> <span style="color:var(--highlight-variable)">=></span> <span style="color:var(--highlight-variable)">item</span> <span style="color:var(--highlight-variable)">+</span> <span style="color:var(--highlight-namespace)">1</span><span style="color:var(--highlight-variable)">);</span>
<span style="color:var(--highlight-variable)">console.log(added)</span> <span style="color:var(--highlight-variable)">//</span> <span style="color:var(--highlight-variable)">prints</span> <span style="color:var(--highlight-variable)">"[1, 2, 3, 4, 5]"</span></code></span>

位于单行上的箭头功能包括隐含语句。无需具有单行箭头功能的括号或半冒号。return

我想说清楚。这不是一种情况,香草匿名功能(特别是类方法)仍有有效的使用案例。也就是说,我发现,如果你总是默认为箭头功能,你最终做的调试少了很多,而不是默认香草匿名功能。var

<span style="color:#3c4146"><a data-cke-saved-href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions">As usual, the Mozilla docs are the best resource</a></span>



传播操作员...
 

提取一个对象的关键/值对并将其添加为另一个对象的子项是一个非常常见的场景。从历史上看,有几种方法可以做到这一点,但所有这些方法都相当笨重:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">const</span> obj1 = { <span style="color:var(--highlight-attribute)">dog</span>: <span style="color:var(--highlight-variable)">'woof'</span> };
<span style="color:var(--highlight-keyword)">const</span> obj2 = { <span style="color:var(--highlight-attribute)">cat</span>: <span style="color:var(--highlight-variable)">'meow'</span> };
<span style="color:var(--highlight-keyword)">const</span> merged = <span style="color:var(--highlight-literal)">Object</span>.assign({}, obj1, obj2);
<span style="color:var(--highlight-literal)">console</span>.log(merged) <span style="color:var(--highlight-comment)">// prints { dog: 'woof', cat: 'meow' }</span></code></span>

这种模式非常普遍,因此上述方法很快变得乏味。由于传播操作员,再也不需要使用它了:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">const</span> obj1 = { <span style="color:var(--highlight-attribute)">dog</span>: <span style="color:var(--highlight-variable)">'woof'</span> };
<span style="color:var(--highlight-keyword)">const</span> obj2 = { <span style="color:var(--highlight-attribute)">cat</span>: <span style="color:var(--highlight-variable)">'meow'</span> };
<span style="color:var(--highlight-literal)">console</span>.log({ ...obj1, ...obj2 }); <span style="color:var(--highlight-comment)">// prints { dog: 'woof', cat: 'meow' }</span></code></span>

最棒的是,这也与阵列无缝配合:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">arr1</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>]<span style="color:var(--highlight-variable)">;</span>
<span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">arr2</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>]<span style="color:var(--highlight-variable)">;</span>
<span style="color:var(--highlight-variable)">console.log([</span> <span style="color:var(--highlight-variable)">...arr1,</span> <span style="color:var(--highlight-variable)">...arr2</span> <span style="color:var(--highlight-variable)">]);</span> <span style="color:var(--highlight-variable)">//</span> <span style="color:var(--highlight-variable)">prints</span> [<span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>, <span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>]</code></span>

这可能不是最近最重要的JS功能,但它是我最喜欢的功能之一。



模板字体(模板字符串)

字符串是最常见的编程构造之一。这就是为什么它是如此尴尬, 本地宣布字符串仍然缺乏支持, 在许多语言。很长一段时间,JS是在"蹩脚的字符串"家庭。但是,模板字面的添加将JS归入了它自己的类别。模板字面文字原生和方便地解决两个最大的问题与书写字符串:添加动态内容和书写字符串,桥接多行:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">const</span> name = <span style="color:var(--highlight-variable)">'Ryland'</span>;
<span style="color:var(--highlight-keyword)">const</span> helloString =
<span style="color:var(--highlight-variable)">`Hello
 <span style="color:var(--highlight-color)">${name}</span>`</span>;</code></span>

我认为代码不言而喻。多么惊人的实现。



对象构造
 

对象破坏是从数据收集(对象、阵列等)中提取值的一种方式,无需在数据上重复或明确访问其密钥:

<span style="color:#3c4146"><em>old way</em></span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">animalParty</span>(dogSound, catSound) {}

<span style="color:var(--highlight-keyword)">const</span> myDict = {
  <span style="color:var(--highlight-attribute)">dog</span>: <span style="color:var(--highlight-variable)">'woof'</span>,
  <span style="color:var(--highlight-attribute)">cat</span>: <span style="color:var(--highlight-variable)">'meow'</span>,
};

animalParty(myDict.dog, myDict.cat);</code></span>

破坏

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">animalParty</span>(dogSound, catSound) {}

<span style="color:var(--highlight-keyword)">const</span> myDict = {
  <span style="color:var(--highlight-attribute)">dog</span>: <span style="color:var(--highlight-variable)">'woof'</span>,
  <span style="color:var(--highlight-attribute)">cat</span>: <span style="color:var(--highlight-variable)">'meow'</span>,
};

<span style="color:var(--highlight-keyword)">const</span> { dog, cat } = myDict;
animalParty(dog, cat);</code></span>

但是等一下,还有更多。您还可以在功能签名中定义破坏:

<span style="color:#3c4146"><em>destructuring 2</em></span>
<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">function</span> <span style="color:var(--highlight-literal)">animalParty</span>({ dog, cat }) {}

<span style="color:var(--highlight-keyword)">const</span> myDict = {
  <span style="color:var(--highlight-attribute)">dog</span>: <span style="color:var(--highlight-variable)">'woof'</span>,
  <span style="color:var(--highlight-attribute)">cat</span>: <span style="color:var(--highlight-variable)">'meow'</span>,
};

animalParty(myDict);</code></span>

它还适用于阵列:

<span style="color:#3c4146"><em>destructuring 3</em></span>
<span style="color:var(--highlight-color)"><code>[<span style="color:var(--highlight-variable)">a</span>, <span style="color:var(--highlight-variable)">b</span>] <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">10</span>, <span style="color:var(--highlight-namespace)">20</span>]<span style="color:var(--highlight-variable)">;</span>

<span style="color:var(--highlight-variable)">console.log(a);</span> <span style="color:var(--highlight-variable)">//</span> <span style="color:var(--highlight-variable)">prints</span> <span style="color:var(--highlight-namespace)">10</span></code></span>

您应该利用大量其他现代功能。以下是我最突出的少数其他:
 

始终假设您的系统已分发

在编写并行应用程序时,您的目标是优化您同时完成的工作量。如果您有四个可用的内核,并且您的代码只能使用单个内核,则 75% 的潜力正在被浪费。这意味着阻塞、同步操作是并行计算的最终敌人。但考虑到JS是单一的线程语言,事情不会在多个内核上运行。那有什么意义呢?

JS 是单线程,但不是单文件(如在学校的行
)。即使它不是平行的,它仍然是并发的。发送 HTTP 请求可能需要几秒钟甚至几分钟,因此,如果 JS 停止执行代码,直到请求回复,则该语言将无法使用。JavaScript 通过事件循环来解决这个问题。



事件循环循环通过已注册的事件,并根据内部调度/优先级逻辑执行它们。这能够同时发送数千个 HTTP 请求或从磁盘中读取多个文件。关键是:JavaScript 仅在使用正确的功能时才能使用此功能。最简单的例子是循环:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-variable)">let</span> <span style="color:var(--highlight-variable)">sum</span> <span style="color:var(--highlight-variable)">=</span> <span style="color:var(--highlight-namespace)">0</span><span style="color:var(--highlight-variable)">;</span>
<span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">myArray</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>, <span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>, <span style="color:var(--highlight-namespace)">5</span>, <span style="color:var(--highlight-variable)">...</span> <span style="color:var(--highlight-namespace)">99</span>, <span style="color:var(--highlight-namespace)">100</span>]<span style="color:var(--highlight-variable)">;</span>
<span style="color:var(--highlight-variable)">for</span> <span style="color:var(--highlight-variable)">(let</span> <span style="color:var(--highlight-variable)">i</span> <span style="color:var(--highlight-variable)">=</span> <span style="color:var(--highlight-namespace)">0</span><span style="color:var(--highlight-variable)">;</span> <span style="color:var(--highlight-variable)">i</span> <span style="color:var(--highlight-variable)"><</span> <span style="color:var(--highlight-variable)">myArray.length;</span> <span style="color:var(--highlight-variable)">i</span> <span style="color:var(--highlight-variable)">+=</span> <span style="color:var(--highlight-namespace)">1</span><span style="color:var(--highlight-variable)">)</span> {
  <span style="color:var(--highlight-variable)">sum</span> <span style="color:var(--highlight-variable)">+=</span> <span style="color:var(--highlight-variable)">myArray</span>[<span style="color:var(--highlight-variable)">i</span>]<span style="color:var(--highlight-variable)">;</span>
}</code></span>

香草环是编程中最不平行的构造之一。在上一份工作中,我带领一个团队花了几个月的时间尝试将传统的 lang 转圈转换为自动平行代码。这基本上是一个不可能解决的问题, 只有等待深度学习来改进才能解决。平行循环的困难源于一些有问题的模式。环环顺序非常罕见,但仅此一项就无法保证环的可分解性:R

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-symbol)">let</span> runningTotal = <span style="color:var(--highlight-namespace)">0</span>;
<span style="color:var(--highlight-symbol)">for</span> (let i = <span style="color:var(--highlight-namespace)">0</span>; <span style="color:var(--highlight-symbol)">i</span> < myArray.length; <span style="color:var(--highlight-symbol)">i</span> += <span style="color:var(--highlight-namespace)">1</span>) {
  <span style="color:var(--highlight-symbol)">if</span> (i === <span style="color:var(--highlight-namespace)">50</span> && runningTotal > <span style="color:var(--highlight-namespace)">50</span>) {
    <span style="color:var(--highlight-symbol)">runningTotal</span> = <span style="color:var(--highlight-namespace)">0</span>;
  }
  <span style="color:var(--highlight-symbol)">runningTotal</span> += Math.random() + runningTotal;
}</code></span>

此代码仅在按顺序执行、迭代迭代时生成预期结果。如果尝试同时执行多个迭代,处理器可能会根据不准确的值错误地分支,从而使结果失效。如果这是 C 代码,我们将进行不同的对话,因为使用情况不同,编译器可以使用循环进行相当多的技巧。在 JavaScript 中,只有在绝对必要时才应使用传统的环。否则,使用以下结构:



地图

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-comment)">// in decreasing relevancy :0</span>
<span style="color:var(--highlight-keyword)">const</span> urls = [<span style="color:var(--highlight-variable)">'google.com'</span>, <span style="color:var(--highlight-variable)">'yahoo.com'</span>, <span style="color:var(--highlight-variable)">'aol.com'</span>, <span style="color:var(--highlight-variable)">'netscape.com'</span>];
<span style="color:var(--highlight-keyword)">const</span> resultingPromises = urls.map((url) => makHttpRequest(url));
<span style="color:var(--highlight-keyword)">const</span> results = <span style="color:var(--highlight-keyword)">await</span> <span style="color:var(--highlight-literal)">Promise</span>.all(resultingPromises);</code></span>

带索引的地图

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-comment)">// in decreasing relevancy :0</span>
<span style="color:var(--highlight-keyword)">const</span> urls = [<span style="color:var(--highlight-variable)">'google.com'</span>, <span style="color:var(--highlight-variable)">'yahoo.com'</span>, <span style="color:var(--highlight-variable)">'aol.com'</span>, <span style="color:var(--highlight-variable)">'netscape.com'</span>];
<span style="color:var(--highlight-keyword)">const</span> resultingPromises = urls.map((url, index) => makHttpRequest(url, index));
<span style="color:var(--highlight-keyword)">const</span> results = <span style="color:var(--highlight-keyword)">await</span> <span style="color:var(--highlight-literal)">Promise</span>.all(resultingPromises);</code></span>

为每个

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-keyword)">const</span> urls = [<span style="color:var(--highlight-variable)">'google.com'</span>, <span style="color:var(--highlight-variable)">'yahoo.com'</span>, <span style="color:var(--highlight-variable)">'aol.com'</span>, <span style="color:var(--highlight-variable)">'netscape.com'</span>];
<span style="color:var(--highlight-comment)">// note this is non blocking</span>
urls.forEach(<span style="color:var(--highlight-keyword)">async</span> (url) => {
  <span style="color:var(--highlight-keyword)">try</span> {
    <span style="color:var(--highlight-keyword)">await</span> makHttpRequest(url);
  } <span style="color:var(--highlight-keyword)">catch</span> (err) {
    <span style="color:var(--highlight-literal)">console</span>.log(<span style="color:var(--highlight-variable)">`<span style="color:var(--highlight-color)">${err}</span> bad practice`</span>);
  }
});</code></span>

我会解释为什么这些是比传统的循环改进。构造(如接收所有元素并将其作为单个事件提交到用户定义的地图功能)等构造,而不是按顺序执行每个迭代。在大多数情况下,单个迭代彼此之间没有内在的联系或依赖,因此可以同时运行。这并不是说你不能完成同样的事情与循环。事实上,它看起来是这样的:map

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">items</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">0</span>, <span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>, <span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>, <span style="color:var(--highlight-namespace)">5</span>, <span style="color:var(--highlight-namespace)">6</span>, <span style="color:var(--highlight-namespace)">7</span>, <span style="color:var(--highlight-namespace)">8</span>, <span style="color:var(--highlight-namespace)">9</span>]<span style="color:var(--highlight-variable)">;</span>

<span style="color:var(--highlight-variable)">async</span> <span style="color:var(--highlight-variable)">function</span> <span style="color:var(--highlight-variable)">testCall()</span> {
  <span style="color:var(--highlight-variable)">//</span> <span style="color:var(--highlight-variable)">do</span> <span style="color:var(--highlight-variable)">async</span> <span style="color:var(--highlight-variable)">stuff</span> <span style="color:var(--highlight-variable)">here</span>
}

<span style="color:var(--highlight-variable)">for</span> <span style="color:var(--highlight-variable)">(let</span> <span style="color:var(--highlight-variable)">i</span> <span style="color:var(--highlight-variable)">=</span> <span style="color:var(--highlight-namespace)">0</span><span style="color:var(--highlight-variable)">;</span> <span style="color:var(--highlight-variable)">i</span> <span style="color:var(--highlight-variable)"><</span> <span style="color:var(--highlight-namespace)">10</span><span style="color:var(--highlight-variable)">;</span> <span style="color:var(--highlight-variable)">i</span> <span style="color:var(--highlight-variable)">+=</span> <span style="color:var(--highlight-namespace)">1</span><span style="color:var(--highlight-variable)">)</span> {
  <span style="color:var(--highlight-variable)">testCall();</span>
}</code></span>

正如你所看到的,循环并不妨碍我以正确的方式做,但它肯定也不会使它更容易。与地图版本相比:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">items</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">0</span>, <span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>, <span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>, <span style="color:var(--highlight-namespace)">5</span>, <span style="color:var(--highlight-namespace)">6</span>, <span style="color:var(--highlight-namespace)">7</span>, <span style="color:var(--highlight-namespace)">8</span>, <span style="color:var(--highlight-namespace)">9</span>]<span style="color:var(--highlight-variable)">;</span>
<span style="color:var(--highlight-variable)">items.map(async</span> <span style="color:var(--highlight-variable)">(item)</span> <span style="color:var(--highlight-variable)">=></span> {
 <span style="color:var(--highlight-variable)">//</span> <span style="color:var(--highlight-variable)">do</span> <span style="color:var(--highlight-variable)">async</span> <span style="color:var(--highlight-variable)">stuff</span> <span style="color:var(--highlight-variable)">here</span>
}<span style="color:var(--highlight-variable)">);</span></code></span>

正如你所看到的,只是工作。如果您要阻止,直到完成所有单独的异步操作,地图的优势就变得更加明显。有了循环代码,您需要自己管理一个数组。下面是版本:mapmap

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">items</span> <span style="color:var(--highlight-variable)">=</span> [<span style="color:var(--highlight-namespace)">0</span>, <span style="color:var(--highlight-namespace)">1</span>, <span style="color:var(--highlight-namespace)">2</span>, <span style="color:var(--highlight-namespace)">3</span>, <span style="color:var(--highlight-namespace)">4</span>, <span style="color:var(--highlight-namespace)">5</span>, <span style="color:var(--highlight-namespace)">6</span>, <span style="color:var(--highlight-namespace)">7</span>, <span style="color:var(--highlight-namespace)">8</span>, <span style="color:var(--highlight-namespace)">9</span>]<span style="color:var(--highlight-variable)">;</span>
 <span style="color:var(--highlight-variable)">const</span> <span style="color:var(--highlight-variable)">allResults</span> <span style="color:var(--highlight-variable)">=</span> <span style="color:var(--highlight-variable)">await</span> <span style="color:var(--highlight-variable)">Promise.all(items.map(async</span> <span style="color:var(--highlight-variable)">(item)</span> <span style="color:var(--highlight-variable)">=></span> {
  <span style="color:var(--highlight-variable)">//</span> <span style="color:var(--highlight-variable)">do</span> <span style="color:var(--highlight-variable)">async</span> <span style="color:var(--highlight-variable)">stuff</span> <span style="color:var(--highlight-variable)">here</span>
 }<span style="color:var(--highlight-variable)">));</span></code></span>
<span style="color:#3c4146">
<em>it's really that easy</em>
</span>

有许多情况下,一个循环将一样执行(或可能更多)相比,或。我仍然认为,现在失去几个周期是值得的优势,使用一个定义明确的API。这样,将来对数据访问模式实现的任何改进都将有利于您的代码。对于循环过于通用,无法对相同的模式进行有意义的优化。
除了地图和预选之外,还有其他有效的非同步选项,例如等待的选项。

mapforEach

 

修改您的代码并强制执行样式

没有一致风格的代码(外观和感觉)是令人难以置信的难以阅读和理解。因此,在任何语言中编写高端代码的一个关键方面是具有一致和明智的风格。由于 JS 生态系统的广度,有很多选择的绒毛和风格细节。我再怎么强调也不为过的是,你使用绒毛和强制执行一种风格(其中任何一种)比你特别选择的林特/风格要重要得多。归根结底,没有人会按照我的方式编写代码,因此为此进行优化是不切实际的目标。我看到很多人问他们应该使用埃斯林特还是更漂亮。



对我来说,它们服务于非常不同的目的,因此应该一起使用。埃斯林特在大多数情况下都是传统的绒毛。它会识别与您的代码问题,这些问题与样式关系不大,与正确性有关。例如,我使用与AirBNB规则的斜线。有了这种配置,以下代码将迫使绒毛发生故障:

<span style="color:var(--highlight-color)"><code><span style="color:var(--highlight-symbol)">var</span> fooVar = <span style="color:var(--highlight-namespace)">3</span>; // airbnb rules forebid <span style="color:var(--highlight-variable)">"var"</span></code></span>

应该很明显,eslint 如何为您的开发周期增加价值。从本质上讲,它确保你遵守关于什么是好的做法和不是好的做法的规则。正因这样,林特人天生就有意见。和所有的意见一样,用一粒盐来拿。林特可能是错的。
更漂亮是一个代码格式化
器。它不太关心正确性,更担心统一性和一致性。更漂亮不会抱怨使用,但它会自动对齐所有括号在你的代码。在我的个人开发过程中,我总是在将代码推给 Git 之前,将更漂亮地运行为最后一步。在许多情况下,在每个提交到回购时自动运行更漂亮甚至有意义。这确保了所有进入源控制的代码具有一致的样式和结构。var


测试您的代码

写作测试是一种间接但令人难以置信的有效方法,可以改进您编写的 JS 代码。我建议使用各种测试工具来适应。您的测试需求会有所不同,而且没有单一的工具可以处理所有问题。JS 生态系统中有许多成熟的测试工具,因此选择工具主要归结于个人品味。和往常一样,自己想想。



测试驱动程序–艾娃

<span style="color:#3c4146">
<a data-cke-saved-href="https://github.com/avajs" href="https://github.com/avajs">AvaJS on Github</a>
</span>

测试驱动程序只是框架,使结构和公用设施在一个非常高的水平。它们通常与其他特定测试工具一起使用,这些测试工具因您的测试需求而异。
艾娃是表现力和简洁性的正确
平衡。艾娃的平行和孤立的建筑是我最爱的源泉。运行速度更快的测试可节省开发人员的时间和公司资金。Ava 拥有大量不错的功能,例如内置断言,同时设法保持非常小。
替代品: 杰斯特, 摩卡, 茉
莉花



间谍和斯图布斯–西诺恩


吉图布的西诺恩

Spies 为我们提供了函数分析,例如一个函数被调用的次数、它们被调用的内容以及其他有见地的数据。

Sinon 是一个图书馆,做很多事情,但只有几个超级
好。具体来说,西诺恩擅长间谍和存根。功能集丰富,但语法简洁。这对于存根尤其重要,因为它们部分存在以节省空间。


 



莫克斯–诺克

Github

HTTP 模拟上的 Nock是伪造 http 请求过程的某些部分的过程,以便测试程序可以注入自定义逻辑来模拟服务器行为。

Http嘲笑可能是一种真正的痛苦,但诺克使它不那么
痛苦。诺克直接覆盖内置节点并拦截传出的 http 请求。这反过来又使您完全控制响应。


不知道任何request



网络自动化–铀

吉图布

上的铀我对推荐铀有复杂的情绪。因为它是网络自动化最流行的选项,它拥有庞大的社区和在线资源集。不幸的是,学习曲线是相当陡峭的,它依赖于大量的外部库的实际使用。这就是说,这是唯一真正的免费选项,所以除非你正在做一些企业级的网络自动化,Selenium将做的工作。
替代品: 柏树,
幻影


永无止境的旅程

与大多数事情一样,编写更好的 JavaScript 是一个连续的过程。代码总是可以更清洁,新功能被添加所有的时间,永远没有足够的测试。这似乎是压倒性的,但由于有这么多潜在的方面需要改进,你真的可以按照自己的节奏前进。一步一步来,在你知道之前,你将成为JavaScript的王牌。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值