Diretrizes para escrever código JavaScript limpo

Se você se importa com o código em si, como ele é gravado e não está apenas ocupado criando programas de trabalho, isso significa que você deseja que seu código esteja limpo. Um desenvolvedor profissional grava o código não apenas para computadores, mas também para si mesmo que conheceu esse código no futuro e para outros programadores. O código que você escreve não desaparece para sempre nas entranhas do computador. Ele vive, muda e, se for mal escrito, pode incomodar muito alguém que precisará editá-lo depois que você o escrever. É possível que você seja esse "alguém".



Com base nessas idéias, o código limpo pode ser definido como código escrito de forma que ele se explique. Este código pode ser facilmente entendido pelas pessoas, será fácil modificá-lo ou estendê-lo.

Perguntas sobre código e WTF


A essência das perguntas da WTF, como "WTF é isso?", Resume-se a extrema surpresa e ressentimento. Em russo, esses sentimentos podem ser expressos pela pergunta "Que diabos?". "Droga", dependendo da situação, pode muito bem dar lugar a algo completamente imprimível. Quantas vezes você teve que adicionar o código de alguém e fazer perguntas semelhantes?

Ao se fazerem perguntas WTF sobre o código de outra pessoa, os programadores se perguntam o que é (WTF é isso?), O que o autor do código tentou fazer (WTF você fez aqui?). Por que esta ou aquela construção está presente no código (WTF é isso para?)

Aqui está uma figura segundo a qual o único indicador confiável da qualidade do código é o número de perguntas do WTF por minuto.


À esquerda está um bom código. Para a direita é ruim

Sério, para ajudar você a pensar em código limpo, vamos citar Robert Martin, conhecido como tio Bob: “Mesmo códigos de programas ruins podem funcionar. No entanto, se o código não for "limpo", ele sempre interferirá no desenvolvimento do projeto. "

Agora, vejamos algumas diretrizes práticas para escrever código limpo. Usaremos o JavaScript aqui, mas essas recomendações podem ser aplicadas ao desenvolvimento em outros idiomas.

1. Verificações estritas da igualdade


Tente usar === vez de == .

 //     == -       .     ,   ,   ,   -    . 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. Variáveis


Nomeie as variáveis ​​para que seus nomes revelem sua essência e seu papel no programa. Com essa abordagem, será conveniente procurá-los no código, e quem vê esse código pode entender mais facilmente o significado das ações que executa.
Ruim:

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

Bom:

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

Não há necessidade de adicionar palavras adicionais aos nomes de variáveis ​​que não são necessários.

Ruim:

 let nameValue; let theProduct; 

Bom:

 let name; let product; 

Você não deve forçar alguém que lê o código a lembrar do ambiente em que a variável é declarada.

Ruim:

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

Bom:

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

Você não precisa fornecer nomes de variáveis ​​com informações redundantes sobre o contexto em que elas são usadas.

Ruim:

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

Bom:

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

3. Funções


Use nomes descritivos longos para funções. Dado que uma função é uma descrição de uma determinada ação, seu nome deve ser um verbo ou frase que descreva completamente a essência da função. Os nomes dos argumentos devem ser selecionados para que descrevam adequadamente os dados que representam. Os nomes das funções devem informar ao leitor de código o que exatamente essas funções fazem.

