Pull to refresh

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

JavaScript *Node.JS *Angular *ReactJS *
Translation
Original author: Ryan McDermott
Перевод книги Райана Макдермота clean-code-javascript

Оглавление:






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


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


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

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

Плохо:

class BankAccount {
  constructor() {
    this.balance = 1000;
  }
}

const bankAccount = new BankAccount();

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

Хорошо:

class BankAccount {
  constructor(balance = 1000) {
    this._balance = balance;
  }

  // It doesn't have to be prefixed with `get` or `set` to be a getter/setter
  set balance(amount) {
    if (this.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


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


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


Колбеки приводят к чрезмерной вложенности и плохой читаемости кода.

Плохо:

const request = require('request');
const fs = require('fs');

const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';

request.get(url, (requestErr, response) => {
  if (requestErr) {
    console.error(requestErr);
  } else {
    fs.writeFile('article.html', response.body, (writeErr) => {
      if (writeErr) {
        console.error(writeErr);
      } else {
        console.log('File written');
      }
    });
  }
});

Хорошо:

const requestPromise = require('request-promise');
const fsPromise = require('fs-promise');

const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';

requestPromise.get(url)
  .then((response) => {
    return fsPromise.writeFile('article.html', response);
  })
  .then(() => {
    console.log('File written');
  })
  .catch((err) => {
    console.error(err);
  });

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


Промисы очень хорошая альтернатива колбекам, но в ES2017 / ES8 спецификации появился аsync/аwait, который предлагает ещё лучшее решение. Все, что вам нужно, это написать функцию с префиксом async, внутри которой вы можете писать вашу асинхронную логику императивно. аsync/аwait можно использовать прямо сейчас при помощи babel.

Плохо:

const requestPromise = require('request-promise');
const fsPromise = require('fs-promise');

const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';

requestPromise.get(url)
  .then((response) => {
    return fsPromise.writeFile('article.html', response);
  })
  .then(() => {
    console.log('File written');
  })
  .catch((err) => {
    console.error(err);
  });

Хорошо:

const requestPromise = require('request-promise');
const fsPromise = require('fs-promise');

async function getCleanCodeArticle() {
  try {
    const url = 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin';
    const response = await requestPromise.get(url);
    await fsPromise.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);
  // Или используйте все три варианта!
});
Tags:
Hubs:
Total votes 22: ↑18 and ↓4 +14
Views 22K
Comments Comments 10