编写干净的JavaScript代码的准则

如果您关心代码本身以及代码的编写方式,并且不仅关心创建工作程序,那么这意味着您希望代码干净。 专业的开发人员不仅为计算机编写代码,而且还为将来遇到该代码的本人以及其他程序员编写代码。 您编写的代码不会永远消失在计算机的肠道中。 它可以生存,变化,并且如果编写得不好,可能会令您不得不在编辑后对其进行编辑的人感到不安。 您很可能会成为这个“人”。



基于这些想法,可以将干净的代码定义为以自我解释的方式编写的代码。 人们可以轻松理解此代码,对其进行修改或扩展也很容易。

代码和WTF问题


WTF问题的实质,例如“那是WTF?”归结为极大的惊讶和不满。 用俄语,这些感觉可以用“到底是什么?”这个问题来表达。 根据情况,“该死”很可能会被完全无法打印的东西所取代。 您有几次添加某人的密码并提出类似的问题?

当问自己WTF有关其他人的代码的问题时,程序员会问自己是什么(WTF是?),代码作者试图做什么(WTF在这里做了吗?),为什么在代码中存在这个或那个结构(WTF是?这个是为了?)

这是一张图片,根据该图片,唯一可靠的代码质量指标是每分钟WTF问题的数量。


左边是好的代码。 右边是坏的

但认真地说,为了帮助您调整干净代码的思路,我们引用了罗伯特·马丁(Robert Martin)(称为Bob叔叔):“即使不良的程序代码也可以工作。 但是,如果代码不是“干净的”,它将始终干扰项目的开发。”

现在,让我们看看一些编写干净代码的实用准则。 我们将在这里使用JavaScript,但是这些建议可以应用于其他语言的开发中。

1.严格的平等检查


尝试使用===而不是==

 //     == -       .     ,   ,   ,   -    . 0 == false // true 0 === false // false 2 == "2" // true 2 === "2" // false //  const value = "500"; if (value === 500) { console.log(value); //     } if (value === "500") { console.log(value); //    } 

2.变量


命名变量,以便它们的名称可以揭示其本质,以及它们在程序中的作用。 使用这种方法,在代码中搜索它们会很方便,并且任何看到此代码的人都可以更轻松地了解他们执行的动作的含义。
不好:

 let daysSLV = 10; let y = new Date().getFullYear(); let ok; if (user.age > 30) { ok = true; } 

好:

 const MAX_AGE = 30; let daysSinceLastVisit = 10; let currentYear = new Date().getFullYear(); ... const isUserOlderThanAllowed = user.age > MAX_AGE; 

无需在不需要的变量名中添加其他单词。

不好:

 let nameValue; let theProduct; 

好:

 let name; let product; 

您不应强迫阅读代码的人记住变量在其中声明的环境。