Ruim:

 function notif(user) { //  } 

Bom:

 function notifyUser(emailAddress) { //  } 

Evite usar longas listas de argumentos. Idealmente, as funções devem ter dois ou menos argumentos. Quanto menos argumentos uma função tiver, mais fácil será testá-la.

Ruim:

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

Bom:

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

Use os argumentos padrão, dando-lhes preferência sobre construções condicionais.

Ruim:

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

Bom:

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

Uma função deve resolver um problema. Esforce-se para garantir que uma função não execute muitas ações.

Ruim:

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

Bom:

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

Use Object.assign para definir as propriedades dos objetos por padrão.

Ruim:

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

Bom:

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

Não use sinalizadores como parâmetros. Seu uso significa que a função executa mais ações do que deveria.

Ruim:

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

Bom:

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

Não polua o escopo global. Se você precisar expandir um objeto existente, use classes ES e mecanismos de herança em vez de criar funções na cadeia de protótipos de objetos padrão.

Ruim:

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

Bom:

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

4. Construções condicionais


Tente não nomear as variáveis ​​booleanas para que haja negação em seus nomes. O mesmo vale para funções que retornam valores booleanos. O uso de tais entidades em construções condicionais dificulta a leitura do código.

Ruim:

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

Bom:

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

Use o formato abreviado para construções condicionais. Talvez essa recomendação possa parecer trivial, mas vale a pena mencionar. Use essa abordagem apenas para variáveis ​​lógicas e se tiver certeza de que o valor da variável não será undefined ou null .

Ruim:

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

Bom:

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

Evite construções lógicas sempre que possível. Use polimorfismo e herança.

Ruim:

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

Bom:

 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. aulas de ES


As aulas apareceram em JavaScript relativamente recentemente. Eles podem ser chamados de açúcar sintático. O que acontece ao usar classes é baseado em protótipos de objetos, como antes. Mas o código que usa as classes parece diferente. Em geral, se possível, as classes ES devem ser preferidas às funções regulares do construtor.

Ruim:

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

Bom:

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

Organize métodos para que eles possam ser encadeados. Muitas bibliotecas usam esse padrão, como jQuery e Lodash. Como resultado, seu código será mais compacto do que sem usar esse padrão. O ponto é que, no final de cada função da classe, você precisa retornar this . Isso permitirá combinar chamadas de tais funções em cadeias.

Ruim:

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

Bom:

 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. O que é melhor não fazer


Qualquer pessoa que queira que seu código seja limpo deve tentar não repeti-lo. O objetivo é evitar situações em que você precise escrever o mesmo código. Além disso, você não precisa deixar funções não utilizadas e fragmentos de programa que nunca são executados na base de código.

Código duplicado pode aparecer por vários motivos. Por exemplo, um projeto pode ter um par de funções ligeiramente diferentes. A natureza dessas diferenças ou falta de tempo força o programador, por exemplo, a criar duas funções independentes contendo código quase idêntico. Em tal situação, livrar-se do código duplicado é abstrair as diferenças e trabalhar com elas em um nível superior.

Agora vamos falar sobre código não utilizado. Este é o código que está presente na base de código, mas não faz absolutamente nada. Isso acontece, por exemplo, quando, em um certo estágio de desenvolvimento, é decidido que não há mais sentido em um determinado fragmento do programa. Para se livrar desses fragmentos de código, você precisa revisar cuidadosamente a base de código e removê-los. É mais fácil se livrar desse código no momento em que se decide que ele não é mais necessário. Mais tarde, você pode esquecer para que foi usado. Isso vai complicar bastante a luta com ele.

Se você adiar a luta contra códigos desnecessários, o programa será semelhante ao mostrado na figura a seguir.


Às vezes, meu código se parece com essa varanda. Não sei que tarefa ele está resolvendo, mas tenho medo de me livrar dele.

Sumário


Aqui discutimos apenas uma pequena parte das ações que podem ser tomadas para melhorar o código. O autor deste material acredita que os princípios discutidos aqui são frequentemente esquecidos. Os programadores tentam segui-los, mas, por várias razões, nem sempre conseguem isso. Talvez, no início do projeto, todos se lembrem da importância do código limpo, como resultado, o programa seja organizado. Então, à medida que os prazos se aproximam, eles geralmente esquecem a pureza do código, prestando atenção apenas ao que está marcado como TODO ou REFACTOR. Nesses momentos, o cliente do projeto insistirá para que o projeto seja concluído no prazo, e não para que seu código esteja limpo.

Com bastante frequência, publicamos materiais sobre o problema de escrever código JavaScript de alta qualidade. Se você está interessado neste tópico, aqui estão alguns links.


Esperamos que o que você aprendeu lendo este artigo e o que você pode encontrar em outras publicações o ajude em sua busca para escrever um código JavaScript limpo.

Caros leitores! Você já fez perguntas ao WTF ao ler o código de outra pessoa?



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


All Articles