显式JavaScript功能

图片


当我读到另一篇有关JavaScript语言鲜为人知的功能的文章,并在浏览器控制台中悄悄撒尿一些疯狂的解决方案时,我经常在脑海中说,当然,产品上肯定不是这样! 毕竟,该语言早已获得了庞大的社区,并且具有令人惊讶的工业发展范围。 如果是这样,那为什么我们经常忘记他被所有人理解并真正促进所有这些具体和“令人难忘”的解释的能力? 使其显而易见!


关于该主题的推理


您可以跳过此纸质狂。


如果说到工业发展,那么在大多数情况下,支持代码的要求比解决企业提出的任务更为重要。 对于许多人来说,这在某些情况下是显而易见的-部分(当然,还发现了罕见的D'Artagnans)。 我们的代码越清晰,进入尘土飞扬的架子的风险就越小,并且我们和我们的继任者会在神经系统中遇到问题。


众所周知,JavaScript的灵活性惊人,这既是其最大的优点,也是令人讨厌的诅咒。 JavaScript开发人员的道路漫长且极为有趣:我们逐本书,逐篇文章地学习,并获得独特的经验,但有时它确实是特定于语言的。 该语言的分布最广,同时又有大量累积的和不可食用的非显而易见性,这有助于形成两个阵线:几乎将这种语言偶像化的人,以及将其视为笨拙和摇摆不定的权利低头的人。


一切都会好起来的,但是通常这两个阵线的代表都在同一个项目上工作。 通常,所有被接受的做法都是对彼此的代码的误解(不愿意理解甚至忽略)。 实际上, “我有一个Java开发人员,而这不是您的!” 。 Java语言的追随者们自己为火上加油,说: “没有人真正了解JavaScript!” 是的“我可以在js中一行编写它!” 。 我承认我自己闲暇时都在滥用异常编程...


当您代替边缘人并获得在路障两边与人及其代码打交道的经验时,就会开始感到这个问题。 当所有开发人员不仅在业务线层次上相互了解,而且在实施水平上至少相互了解时,计划会议和其他会议也会更有成效。 臭名昭著的低音因素对项目的影响较小,当一个前锋患病时,团队的其他成员都不会轻视纠正.js文件的某些行。 当每个人都有更详细的情况时,在团队内外共享知识的过程将对每个人变得更加透明。 好吧,一切都在同一条脉线上。


我不敦促任何人“全杯”或“ T形”(现在怎么说)?但是,如果仅来自JavaScript社区,为什么我们不把这个帷幕抬高一点呢? 为此,只需使用语言的灵活性而不是炫耀,而是易于理解,就可以使我们的代码更加清晰。


成长并承担责任


就其本身而言,JavaScript早就意识到了它的作用,而不是作为一种用于Internet页面交互和“粘贴”其资源的语言,而是作为一种功能强大且足够的工具来创建完整的跨平台且通常具有高度可伸缩性的应用程序。


尽管这种流行度和相关性迅速增长,但这种“最易被误解的编程语言”最初是为网页设计师设计的,它已经投入了很长时间。 在ECMAScript 5.1发行之前的13至14年中,很难回忆起标准中的任何重要更改或了解其发展的方向。 当时,他的社区为语言生态系统的形成做出了巨大贡献:原型,jQuery,MooTools等。 在收到开发者的反馈后,JavaScript进行了重大的错误工作:由于TC39委员会重新设计了规范引入新功能的流程,因此在2015年大声发布了6年的ES6,现在是ECMAScript的年度版本。


好吧,当我们的应用程序变得足够大时,由于一种不寻常的方法,用于描述用户类型的原型OOP模型不再合理。 说真的,这是什么?


function Animal() { /* Call me via new and I will be the constructor ;) */ } function Rabbit() {} Rabbit.prototype = Object.create(Animal.prototype); Rabbit.prototype.constructor = Rabbit; 

类没有出现在语言中,但是出现了语法。 这些代码已可供传统的面向类的范例的拥护者使用:


 class Animal { constructor() { /* Obviously, the constructor is here! */ } } class Rabbit extends Animal {} 

现在,要发布的候选者是该类的私有字段 。 很难相信,我们早晚会通过下划线命名私有财产的协议而停止彼此开怀大笑。


同时,在一种函数是一阶对象且发生常量事件的语言中,这很常见:


 let that = this; setTimeout(function() { that.n += 1; }, 1000); 

然后开始对此上下文的解释以及JavaScript的闭包,这使第二个外部开发人员感到恐惧。 但是在许多情况下,该语言通过显式使用Function.prototype.bind甚至这样避免了不必要的意外:


 setTimeout(() => this.n += 1, 1000); 

我们还获得了箭头函数,它们实际上是函数,而不是函数接口(是,Java吗?)。 结合使用一组扩展的数组处理方法,它们还有助于编写通常的声明式支付线:


 [-1, 2, -3, 4] .filter(x => x > 0) .map(x => Math.pow(2, x)) .reduce((s, x) => s + x, 0); 

语言正确地认为自己是多范式的。 但是这是一个有关某些函数签名的简单示例:


 function ping(host, count) { count = count || 5; /* send ping to host count times */ } 

首先,一个路过的人会问一个问题,即一个函数可能只能接受第一个参数,然后说在这种情况下,到底是什么, count变成布尔值! 实际上,该函数有两个用途:有计数和无计数 。 但这是完全不明显的:您必须查看实现并理解。 使用JSDoc可以提供帮助,但这不是常见的做法。 JavaScript向前发展,不增加对重载的支持,至少对默认参数增加了支持:


 function ping(host, count = 5) { /* ... */ } 

总而言之,JavaScript有很多熟悉的东西:生成器,迭代器, Set集合和Map字典,类型数组,甚至正则表达式也开始令人欣喜。 语言会做所有事情以适合许多事物并变得对所有人友好。


通往显而易见的有利之路


语言本身当然做得很好,很难与之抗争! 但是,我们怎么了? 为什么我们不断提醒全世界JavaScript有所不同? 让我们看一些广泛使用的技术的例子,并询问它们的适当性。


型铸


是的,JavaScript具有动态弱类型系统,可让您对任何内容执行操作,从而为我们隐式执行转换。 但是通常,显式强制转换对于我们仍然是必需的,并且可以观察到以下内容:


 let bool = !!(expr); let numb = +(expr); let str = ''+(expr); 

这些技巧对每个JavaScript开发人员都是众所周知的,它们的动机是因为它们说您可以“快速”将某些东西变成某种东西:这里所说的速度表示简短记录。 它也可以立即将false写入!1吗? 如果开发人员非常担心可打印字符,那么在他最喜欢的IDE中,您可以轻松配置必要的实时模板或自动完成。 而且,如果-对于已发布代码的大小,那么我们总是通过混淆器来运行它,谁比我们更了解如何对所有这些代码进行非人性化处理。 为什么不呢?


 let bool = Boolean(expr); let numb = Number(expr); let str = String(expr); 

结果是相同的,只有所有人都知道。


对于字符串转换,我们有toString ,但是对于数字转换,有一个有趣的valueOf ,它也可以被覆盖。 一个经典示例,将“未启动”引入到木僵器中:


 let timestamp = +new Date; 

但是Date确实有一个已知的getTime方法,让我们使用它:


 let timestamp = (new Date()).getTime(); 

或现成的功能:


 let timestamp = Date.now(); 

绝对不需要利用隐式类型转换。


逻辑运算符


要特别注意逻辑运算符AND(&&&)和OR(||),它们在JavaScript中不是很逻辑:它们接受并返回任何类型的值。 我们不会详细讨论逻辑表达式计算器的操作;我们将考虑示例。 先前提供的带有功能的选项:


 function ping(host, count) { count = count || 5; /* ... */ } 

它可能看起来像这样:


 function ping(host, count) { // OR arguments.length? if (typeof count == 'undefined') { count = 5; } /* ... */ } 

这种验证更加熟悉,并且在某些情况下可以帮助避免错误。


而是,对于最初选择JavaScript路径的开发人员来说,这似乎很野蛮。 但是对于大多数其他人来说,这段代码确实很疯狂:


 var root = (typeof self == 'object' && self.self === self && self) || (typeof global == 'object' && global.global === global && global); 

是的,它很紧凑,是的,流行的图书馆可以负担得起。 但是,请不要滥用它,因为我们的代码不会被JavaScript的贡献者读取,而是由能够在一定时间内解决业务问题的开发人员读取。


这种模式可能完全发生:


 let count = typeof opts == 'object' && opts.count || 5; 

这绝对比通常的三元运算符短,但是在读取此类代码时,您记住的第一件事是所使用操作的优先级。


如果我们编写一个谓词函数,并将其传递给相同的Array.prototype.filter ,则将返回值包装在Boolean中是一个很好的基调。 此功能的目的立即变得显而易见,并且在其语言具有“正确的”逻辑运算符的开发人员之间没有任何不协调。


按位运算


一个使用按位NOT(NOT)检查数组中的元素或字符串中的子字符串是否存在的常见示例,甚至某些教程也提供了该示例:


 if (~[1, 2, 3].indexOf(1)) { console.log('yes'); } 

这能解决什么问题? 我们不必检查!== -1 ,因为indexOf将获取元素的索引或-1,而代字号将添加1并更改符号。 因此,在索引为-1的情况下,该表达式将变为“ false”。


但是可以通过另一种方式避免重复代码:就像每个人一样,将检查放入某些utils-object的单独函数中,而不是将按位运算用于其他目的。 在lodash中有一个include函数可以解决这个问题, 搞砸了 波浪号。 您可以高兴,因为在ECMAScript 2016中, Array.prototype.includes方法已得到修复 (行也有一个)。


但是就在这里! 另一个波浪号(以及XOR)用于舍入数字,舍去小数部分:


 console.log(~~3.14); // 3 console.log(2.72^0); // 2 

但是出于这些目的有parseIntMath.floor 。 此处的按位运算对于在控制台中快速键入代码非常方便,因为它们的优先级比其他算法低。 但是,在进行代码审查时,最好不要错过它。


语法和语言构造


一些奇怪的做法很难归因于任何特定部分。 例如,他们说括号在调用构造函数时是可选的,并且以下两个表达式相同:


 let rabbit = new Rabbit(); let rabbit = new Rabbit; 

确实是! 但是为什么要从头开始提出问题? 并非每种语言都可以拥有这种“功能”。 而且,如果您仍然愿意,那么请就整个项目达成协议。 否则,就会有一种虚假的感觉,即存在一些差异。


声明一组变量的情况与此类似。 varlet指令的语法允许您一次声明(和定义)多个变量,以逗号分隔:


 let count = 5, host, retry = true; 

有人使用换行符来提高可读性,但是无论如何,这种语法在流行语言中并不常见。 没有人会伸出援手,问你是否这样写:


 let count = 5; let retry = true; let host; 

同样,如果在项目/公司级别就良好风格达成协议,那么就没有问题了。 只是您不必为自己的情绪而结合太多语法。


该语言有一些特定的构造,例如IIFE,它使您可以在其定义位置立即调用一个函数。 诀窍是让解析器识别函数表达式,而不是函数声明。 这可以通过许多不同的方式来完成:经典地通过void或任何其他一元运算符将其包装在括号中。 并没有什么奇妙的事情! 有必要选择唯一的选项,并且不要不需离开它:


 (function() { /* ... */ }()); 

无需使用运算符来破解解析器。 当一个新手来到该项目时,我想将其沉浸在应用程序的业务逻辑中,而不是向其提供间谍发现所有这些感叹号和空白的解释。 还有第二个经典括号内的条目, Crockford对此主题发表了有趣的评论


ES6中类语法的出现并没有常见的访问修饰符。 有时,开发人员想在课堂上撒尿并观察隐私。 这导致科学怪人代码:


 class Person { constructor(name) { let _name = name; this.getName = function() { return _name; } } toString() { return `Hello, ${this.getName()}`; } } 

也就是说,访问器是在构造函数中为该实例创建的,而隐私是通过它们通过闭包访问局部属性变量来实现的。 该示例看起来甚至与Lacconcino相当,但这是一种完全不可扩展的方法,除非您围绕它构建了文档化的框架解决方案。 先生们,让我们使用可用的类(并等待私有字段的标准化)或流行的模式模块。 在这里创建某种中间混合解决方案对您来说是一件容易的事,因为类不再是类,并且代码是可理解的。


总结一下,他将与项目中采用的样式指南,lint的配置或与为项目贡献其非JavaScript组件的同事简单地编写代码片段,分享他的常识。 该语言为每个典型任务提供了几种选择,因此增进彼此之间的理解并陷入共同点并不困难(或几乎没有困难)。


冒险


这个主题肯定是整体的,并且有更多示例,但是本文的主要信息是,您不应滥用JavaScript中的非显而易见性,而这可以避免这种情况。 语言的本质是独特的:它使您既可以编写优雅又富于表现力的(适度的“针对性”)解决方案,并且每个人都可以理解和访问。 我从根本上不同意JavaScript会“惩罚自己”或“被埋在一堆好的意图和错误中”的传统观点。 因为现在大多数的陌生性不是由语言证明的,而是由开发人员的文化和(而不是)对语言的冷漠形成的。

Source: https://habr.com/ru/post/zh-CN432102/


All Articles