不好:

 const users = ["John", "Marco", "Peter"]; users.forEach(u => { doSomething(); doSomethingElse(); // ... // ... // ... // ... //    ,    WTF- "   `u`?" register(u); }); 

好:

 const users = ["John", "Marco", "Peter"]; users.forEach(user => { doSomething(); doSomethingElse(); // ... // ... // ... // ... register(user); }); 

您不需要为变量名提供有关使用它们的上下文的冗余信息。

不好:

 const user = { userName: "John", userSurname: "Doe", userAge: "28" }; ... user.userName; 

好:

 const user = { name: "John", surname: "Doe", age: "28" }; ... user.name; 

3.功能


对函数使用长的描述性名称。 鉴于功能是对特定动作的描述,其名称应为能完整描述功能本质的动词或短语。 必须选择参数的名称,以便它们充分描述它们表示的数据。 函数名称应告诉代码读取器这些函数的确切作用。

不好:

 function notif(user) { //  } 

好:

 function notifyUser(emailAddress) { //  } 

避免使用一长串参数。 理想情况下,函数应具有两个或更少的参数。 函数的参数越少,对其进行测试就越容易。

不好:

 function getUsers(fields, fromDate, toDate) { //  } 

好:

 function getUsers({ fields, fromDate, toDate }) { //  } getUsers({ fields: ['name', 'surname', 'email'], fromDate: '2019-01-01', toDate: '2019-01-18' }) 

使用默认参数,使它们优先于条件构造。

不好:

 function createShape(type) { const shapeType = type || "cube"; // ... } 

好:

 function createShape(type = "cube") { // ... } 

一个功能应该解决一个问题。 努力确保一个功能不会执行很多动作。

不好:

 function notifyUsers(users) { users.forEach(user => {   const userRecord = database.lookup(user);   if (userRecord.isVerified()) {     notify(user);   } }); } 

好:

 function notifyVerifiedUsers(users) { users.filter(isUserVerified).forEach(notify); } function isUserVerified(user) { const userRecord = database.lookup(user); return userRecord.isVerified(); } 

默认情况下,使用Object.assign设置对象的属性。

不好:

 const shapeConfig = { type: "cube", width: 200, height: null }; function createShape(config) { config.type = config.type || "cube"; config.width = config.width || 250; config.height = config. height || 250; } createShape(shapeConfig); 

好:

 const shapeConfig = { type: "cube", width: 200 //   'height'   }; function createShape(config) { config = Object.assign(   {     type: "cube",     width: 250,     height: 250   },   config ); ... } createShape(shapeConfig); 

不要将标志用作参数。 它们的使用意味着该功能执行的动作比其应执行的动作更多。

不好:

 function createFile(name, isPublic) { if (isPublic) {   fs.create(`./public/${name}`); } else {   fs.create(name); } } 

好:

 function createFile(name) { fs.create(name); } function createPublicFile(name) { createFile(`./public/${name}`); } 

不要污染全局范围。 如果需要扩展现有对象,请使用ES类和继承机制,而不要在标准对象的原型链中创建函数。

不好:

 Array.prototype.myFunc = function myFunc() { //  }; 

好:

 class SuperArray extends Array { myFunc() {   //  } } 

4.条件构造


尽量不要命名布尔变量,以使它们的名称取反。 返回布尔值的函数也是如此。 在条件构造中使用这样的实体会使阅读代码变得困难。

不好:

 function isUserNotBlocked(user) { //  } if (!isUserNotBlocked(user)) { //  } 

好:

 function isUserBlocked(user) { //  } if (isUserBlocked(user)) { //  } 

使用缩写形式进行条件构造。 这项建议看似微不足道,但值得一提。 仅在确定逻辑变量的值不会为undefinednull ,才可以使用此方法。

不好:

 if (isValid === true) { // - ... } if (isValid === false) { // - ... } 

好:

 if (isValid) { // - ... } if (!isValid) { // - ... } 

尽可能避免逻辑构造。 请改用多态和继承。

不好:

 class Car { // ... getMaximumSpeed() {   switch (this.type) {     case "Ford":       return this.someFactor() + this.anotherFactor();     case "Mazda":       return this.someFactor();     case "McLaren":       return this.someFactor() - this.anotherFactor();   } } } 

好:

 class Car { // ... } class Ford extends Car { // ... getMaximumSpeed() {   return this.someFactor() + this.anotherFactor(); } } class Mazda extends Car { // ... getMaximumSpeed() {   return this.someFactor(); } } class McLaren extends Car { // ... getMaximumSpeed() {   return this.someFactor() - this.anotherFactor(); } } 

5. ES班


在JavaScript中出现的类相对较新。 它们可以称为语法糖。 和以前一样,使用类时发生的事情基于对象的原型。 但是使用这些类的代码看起来有所不同。 通常,如果可能的话,应优先使用ES类而不是常规的构造函数。

不好:

 const Person = function(name) { if (!(this instanceof Person)) {   throw new Error("Instantiate Person with `new` keyword"); } this.name = name; }; Person.prototype.sayHello = function sayHello() { /**/ }; const Student = function(name, school) { if (!(this instanceof Student)) {   throw new Error("Instantiate Student with `new` keyword"); } Person.call(this, name); this.school = school; }; Student.prototype = Object.create(Person.prototype); Student.prototype.constructor = Student; Student.prototype.printSchoolName = function printSchoolName() { /**/ }; 

好:

 class Person { constructor(name) {   this.name = name; } sayHello() {   /* ... */ } } class Student extends Person { constructor(name, school) {   super(name);   this.school = school; } printSchoolName() {   /* ... */ } } 

组织方法,以便可以将它们链接起来。 许多库都使用这种模式,例如jQuery和Lodash。 结果,您的代码将比不使用此模式的代码更紧凑。 关键是在类的每个函数的末尾,您需要返回this 。 这将使您可以将这些函数的调用组合成链。

不好:

 class Person { constructor(name) {   this.name = name; } setSurname(surname) {   this.surname = surname; } setAge(age) {   this.age = age; } save() {   console.log(this.name, this.surname, this.age); } } const person = new Person("John"); person.setSurname("Doe"); person.setAge(29); person.save(); 

好:

 class Person { constructor(name) {   this.name = name; } setSurname(surname) {   this.surname = surname;   //  this           return this; } setAge(age) {   this.age = age;   //  this           return this; } save() {   console.log(this.name, this.surname, this.age);   //  this           return this; } } const person = new Person("John")   .setSurname("Doe")   .setAge(29)   .save(); 

6.最好不要做什么


希望自己的代码干净的任何人都应尽量避免重复。 这样做的目的是避免必须编写相同代码的情况。 此外,您不需要保留从未在代码库中执行的未使用函数和程序片段。

由于各种原因,可能会出现重复的代码。 例如,一个项目可能具有一对稍有不同的功能。 这些差异或缺乏时间的性质迫使程序员例如创建包含几乎相同代码的两个独立功能。 在这种情况下,消除重复的代码就是抽象出差异并在更高层次上使用它们。

现在让我们谈谈未使用的代码。 这是代码库中存在的代码,但绝对不起作用。 例如,当在开发的某个特定阶段确定该程序的某个片段中不再有任何意义时,就会发生这种情况。 为了摆脱这些代码片段,您需要仔细检查代码库并删除它们。 在决定不再需要此类代码时,最容易摆脱此类代码。 以后,您可以忘记它的用途。 与他的战斗将大大复杂化。

如果推迟对抗不必要的代码,该程序将类似于下图所示。


有时我的代码看起来像这个阳台。 我不知道他要解决什么任务,但是我怕摆脱他

总结


在这里,我们仅讨论了可以用来改进代码的一小部分动作。 该材料的作者认为,这里讨论的原理经常被遗忘。 程序员试图跟随他们,但是由于种种原因,并不总是成功。 也许,在项目开始之初,每个人都记得干净代码的重要性,因此,程序很简洁。 然后,随着最后期限的临近,通常会忘记代码的纯度,只注意标记为TODO或REFACTOR的内容。 在这种时候,项目客户将坚持按时完成项目,而不是保证其代码干净。

我们经常发布有关编写高质量JavaScript代码的材料。 如果您对此主题感兴趣,这里有一些链接。


我们希望您从阅读本文中学到的知识以及在其他出版物中获得的知识将对您编写干净的JavaScript代码有帮助。

亲爱的读者们! 您曾经在阅读别人的代码时问过WTF问题吗?



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


All Articles