[Перевод] Чистый javascript.Объекты и структуры данных. Асинхронность. Обработка ошибок

Перевод книги Райана Макдермота clean-code-javascript

Оглавление:


  • Введение
  • Переменные
  • Функции
  • Классы
  • Объекты и структуры данных. Асинхронность. Обработка ошибок.

6bc4524160284f4290794957ff2430bd.jpg


Объекты и структуры данных


Используйте геттеры и сеттеры


В javascript отсутствуют ключевые слова private и public, что усложняет реализацию классов. Лучше использовать геттеры и сеттеры для доступа к свойствам объекта, чем напрямую к ним обращаться. Вы спросите «Зачем?». Вот несколько причин:
  • Если вы хотите реализовать дольше чем просто доступ к свойству, вам надо будет поменять реализацию в одном месте, а не по всему коду.
  • Валидацию легко реализовать на уровне реализации сеттера
  • Инкапсуляция внутреннего состояния объекта
  • Легко добавить логирование и обработку ошибок на уровне геттеров и сеттеров
  • Наследуя этот класс, вы можете переопределить функциональность по умолчанию
  • Вы можете лениво подгружать свойства вашего объекта, например, с сервера.

Плохо:
class BankAccount {
  constructor() {
    this.balance = 1000;
  }
}

const bankAccount = new BankAccount();

// Покупаем, например, обувь...
bankAccount.balance -= 100;

Хорошо:
class BankAccount {
  constructor(balance = 1000) {
    this._balance = balance;
  }

  set balance(amount) {
    if (verifyIfAmountCanBeSetted(amount)) {
      this._balance = amount;
    }
  }

  get balance() {
    return this._balance;
  }

  verifyIfAmountCanBeSetted(val) {
    // ...
  }
}

const bankAccount = new BankAccount();

// Покупаем, например, обувь...
bankAccount.balance -= shoesPrice;

// получаем баланс
let balance = bankAccount.balance;

Реализуйте приватные свойства ваших объектов


Это возможно с помощью замыканий.
Плохо:
const Employee = function(name) {
  this.name = name;
};

Employee.prototype.getName = function getName() {
  return this.name;
};

const employee = new Employee('John Doe');
console.log(`Employee name: ${employee.getName()}`); 
// Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`); 
// Employee name: undefined

Хорошо:
const Employee = function (name) {
  this.getName = function getName() {
    return name;
  };
};

const employee = new Employee('John Doe');
console.log(`Employee name: ${employee.getName()}`); 
// Employee name: John Doe
delete employee.name;
console.log(`Employee name: ${employee.getName()}`);
 // Employee name: John Doe


Асинхронность


Используйте промисы вместо колбеков


Колбеки приводят к чрезмерной вложенности и плохой читаемости кода.
Плохо:
require('request').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', (requestErr, response) => {
  if (requestErr) {
    console.error(requestErr);
  } else {
    require('fs').writeFile('article.html', response.body, (writeErr) => {
      if (writeErr) {
        console.error(writeErr);
      } else {
        console.log('File written');
      }
    });
  }
});

Хорошо:
require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then((response) => {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(() => {
    console.log('File written');
  })
  .catch((err) => {
    console.error(err);
  });

Async/Await делает код чище, чем промисы


Промисы очень хорошая альтернатива колбекам, но в ES2017 / ES8 спецификации появился аsync/аwait, который предлагает ещё лучше решение. Все, что вам нужно, это написать функцию с префиксом async, внутри которой вы можете писать вашу асинхронную логику императивно. аsync/аwait можно использовать прямо сейчас при помощи babel.
Плохо:
require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin')
  .then((response) => {
    return require('fs-promise').writeFile('article.html', response);
  })
  .then(() => {
    console.log('File written');
  })
  .catch((err) => {
    console.error(err);
  });

Хорошо:
async function getCleanCodeArticle() {
  try {
    const response = await require('request-promise').get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');
    await require('fs-promise').writeFile('article.html', response);
    console.log('File written');
  } catch(err) {
    console.error(err);
  }
}


Обработка ошибок


Бросать ошибки хорошее решение! Это означает, что во время выполнения вы будете знать если что-то пошло не так. Вы сможете остановить выполнение вашего приложение в нужный момент и видеть место ошибки с помощью стек трейса в консоли

Не игнорируйте отловленные ошибки


Ничего не делая с пойманной ошибкой вы теряете возможность исправить ошибку или отреагировать на неё когда-либо. Вывод ошибки в консоль (console.log (error)) не дает лучшего результата, потому-что ошибка может потеряться среди выводимых записей в консоль. Если вы заворачиваете кусок кода в try / catch, значит вы предполагаете возникновение ошибки. В таком случае вы должны иметь запасной план на этот случай.
Плохо:
try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

Хорошо:
try {
  functionThatMightThrow();
} catch (error) {
  // Один из вариантов (более заметный, чем console.log):
  console.error(error);
  // Другой вариант, известить пользователя про ошибку:
  notifyUserOfError(error);
  // И еще вариант, отправить ошибку на сервер :
  reportErrorToService(error);
  // Или используйте  все три варианта!
}

Не игнорируйте ошибки возникшие в промисах


Вы не должны игнорировать ошибки возникшие в промисе по той же причине что отловленные ошибки в try / catch.
Плохо:
getdata()
.then((data) => {
  functionThatMightThrow(data);
})
.catch((error) => {
  console.log(error);
});

Хорошо:
getdata()
.then((data) => {
  functionThatMightThrow(data);
})
.catch((error) => {
  // Один из вариантов (более заметный, чем console.log):
  console.error(error);
  // Другой вариант, известить пользователя про ошибку:
  notifyUserOfError(error);
  // И еще вариант, отправить ошибку на сервер :
  reportErrorToService(error);
  // Или используйте  все три варианта!
});

Комментарии (0)

© Habrahabr.ru