Найкращі практики чистого коду на JS

10 хв. читання

Інтро

Навіть поганий код може працювати. Однак, якщо код буде «брудним», це завжди буде заважати розвитку проекту і компанії в цілому.

Robert C. Martin (Uncle Bob)

Якщо вас турбує не лише робота вашого коду, але також якість його написання – цей матеріал для вас. Професіональний розробник писатиме код так, щоб в майбутньому його міг зрозуміти не лише комп'ютер. Варто пам'ятати про це, щоб не викликати істерику у розробника, який читатиме код після вас. Сподіваємось, що цим "розробником з майбутнього" не будете ви...

Тож чистий код можна визначити як такий, що не потребує пояснень, легко сприймається та не менш легко модифікується чи розширюється.

Згадайте коли останній раз при ознайомленні з чужим кодом ви рахували кількість "WTF" на хвилину. Як можна помітити: чистий код – одвічна тема для суперечок серед розробників:

Найкращі практики чистого коду на JS

У статті ми приділимо увагу принципам організації чистого коду саме у JS, але більшість з наведених концепцій можуть застосовуватись також для інших мов програмування.

Кращі практики чистого коду

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.width || 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)) {
  // реалізація
}

Використовуйте скорочені умовні вирази. Хоч це може здатися елементарним, однак наголосити варто. Використовуйте такий підхід для булевих значень або якщо ви впевнені, що результат не буде дорівнювати undefined чи null.

Погано:

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. Класи EcmaScript

Класи — новий синтаксичний цукор в JavaScript. «Під капотом» залишається добре відоме прототипне наслідування, відрізняється лише вигляд коду. Використовуйте саме такі нововведення, а не прості функції ES5.

Погано:

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. Загальні поради

Отже, ви повинні робити все можливе аби не дублювати код та не залишати «мертвий код».

Дублювання коду відбувається з декількох причин. Наприклад, у вас є два подібні завдання, які трохи відрізняються, тому ви змушені створювати для них дві різні функції, що мають дуже схожий код. В такому контексті «позбавитись від повторюваного коду» означатиме абстрагування від відмінностей та їх обробка на іншому рівні.

Інша проблема – "мертвий код", який приховався серед вашої кодової бази, проте наразі не виконує жодного функціоналу, тому що у певний момент ви вирішили, що в ньому більше не має потреби. Вам треба відшукати такі фрагменти коду та видалити їх. Слід позбавлятися коду одразу, як ви вирішили, що він непотрібний, інакше пізніше ви просто забудете його першочергове призначення та опинитесь в такій ситуації:

Найкращі практики чистого коду на JS
Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація