📜 ⬆️ ⬇️

Recommendations for writing clean code in JavaScript

If you care about the code itself, and how it is written, and not only engaged in creating working programs, it means that you want your code to be clean. A professional developer writes code not only for computers, but also for himself, who has met this code in the future, and for other programmers. The code you write does not disappear forever in the depths of the computer. It lives, changes, and, if written poorly, it may well upset someone who will have to edit it after you have written it. It is possible that you will be this “someone”.



Based on these ideas, pure code can be defined as code written in such a way that it explains itself. This code can easily be understood by people, it will be easy to modify or extend it.

Code and WTF Questions


The essence of WTF questions, like “WTF is that?”, Comes down to utter surprise and indignation. In Russian, these feelings can be expressed by the question "What the hell?". “Damn,” depending on the situation, may well give way to something completely unprintable. How many times have you had to add someone's code and at the same time ask such questions?
')
When asking themselves WTF questions about someone else's code, programmers ask themselves what it is (WTF is that?), What did the author of the code try to do (WTF did you do here?), Why does this or that construct (WTF is this for?)

Here is a picture, according to which the only reliable indicator of the quality of a code is the number of WTF questions per minute.


On the left is a good code. Right - bad

But seriously, in order to help you tune in to thinking about clean code, we quote Robert Martin, known as Uncle Bob: “Even a bad program code can work. However, if the code is not “clean”, it will always hinder the development of the project. ”

Now consider some practical recommendations for writing clean code. We will use JavaScript here, but these recommendations can be applied to development in other languages.

1. Strict equality tests


Aim to use === instead of == .

 //     == -       .     ,   ,   ,   -    . 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. Variables


Name the variables so that their names would reveal their essence, their role in the program. With this approach, it will be convenient to look for them in the code, and the one who sees this code will be able to more easily understand the meaning of the actions he performs.
Poorly:

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

Good:

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

No need to add additional words to variable names that are not necessary.

Poorly:

 let nameValue; let theProduct; 

Good:

 let name; let product; 

It is not necessary to force the one who reads the code so that he would have to remember the environment in which the variable is declared.

Poorly:

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

Good:

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

You do not need to supply variable names with redundant information about the context in which they are used.

Poorly:

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

Good:

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

3. Functions


Use long descriptive names for functions. Considering that a function is a description of the performance of an action, its name must be a verb or phrase that fully describes the essence of the function. Argument names must be selected so that they adequately describe the data they represent. Function names must tell the reader what the functions are doing.

Poorly:

 function notif(user) { //  } 

Good:

 function notifyUser(emailAddress) { //  } 

Avoid using long argument lists. Ideally, functions should have two or fewer arguments. The fewer the arguments of the function, the easier it will be to test it.

Poorly:

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

Good:

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

Use default arguments, giving them preference over conditional constructs.

Poorly:

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

Good:

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

The function should solve one problem. Aim for one function to not perform multiple actions.

Poorly:

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

Good:

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

Use Object.assign to set default object properties.

Poorly:

 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); 

Good:

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

Do not use flags as parameters. Using them means that the function performs more actions than it should.

Poorly:

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

Good:

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

Do not pollute the global scope. If you need to extend an existing object, use ES classes and inheritance mechanisms instead of creating functions in the prototype chain of standard objects.

Poorly:

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

Good:

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

4. Conditional constructions


Try not to name logical variables in such a way that there is a negation in their names. The same applies to functions that return logical values. The use of such entities in conditional constructions makes it difficult to read the code.

Poorly:

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

Good:

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

Use the abbreviated form of recording conditional structures. Perhaps this recommendation may seem trivial, but this is worth mentioning. Use this approach only for logical variables, and if you are sure that the value of the variable will not be undefined or null .

Poorly:

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

Good:

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

Avoid logical constructs wherever possible. Use polymorphism and inheritance instead.

Poorly:

 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();   } } } 

Good:

 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 classes


Classes appeared in JavaScript relatively recently. They can be called "syntactic sugar." The basis of what is obtained when using classes is, as before, prototypes of objects. But the code in which classes are applied looks different. In general, if there is such an opportunity, ES-classes should be preferred to ordinary constructor functions.

Poorly:

 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() { /**/ }; 

Good:

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

Arrange the methods so that they can be chained. This pattern is used by many libraries, such as jQuery and Lodash. As a result, your code will be more compact than without using this pattern. The point is that at the end of each of the functions of the class you need to return this . This will allow to unite calls of such functions in chains.

Poorly:

 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(); 

Good:

 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. About what is better not to do


Anyone who wants his code to be clean should strive not to repeat. The idea is to avoid situations in which you have to write the same code. In addition, it is not necessary to leave unused functions and program fragments that are never executed in the code base.

Duplicate code may appear for various reasons. For example, in the project there may be a couple of functions slightly different from each other. The nature of these differences, or lack of time, forces a programmer, for example, to create two independent functions containing almost identical code. In this situation, getting rid of duplicate code is to abstract the differences and work with them at a higher level.

Now let's talk about unused code. This is code that is present in the code base, but does absolutely nothing. This happens, for example, when at a certain stage of development it is decided that there is no longer any sense in a certain fragment of the program. In order to get rid of such code fragments, you need to carefully review the code base and remove them. The easiest way to get rid of such a code is when it is decided that it is no longer needed. Later, you can forget about what it was used for. This will greatly complicate the fight with him.

If you put off the fight with unnecessary code, the program will resemble what is shown in the following figure.


Sometimes my code looks like this balcony. I do not know - what problem he solves, but I am afraid to get rid of him

Results


Here we have discussed only a small part of the actions that can be taken to improve the code. The author of this material believes that the principles discussed here are often forgotten. Programmers try to follow them, but, for various reasons, they do not always succeed in this. Perhaps, at the very beginning of the project, everyone remembers the importance of clean code, as a result, the program turns out to be accurate. Then, as deadlines approach, the purity of the code is often forgotten, paying attention only to what is marked as TODO or REFACTOR. At such moments, the project customer will insist that the project be completed on time, and not on its code being clean.

We quite often publish materials on the problem of writing high-quality JavaScript-code. If you are interested in this topic - here are a few links.


We hope that what you have learned by reading this article and what you can find in other publications will help you in your desire to write pure JavaScript code.

Dear readers! Have you ever wondered WTF questions when reading someone else's code?



Source: https://habr.com/ru/post/454520/


All Articles