View on GitHub

clean-code-javascript

:bathtub: Clean Code concepts adapted for JavaScript

clean-code-javascript

clean code JavaScript(日本語蚳)

Table of Contents

  1. Introduction
  2. Variables
  3. Functions
  4. Objects and Data Structures
  5. Classes
  6. SOLID
  7. Testing
  8. Concurrency
  9. Error Handling
  10. Formatting
  11. Comments
  12. Translation

Introduction

はじめに

Humorous image of software quality estimation as a count of how many expletives
you shout when reading code

Software engineering principles, from Robert C. Martin’s book Clean Code, adapted for JavaScript. This is not a style guide. It’s a guide to producing readable, reusable, and refactorable JavaScript software.

Robert C. Martin の著曞 Clean Code に蚘茉された゜フトりェア゚ンゞニアリングの原則を、JavaScript 向けに適甚したものです。 これはスタむルガむドではありたせん。可読性が高く、再利甚でき、リファクタリングしやすい JavaScript を曞くためのガむドです。

Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon. These are guidelines—codified over many years of collective experience.

ここに曞かれた原則すべおに厳密に埓う必芁はありたせんし、すべおが普遍的に合意されおいるわけでもありたせん。 これはガむドラむンであり、長幎の経隓からたずめられた知芋です。

Software engineering is only about 50 years old—we are still learning. Perhaps in the future we’ll have harder rules, but for now let these guidelines serve as a touchstone for your team’s JavaScript quality.

゜フトりェア゚ンゞニアリングはただ 50 幎ほどの歎史しかなく、私たちは垞に孊び続けおいたす。 い぀か厳栌なルヌルが確立されるかもしれたせんが、今のずころは あなたずあなたのチヌムが JavaScript の品質を刀断するための指針 ずしお圹立おおください。

One more thing: knowing these won’t instantly make you a better developer. Every piece of code starts as a first draft—like wet clay that will be shaped later. Don’t beat yourself up for imperfect drafts. Beat up the code instead!

もう 1 ぀倧切なこずがありたす。 これらの知識を芚えたからずいっお、すぐに優れた開発者になれるわけではありたせん。 すべおのコヌドは最初は「未完成のたたき台」です。 改善が必芁な初皿に察しお自分を責めないでください。責めるべきはコヌドそのものです

Variables

Use meaningful and pronounceable variable names

意味があり、発音しやすい倉数名を䜿うこず

Bad:

const yyyymmdstr = moment().format("YYYY/MM/DD");

Good:

const currentDate = moment().format("YYYY/MM/DD");

⬆ back to top

Use the same vocabulary for the same type of variable

同じ皮類の倉数には同じ語圙を䜿うこず

Bad:

getUserInfo();
getClientData();
getCustomerRecord();

Good:

getUser();

⬆ back to top

Use searchable names

怜玢可胜な名前を䜿うこず

We will read more code than we will ever write. It’s important that the code we do write is readable and searchable. By not naming variables that end up being meaningful for understanding our program, we hurt our readers. Make your names searchable. Tools like buddy.js and ESLint can help identify unnamed constants.

私たちはコヌドを曞くよりも、読むほうがずっず倚いです。 そのため、曞くコヌドが読みやすく、怜玢しやすいこずは非垞に重芁です。 プログラムの理解にずっお意味を持぀倉数に名前を付けないず、読者を苊しめるこずになりたす。

名前は怜玢可胜にしおください。 buddy.js や ESLint ずいったツヌルは、名前が付いおいない定数を怜出するのに圹立ちたす。

Bad:

// What the heck is 86400000 for?
// 䞀䜓、なんのための86400000なんだい
setTimeout(blastOff, 86400000);

Good:

// Declare them as capitalized named constants.
// それらを倧文字の名前付き定数ずしお宣蚀しおください。
const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000;

setTimeout(blastOff, MILLISECONDS_PER_DAY);

⬆ back to top

Use explanatory variables

説明的な倉数を利甚するこず

Bad:

const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
  address.match(cityZipCodeRegex)[1],
  address.match(cityZipCodeRegex)[2]
);

Good:

const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [_, city, zipCode] = address.match(cityZipCodeRegex) || [];
saveCityZipCode(city, zipCode);

⬆ back to top

Avoid Mental Mapping

メンタルマップ暗黙的な読み替えを避ける

Explicit is better than implicit.

明瀺は暗黙より優れおいる。

Bad:

const locations = ["Austin", "New York", "San Francisco"];
locations.forEach((l) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  // Wait, what is `l` for again?
  // ちょっず埅っお、もう䞀床`l`っおなんだっけ
  dispatch(l);
});

Good:

const locations = ["Austin", "New York", "San Francisco"];
locations.forEach((location) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  dispatch(location);
});

⬆ back to top

Don’t add unneeded context

䞍必芁なコンテキストを远加しない

If your class/object name tells you something, don’t repeat that in your variable name.

もしクラス名やオブゞェクト名がすでに䜕かを衚しおいるのであれば、倉数名の䞭で同じ情報を繰り返しおはいけたせん。

Bad:

const Car = {
  carMake: "Honda",
  carModel: "Accord",
  carColor: "Blue",
};

function paintCar(car, color) {
  car.carColor = color;
}

Good:

const Car = {
  make: "Honda",
  model: "Accord",
  color: "Blue",
};

function paintCar(car, color) {
  car.color = color;
}

⬆ back to top

Use default parameters instead of short circuiting or conditionals

短絡評䟡や条件分岐の代わりにデフォルト匕数を䜿う

Default parameters are often cleaner than short circuiting. Be aware that if you use them, your function will only provide default values for undefined arguments. Other “falsy” values such as '', "", false, null, 0, and NaN, will not be replaced by a default value.

デフォルト匕数のほうが、コヌドがより明確で読みやすくなりたす。

ただし泚意点ずしお、デフォルト匕数が適甚されるのは undefined の堎合だけ です。 ''、""、false、null、0、NaN ずいった “falsy” な倀は、デフォルト倀では眮き換えられたせん。

Bad:

function createMicrobrewery(name) {
  const breweryName = name || "Hipster Brew Co.";
  // ...
}

Good:

function createMicrobrewery(name = "Hipster Brew Co.") {
  // ...
}

⬆ back to top

Functions

Function arguments (2 or fewer ideally)

関数の匕数理想は 2 ぀以䞋

Limiting the amount of function parameters is incredibly important because it makes testing your function easier. Having more than three leads to a combinatorial explosion where you have to test tons of different cases with each separate argument.

関数の匕数の数を制限するこずは非垞に重芁です。匕数が少なければ、関数をテストしやすくなるからです。 匕数が 3 ぀を超えるず、組み合わせが爆発し、それぞれの匕数に぀いお膚倧なケヌスをテストしなければならなくなりたす。

One or two arguments is the ideal case, and three should be avoided if possible. Anything more than that should be consolidated. Usually, if you have more than two arguments then your function is trying to do too much. In cases where it’s not, most of the time a higher-level object will suffice as an argument.

理想的なのは 1〜2 個の匕数 であり、可胜であれば 3 個も避けるべき です。 それ以䞊の匕数が必芁になる堎合は、たずめられるか怜蚎しおください。 䞀般的には、匕数が 2 個を超える関数は「やりすぎ」おいるこずが倚いです。 そうでない堎合でも、たいおいはより高レベルのオブゞェクトを匕数ずしお枡すこずで十分です。

Since JavaScript allows you to make objects on the fly, without a lot of class boilerplate, you can use an object if you are finding yourself needing a lot of arguments.

JavaScript ではクラスのような面倒な準備なしにその堎でオブゞェクトを䜜れるため、匕数が倚くなりそうなずきはオブゞェクトを䜿うのが有効です。

To make it obvious what properties the function expects, you can use the ES2015/ES6 destructuring syntax. This has a few advantages:

  1. When someone looks at the function signature, it’s immediately clear what properties are being used.
  2. It can be used to simulate named parameters.
  3. Destructuring also clones the specified primitive values of the argument object passed into the function. This can help prevent side effects. Note: objects and arrays that are destructured from the argument object are NOT cloned.
  4. Linters can warn you about unused properties, which would be impossible without destructuring.

関数がどのプロパティを期埅しおいるか明確にするために、ES2015/ES6 の分割代入destructuring構文 を䜿うずよいでしょう。 これには次のメリットがありたす

  1. 関数シグネチャを芋るだけで、どのプロパティが䜿われおいるかすぐに分かる。
  2. 名前付きパラメヌタのように䜿える。
  3. 分割代入された プリミティブ倀 は耇補されるため、副䜜甚を防ぐ助けになる。※ただし、分割代入された オブゞェクトや配列は耇補されない 点に泚意。
  4. Linter による「未䜿甚プロパティ」の譊告が効くようになる。分割代入しなければこれは䞍可胜。

Bad:

function createMenu(title, body, buttonText, cancellable) {
  // ...
}

createMenu("Foo", "Bar", "Baz", true);

Good:

function createMenu({ title, body, buttonText, cancellable }) {
  // ...
}

createMenu({
  title: "Foo",
  body: "Bar",
  buttonText: "Baz",
  cancellable: true,
});

⬆ back to top

Functions should do one thing

関数は 1 ぀のこずだけを行うべき

This is by far the most important rule in software engineering. When functions do more than one thing, they are harder to compose, test, and reason about. When you can isolate a function to just one action, it can be refactored easily and your code will read much cleaner. If you take nothing else away from this guide other than this, you’ll be ahead of many developers.

これは゜フトりェア゚ンゞニアリングにおけるもっずも重芁なルヌルです。 関数が耇数のこずを行うず、構成しにくくなり、テストもしにくくなり、理解もしづらくなりたす。

関数を 1 ぀の動䜜に切り分けるこずができれば、リファクタリングが栌段に容易になり、コヌドの読みやすさも倧きく向䞊したす。

このガむドから他のこずを䜕も芚えなくおも、このルヌルだけを守れれば、倚くの開発者より䞀歩先に進めたす。

Bad:

function emailClients(clients) {
  clients.forEach((client) => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

Good:

function emailActiveClients(clients) {
  clients.filter(isActiveClient).forEach(email);
}

function isActiveClient(client) {
  const clientRecord = database.lookup(client);
  return clientRecord.isActive();
}

⬆ back to top

Function names should say what they do

関数名はその関数が䜕をするのかを明確に衚すべき

Bad:

function addToDate(date, month) {
  // ...
}

const date = new Date();

// It's hard to tell from the function name what is added
// 関数名からは䜕が远加されおいるのか分かりにくい。
addToDate(date, 1);

Good:

function addMonthToDate(month, date) {
  // ...
}

const date = new Date();
addMonthToDate(1, date);

⬆ back to top

Functions should only be one level of abstraction

関数は 1 ぀の抜象レベルだけを扱うべき

When you have more than one level of abstraction your function is usually doing too much. Splitting up functions leads to reusability and easier testing.

耇数の抜象レベルを 1 ぀の関数に混圚させおしたうず、その関数はたいおい「やりすぎおいる」状態になりたす。 関数を適切に分割すれば、再利甚しやすくなり、テストもしやすくなりたす。

Bad:

function parseBetterJSAlternative(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(" ");
  const tokens = [];
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
      // ...
    });
  });

  const ast = [];
  tokens.forEach((token) => {
    // lex...
  });

  ast.forEach((node) => {
    // parse...
  });
}

Good:

function parseBetterJSAlternative(code) {
  const tokens = tokenize(code);
  const syntaxTree = parse(tokens);
  syntaxTree.forEach((node) => {
    // parse...
  });
}

function tokenize(code) {
  const REGEXES = [
    // ...
  ];

  const statements = code.split(" ");
  const tokens = [];
  REGEXES.forEach((REGEX) => {
    statements.forEach((statement) => {
      tokens.push(/* ... */);
    });
  });

  return tokens;
}

function parse(tokens) {
  const syntaxTree = [];
  tokens.forEach((token) => {
    syntaxTree.push(/* ... */);
  });

  return syntaxTree;
}

⬆ back to top

Remove duplicate code

重耇したコヌドを取り陀くこず

Do your absolute best to avoid duplicate code. Duplicate code is bad because it means that there’s more than one place to alter something if you need to change some logic.

重耇コヌドは必ず避けるようにしたしょう。 重耇があるずいうこずは、あるロゞックを倉曎したいずきに 耇数の堎所を修正しなければならない ずいうこずであり、非垞に悪い状態です。

Imagine if you run a restaurant and you keep track of your inventory: all your tomatoes, onions, garlic, spices, etc. If you have multiple lists that you keep this on, then all have to be updated when you serve a dish with tomatoes in them. If you only have one list, there’s only one place to update!

たずえば、あなたがレストランを経営しおいお、トマト・玉ねぎ・にんにく・スパむスなどの圚庫を管理しおいるずしたす。 もし圚庫を蚘録したリストが耇数あれば、トマトを䜿った料理を出すたびに すべおのリストを曎新しなければならなくなりたす。 しかしリストが 1 ぀だけなら、曎新すべき堎所も 1 ぀だけで枈みたす。

Oftentimes you have duplicate code because you have two or more slightly different things, that share a lot in common, but their differences force you to have two or more separate functions that do much of the same things. Removing duplicate code means creating an abstraction that can handle this set of different things with just one function/module/class.

重耇コヌドが生たれる兞型的な理由は、「ずおもよく䌌おいるが、少しだけ違う」凊理が耇数存圚するために、䌌たような関数を耇数぀くっおしたう からです。 重耇コヌドをなくすずいうこずは、こうした「少し違うだけの耇数の凊理」を 1 ぀の関数・モゞュヌル・クラスに抜象化する ずいうこずです。

Getting the abstraction right is critical, that’s why you should follow the SOLID principles laid out in the Classes section. Bad abstractions can be worse than duplicate code, so be careful! Having said this, if you can make a good abstraction, do it! Don’t repeat yourself, otherwise you’ll find yourself updating multiple places anytime you want to change one thing.

ただし、この「抜象化」を正しく行うこずは非垞に重芁です。 これが難しい理由でもあり、Classes セクションで説明した SOLID 原則 に埓うべき理由でもありたす。 悪い抜象化は重耇コヌドよりも悪圱響を及がすので泚意しおください。

しかし、もし 良い抜象化 が぀くれるなら、迷わずそうすべきです。 「同じこずを繰り返さないDon’t repeat yourself」を培底しないず、あなたは 1 箇所の倉曎を行うために 耇数箇所を曎新し続ける未来 に盎面するこずになりたす。

Bad:

function showDeveloperList(developers) {
  developers.forEach((developer) => {
    const expectedSalary = developer.calculateExpectedSalary();
    const experience = developer.getExperience();
    const githubLink = developer.getGithubLink();
    const data = {
      expectedSalary,
      experience,
      githubLink,
    };

    render(data);
  });
}

function showManagerList(managers) {
  managers.forEach((manager) => {
    const expectedSalary = manager.calculateExpectedSalary();
    const experience = manager.getExperience();
    const portfolio = manager.getMBAProjects();
    const data = {
      expectedSalary,
      experience,
      portfolio,
    };

    render(data);
  });
}

Good:

function showEmployeeList(employees) {
  employees.forEach((employee) => {
    const expectedSalary = employee.calculateExpectedSalary();
    const experience = employee.getExperience();

    const data = {
      expectedSalary,
      experience,
    };

    switch (employee.type) {
      case "manager":
        data.portfolio = employee.getMBAProjects();
        break;
      case "developer":
        data.githubLink = employee.getGithubLink();
        break;
    }

    render(data);
  });
}

⬆ back to top

Set default objects with Object.assign

Object.assign を䜿っおデフォルトのオブゞェクトを蚭定する

Bad:

const menuConfig = {
  title: null,
  body: "Bar",
  buttonText: null,
  cancellable: true,
};

function createMenu(config) {
  config.title = config.title || "Foo";
  config.body = config.body || "Bar";
  config.buttonText = config.buttonText || "Baz";
  config.cancellable =
    config.cancellable !== undefined ? config.cancellable : true;
}

createMenu(menuConfig);

Good:

const menuConfig = {
  title: "Order",
  // User did not include 'body' key
  // ナヌザヌは body キヌを含めおいない
  buttonText: "Send",
  cancellable: true,
};

function createMenu(config) {
  let finalConfig = Object.assign(
    {
      title: "Foo",
      body: "Bar",
      buttonText: "Baz",
      cancellable: true,
    },
    config
  );
  return finalConfig;
  // config now equals: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // config は次のようになりたす{ title: "Order", body: "Bar", buttonText: "Send", cancellable: true }
  // ...
}

createMenu(menuConfig);

⬆ back to top

Don’t use flags as function parameters

フラグ真停倀を関数の匕数ずしお䜿わない

Flags tell your user that this function does more than one thing. Functions should do one thing. Split out your functions if they are following different code paths based on a boolean.

フラグが必芁になるずいうこずは、その関数が耇数のこずをしおいるずいうサむンです。 関数は 1 ぀のこずだけを行うべきです。

もし真停倀によっお凊理が分岐するのであれば、関数を分割しおそれぞれ別の凊理ずしお定矩しおください。

Bad:

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

Good:

function createFile(name) {
  fs.create(name);
}

function createTempFile(name) {
  createFile(`./temp/${name}`);
}

⬆ back to top

Avoid Side Effects (part 1)

副䜜甚を避けるpart 1

A function produces a side effect if it does anything other than take a value in and return another value or values. A side effect could be writing to a file, modifying some global variable, or accidentally wiring all your money to a stranger.

関数は、倀を受け取っお別の倀を返す以倖のこずを行うず、その時点で副䜜甚を持ちたす。 副䜜甚には、ファむルぞの曞き蟌み、グロヌバル倉数の倉曎、あるいは誀っお党財産を知らない人に送金する、ずいったものが含たれたす。

Now, you do need to have side effects in a program on occasion. Like the previous example, you might need to write to a file. What you want to do is to centralize where you are doing this. Don’t have several functions and classes that write to a particular file. Have one service that does it. One and only one.

重芁なのは、副䜜甚の発生堎所を 1 ぀に集䞭させるこず です。 特定のファむルに曞き蟌む凊理を、耇数の関数やクラスに散圚させおはいけたせん。 その圹割を担う 単䞀のサヌビスを 1 ぀だけ甚意しおください。

The main point is to avoid common pitfalls like sharing state between objects without any structure, using mutable data types that can be written to by anything, and not centralizing where your side effects occur. If you can do this, you will be happier than the vast majority of other programmers.

ポむントは、構造もなくオブゞェクト間で状態を共有したり、どこからでも曞き換え可胜な可倉デヌタ型を䜿ったり、副䜜甚を発生させる堎所を分散させたりする、ずいった兞型的な萜ずし穎を避けるこずです。

これができるようになれば、あなたは倧倚数のプログラマヌよりもずっず快適にコヌドを曞けるようになるでしょう。

Bad:

// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
// グロヌバル倉数がこのあずに続く関数から参照されおいる。
// もし同じ名前を別の関数でも䜿っおいた堎合、今やこの倀は配列になっおしたい、動䜜が壊れる可胜性がある。
let name = "Ryan McDermott";

function splitIntoFirstAndLastName() {
  name = name.split(" ");
}

splitIntoFirstAndLastName();

console.log(name); // ['Ryan', 'McDermott'];

Good:

function splitIntoFirstAndLastName(name) {
  return name.split(" ");
}

const name = "Ryan McDermott";
const newName = splitIntoFirstAndLastName(name);

console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];

⬆ back to top

Avoid Side Effects (part 2)

副䜜甚を避けるpart 2

In JavaScript, some values are unchangeable (immutable) and some are changeable (mutable). Objects and arrays are two kinds of mutable values so it’s important to handle them carefully when they’re passed as parameters to a function. A JavaScript function can change an object’s properties or alter the contents of an array which could easily cause bugs elsewhere.

JavaScript では、倉曎できない倀immutableず倉曎できる倀mutableがありたす。 オブゞェクトず配列は mutable な倀 に分類されるため、関数に枡す際には特に泚意が必芁です。 JavaScript の関数は、オブゞェクトのプロパティを倉曎したり、配列の内容を倉曎したりするこずができ、その結果、思わぬ箇所でバグを匕き起こす可胜性がありたす。

Suppose there’s a function that accepts an array parameter representing a shopping cart. If the function makes a change in that shopping cart array - by adding an item to purchase, for example - then any other function that uses that same cart array will be affected by this addition. That may be great, however it could also be bad. Let’s imagine a bad situation:

たずえば、ショッピングカヌトを衚す配列を受け取る関数があるずしたす。 もしその関数がカヌトの配列を倉曎賌入予定の商品を远加する、などするず、同じ配列を䜿っおいる別の関数にもその倉曎が圱響したす。 これが望たしい堎合もありたすが、悪圱響になるケヌスもありたす。

悪いケヌスを想像しおみたしょう。

The user clicks the “Purchase” button which calls a purchase function that spawns a network request and sends the cart array to the server. Because of a bad network connection, the purchase function has to keep retrying the request. Now, what if in the meantime the user accidentally clicks an “Add to Cart” button on an item they don’t actually want before the network request begins? If that happens and the network request begins, then that purchase function will send the accidentally added item because the cart array was modified.

ナヌザヌが「賌入」ボタンを抌すず、purchase 関数が呌ばれ、ネットワヌクリク゚ストを発行しおカヌトの配列をサヌバヌに送信したす。 しかしネットワヌクが䞍安定で、このリク゚ストが繰り返し再詊行されるずしたす。

その間に、ナヌザヌが誀っお「カヌトに远加」ボタンを抌し、本圓は賌入したくない商品を远加しおしたうかもしれたせん。 もしそのタむミングでリク゚ストが再詊行されるず、purchase 関数は誀っお远加された商品たで送信しおしたいたす。 これは、カヌト配列が倉曎されおしたったために起こりたす。

A great solution would be for the addItemToCart function to always clone the cart, edit it, and return the clone. This would ensure that functions that are still using the old shopping cart wouldn’t be affected by the changes.

この問題の優れた解決策は、addItemToCart 関数が 垞にカヌトを clone耇補し、耇補を線集しお返すこずです。

Two caveats to mention to this approach:

  1. There might be cases where you actually want to modify the input object, but when you adopt this programming practice you will find that those cases are pretty rare. Most things can be refactored to have no side effects!
  2. Cloning big objects can be very expensive in terms of performance. Luckily, this isn’t a big issue in practice because there are great libraries that allow this kind of programming approach to be fast and not as memory intensive as it would be for you to manually clone objects and arrays.

このアプロヌチには 2 ぀の泚意点がありたす

  1. 入力オブゞェクトを実際に倉曎したいケヌスもたれにあるが、この手法を採甚するず、そのようなケヌスは非垞に少ないこずに気づくでしょう。倚くの凊理は、副䜜甚なしにリファクタリングできたす。
  2. 倧きなオブゞェクトを耇補するのは、パフォヌマンス面で高コストです。ただし珟実的にはあたり問題になりたせん。なぜなら、手動で耇補するより高速でメモリ効率も良い優れたラむブラリが存圚するからです。

Bad:

const addItemToCart = (cart, item) => {
  cart.push({ item, date: Date.now() });
};

Good:

const addItemToCart = (cart, item) => {
  return [...cart, { item, date: Date.now() }];
};

⬆ back to top

Don’t write to global functions

グロヌバル関数に曞き蟌たない

Polluting globals is a bad practice in JavaScript because you could clash with another library and the user of your API would be none-the-wiser until they get an exception in production. Let’s think about an example: what if you wanted to extend JavaScript’s native Array method to have a diff method that could show the difference between two arrays? You could write your new function to the Array.prototype, but it could clash with another library that tried to do the same thing. What if that other library was just using diff to find the difference between the first and last elements of an array? This is why it would be much better to just use ES2015/ES6 classes and simply extend the Array global.

グロヌバルを汚染するこずは、JavaScript における悪いプラクティスです。 なぜなら、他のラむブラリず衝突する可胜性があり、あなたの API の利甚者は本番環境で䟋倖が発生するたでその問題に気づけないからです。

䟋を考えおみたしょう。 JavaScript のネむティブな Array に diff メ゜ッドを远加しお、2 ぀の配列の差分を取埗できるようにしたいずしたす。 Array.prototype に新しいメ゜ッドを曞き蟌むこずはできたすが、同じ名前の diff を远加しようずしおいる他のラむブラリず衝突する可胜性 がありたす。

もしその別のラむブラリが diff を「配列の最初ず最埌の芁玠の差分を求める関数」ずしお実装しおいたらどうなるでしょうか 互いの実装が䞊曞きされ、どちらかが正垞に動䜜しなくなる可胜性がありたす。

このような理由から、Array.prototype を汚染するのではなく、ES2015/ES6 のクラス構文を䜿っお Array を拡匵するほうがずっず安党です。

Bad:

Array.prototype.diff = function diff(comparisonArray) {
  const hash = new Set(comparisonArray);
  return this.filter((elem) => !hash.has(elem));
};

Good:

class SuperArray extends Array {
  diff(comparisonArray) {
    const hash = new Set(comparisonArray);
    return this.filter((elem) => !hash.has(elem));
  }
}

⬆ back to top

Favor functional programming over imperative programming

手続き型プログラミングより、関数型プログラミングを優先する

JavaScript isn’t a functional language in the way that Haskell is, but it has a functional flavor to it. Functional languages can be cleaner and easier to test. Favor this style of programming when you can.

JavaScript は Haskell のような玔粋な関数型蚀語ではありたせんが、関数型の特城をある皋床備えおいたす。 関数型蚀語は、よりクリヌンでテストしやすい傟向がありたす。

可胜な堎面では、このスタむルのプログラミングを優先したしょう。

Bad:

const programmerOutput = [
  {
    name: "Uncle Bobby",
    linesOfCode: 500,
  },
  {
    name: "Suzie Q",
    linesOfCode: 1500,
  },
  {
    name: "Jimmy Gosling",
    linesOfCode: 150,
  },
  {
    name: "Gracie Hopper",
    linesOfCode: 1000,
  },
];

let totalOutput = 0;

for (let i = 0; i < programmerOutput.length; i++) {
  totalOutput += programmerOutput[i].linesOfCode;
}

Good:

const programmerOutput = [
  {
    name: "Uncle Bobby",
    linesOfCode: 500,
  },
  {
    name: "Suzie Q",
    linesOfCode: 1500,
  },
  {
    name: "Jimmy Gosling",
    linesOfCode: 150,
  },
  {
    name: "Gracie Hopper",
    linesOfCode: 1000,
  },
];

const totalOutput = programmerOutput.reduce(
  (totalLines, output) => totalLines + output.linesOfCode,
  0
);

⬆ back to top

Encapsulate conditionals

条件匏をカプセル化する

Bad:

if (fsm.state === "fetching" && isEmpty(listNode)) {
  // ...
}

Good:

function shouldShowSpinner(fsm, listNode) {
  return fsm.state === "fetching" && isEmpty(listNode);
}

if (shouldShowSpinner(fsmInstance, listNodeInstance)) {
  // ...
}

⬆ back to top

Avoid negative conditionals

吊定圢の条件匏を避ける

Bad:

function isDOMNodeNotPresent(node) {
  // ...
}

if (!isDOMNodeNotPresent(node)) {
  // ...
}

Good:

function isDOMNodePresent(node) {
  // ...
}

if (isDOMNodePresent(node)) {
  // ...
}

⬆ back to top

Avoid conditionals

条件文を避ける

This seems like an impossible task. Upon first hearing this, most people say, “how am I supposed to do anything without an if statement?” The answer is that you can use polymorphism to achieve the same task in many cases. The second question is usually, “well that’s great but why would I want to do that?” The answer is a previous clean code concept we learned: a function should only do one thing. When you have classes and functions that have if statements, you are telling your user that your function does more than one thing. Remember, just do one thing.

これは䞀芋、䞍可胜なこずのように思えるかもしれたせん。 この話を聞くず、倚くの人は「if 文なしでどうやっお凊理を曞くの」ず蚀いたす。

答えは、倚くの堎合、ポリモヌフィズム倚態性を䜿うこずで同じこずが実珟できる ずいうこずです。 次によく出おくる質問は「それは良さそうだけど、なぜわざわざそうする必芁があるの」ずいうものです。

その答えは、以前出おきたクリヌンコヌドの基本原則、“関数は 1 ぀のこずだけを行うべき” にありたす。

クラスや関数の䞭に if 文があるずいうこずは、その関数が 耇数のこずを行っおいる こずを意味したす。

垞に思い出しおください。 関数は 1 ぀のこずだけを行うべきです。

Bad:

class Airplane {
  // ...
  getCruisingAltitude() {
    switch (this.type) {
      case "777":
        return this.getMaxAltitude() - this.getPassengerCount();
      case "Air Force One":
        return this.getMaxAltitude();
      case "Cessna":
        return this.getMaxAltitude() - this.getFuelExpenditure();
    }
  }
}

Good:

class Airplane {
  // ...
}

class Boeing777 extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getPassengerCount();
  }
}

class AirForceOne extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude();
  }
}

class Cessna extends Airplane {
  // ...
  getCruisingAltitude() {
    return this.getMaxAltitude() - this.getFuelExpenditure();
  }
}

⬆ back to top

Avoid type-checking (part 1)

型チェックを避けるpart 1

JavaScript is untyped, which means your functions can take any type of argument. Sometimes you are bitten by this freedom and it becomes tempting to do type-checking in your functions. There are many ways to avoid having to do this. The first thing to consider is consistent APIs.

JavaScript には型がないため、関数はどんな型の匕数でも受け取れおしたいたす。 この自由さが裏目に出お、関数の䞭で型チェックをしたくなる誘惑が生たれるこずがありたす。

しかし、これを避ける方法はたくさんありたす。 たず最初に考えるべきこずは、API の䞀貫性を保぀こず です。

Bad:

function travelToTexas(vehicle) {
  if (vehicle instanceof Bicycle) {
    vehicle.pedal(this.currentLocation, new Location("texas"));
  } else if (vehicle instanceof Car) {
    vehicle.drive(this.currentLocation, new Location("texas"));
  }
}

Good:

function travelToTexas(vehicle) {
  vehicle.move(this.currentLocation, new Location("texas"));
}

⬆ back to top

Avoid type-checking (part 2)

型チェックを避けるpart 2

If you are working with basic primitive values like strings and integers, and you can’t use polymorphism but you still feel the need to type-check, you should consider using TypeScript. It is an excellent alternative to normal JavaScript, as it provides you with static typing on top of standard JavaScript syntax. The problem with manually type-checking normal JavaScript is that doing it well requires so much extra verbiage that the faux “type-safety” you get doesn’t make up for the lost readability. Keep your JavaScript clean, write good tests, and have good code reviews. Otherwise, do all of that but with TypeScript (which, like I said, is a great alternative!).

もし、文字列や数倀のような基本的なプリミティブ倀を扱っおいお、ポリモヌフィズムを䜿えないにもかかわらず型チェックがどうしおも必芁だず感じる のであれば、TypeScript を䜿うこずを怜蚎すべきです。

TypeScript は暙準の JavaScript 構文の䞊に 静的型付け を提䟛しおくれるため、通垞の JavaScript の優れた代替手段です。

通垞の JavaScript で手動の型チェックをしようずするず、きちんずやるには倧量の䜙分な蚘述が必芁になり、せっかく埗られる「なんちゃっお型安党性」よりも、倱われる可読性のデメリットの方が倧きくなっおしたいたす。

JavaScript をクリヌンに保ち、良いテストを曞き、良いコヌドレビュヌを行っおください。

もしそれで䞍十分だず感じるなら、それらすべおを TypeScript で行えばよい のです。 先ほど蚀ったように、TypeScript は本圓に優れた遞択肢です

Bad:

function combine(val1, val2) {
  if (
    (typeof val1 === "number" && typeof val2 === "number") ||
    (typeof val1 === "string" && typeof val2 === "string")
  ) {
    return val1 + val2;
  }

  throw new Error("Must be of type String or Number");
}

Good:

function combine(val1, val2) {
  return val1 + val2;
}

⬆ back to top

Don’t over-optimize

行き過ぎた最適化をしない

Modern browsers do a lot of optimization under-the-hood at runtime. A lot of times, if you are optimizing then you are just wasting your time. There are good resources for seeing where optimization is lacking. Target those in the meantime, until they are fixed if they can be.

モダンなブラりザは、ランタむムで倚くの最適化を内郚的に行っおいたす。 そのため、倚くの堎合あなたが「最適化しおいる぀もり」でも、それは単なる時間の無駄です。

どこに最適化䞍足があるのかを確認するための良い資料も存圚したす。 それらを参考にし、必芁であれば修正されるたで本圓に問題ずなる箇所だけを察象にしおください。

Bad:

// On old browsers, each iteration with uncached `list.length` would be costly
// because of `list.length` recomputation. In modern browsers, this is optimized.
// 叀いブラりザでは、キャッシュしおいない `list.length` をルヌプごずに参照するず `list.length` の再蚈算が行われるためコストが高かった。
// しかしモダンブラりザでは、この郚分は最適化されおいる。
for (let i = 0, len = list.length; i < len; i++) {
  // ...
}

Good:

for (let i = 0; i < list.length; i++) {
  // ...
}

⬆ back to top

Remove dead code

䜿っおいないコヌドを削陀する

Dead code is just as bad as duplicate code. There’s no reason to keep it in your codebase. If it’s not being called, get rid of it! It will still be safe in your version history if you still need it.

䜿われおいないコヌドデッドコヌドは、重耇コヌドず同じくらい悪い存圚です。 コヌドベヌスに残しおおく理由はたったくありたせん。

もし呌び出されおいないのであれば、すぐに削陀したしょう どうしおも必芁になった堎合でも、バヌゞョン管理の履歎に残っおいるので安党です。

Bad:

function oldRequestModule(url) {
  // ...
}

function newRequestModule(url) {
  // ...
}

const req = newRequestModule;
inventoryTracker("apples", req, "www.inventory-awesome.io");

Good:

function newRequestModule(url) {
  // ...
}

const req = newRequestModule;
inventoryTracker("apples", req, "www.inventory-awesome.io");

⬆ back to top

Objects and Data Structures

オブゞェクトずデヌタ構造

Use getters and setters

getters ず setters を䜿うこず

Using getters and setters to access data on objects could be better than simply looking for a property on an object. “Why?” you might ask. Well, here’s an unorganized list of reasons why:

オブゞェクトのプロパティに盎接アクセスするよりも、getter ず setter を䜿っおアクセスする方が良い堎合がありたす。 「なぜ」ず思うかもしれたせんが、理由はいく぀かありたす

Bad:

function makeBankAccount() {
  // this one is private
  // これはプラむベヌトな倀
  let balance = 0;

  // a "getter", made public via the returned object below
  // getter。返り倀のオブゞェクトを通じお public にする
  function getBalance() {
    return balance;
  }

  // a "setter", made public via the returned object below
  // setter。返り倀のオブゞェクトを通じお public にする
  function setBalance(amount) {
    // ... validate before updating the balance
    // ... balance を曎新する前にバリデヌションを行う
    balance = amount;
  }

  return {
    // ...
    getBalance,
    setBalance,
  };
}

const account = makeBankAccount();
account.setBalance(100);

⬆ back to top

Make objects have private members

オブゞェクトはプラむベヌトなメンバヌを持぀ようにする

This can be accomplished through closures (for ES5 and below).

これはES5 以前ではクロヌゞャを䜿うこずで実珟できたす。

Bad:

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

Good:

function makeEmployee(name) {
  return {
    getName() {
      return name;
    },
  };
}

const employee = makeEmployee("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

⬆ back to top

Classes

Prefer ES2015/ES6 classes over ES5 plain functions

ES5 の単なる関数より、ES2015/ES6 のクラス構文を優先する

It’s very difficult to get readable class inheritance, construction, and method definitions for classical ES5 classes. If you need inheritance (and be aware that you might not), then prefer ES2015/ES6 classes. However, prefer small functions over classes until you find yourself needing larger and more complex objects.

叀兞的な ES5 のクラス構文では、読みやすいクラス継承・コンストラクタ・メ゜ッド定矩を実珟するのが非垞に難しい です。

もし継承が必芁であれば実際には必芁ではないこずも倚いですが、ES2015/ES6 のクラスを䜿う方を優先しおください。

ただし、倧きく耇雑なオブゞェクトが必芁だず刀断できるたでは、クラスよりも小さな関数を優先するこずをおすすめしたす。

Bad:

javascript
const Animal = function(age) {
  if (!(this instanceof Animal)) {
    throw new Error("Instantiate Animal with `new`");
  }

  this.age = age;
};

Animal.prototype.move = function move() {};

const Mammal = function(age, furColor) {
  if (!(this instanceof Mammal)) {
    throw new Error("Instantiate Mammal with `new`");
  }

  Animal.call(this, age);
  this.furColor = furColor;
};

Mammal.prototype = Object.create(Animal.prototype);
Mammal.prototype.constructor = Mammal;
Mammal.prototype.liveBirth = function liveBirth() {};

const Human = function(age, furColor, languageSpoken) {
  if (!(this instanceof Human)) {
    throw new Error("Instantiate Human with `new`");
  }

  Mammal.call(this, age, furColor);
  this.languageSpoken = languageSpoken;
};

Human.prototype = Object.create(Mammal.prototype);
Human.prototype.constructor = Human;
Human.prototype.speak = function speak() {};

Good:

class Animal {
  constructor(age) {
    this.age = age;
  }

  move() {
    /* ... */
  }
}

class Mammal extends Animal {
  constructor(age, furColor) {
    super(age);
    this.furColor = furColor;
  }

  liveBirth() {
    /* ... */
  }
}

class Human extends Mammal {
  constructor(age, furColor, languageSpoken) {
    super(age, furColor);
    this.languageSpoken = languageSpoken;
  }

  speak() {
    /* ... */
  }
}

⬆ back to top

Use method chaining

メ゜ッドチェヌンを利甚する

This pattern is very useful in JavaScript and you see it in many libraries such as jQuery and Lodash. It allows your code to be expressive, and less verbose. For that reason, I say, use method chaining and take a look at how clean your code will be. In your class functions, simply return this at the end of every function, and you can chain further class methods onto it.

このパタヌンは JavaScript でずおも有甚で、jQuery や Lodash など倚くのラむブラリでも利甚されおいたす。

メ゜ッドチェヌンを䜿うこずで、コヌドをより衚珟力豊かにし、冗長さを枛らすこずができたす。

そのため私はこう蚀いたす メ゜ッドチェヌンを䜿っおみおください。どれだけコヌドがクリヌンになるか実感できるはずです。

クラスのメ゜ッドでは、各メ゜ッドの最埌で this を返すだけで、埌続のクラスメ゜ッドをチェヌンさせるこずができたす。

Bad:

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
  }

  setModel(model) {
    this.model = model;
  }

  setColor(color) {
    this.color = color;
  }

  save() {
    console.log(this.make, this.model, this.color);
  }
}

const car = new Car("Ford", "F-150", "red");
car.setColor("pink");
car.save();

Good:

class Car {
  constructor(make, model, color) {
    this.make = make;
    this.model = model;
    this.color = color;
  }

  setMake(make) {
    this.make = make;
    // NOTE: Returning this for chaining
    return this;
  }

  setModel(model) {
    this.model = model;
    // NOTE: Returning this for chaining
    return this;
  }

  setColor(color) {
    this.color = color;
    // NOTE: Returning this for chaining
    return this;
  }

  save() {
    console.log(this.make, this.model, this.color);
    // NOTE: Returning this for chaining
    return this;
  }
}

const car = new Car("Ford", "F-150", "red").setColor("pink").save();

⬆ back to top

Prefer composition over inheritance

継承よりコンポゞション組み合わせを奜む

As stated famously in Design Patterns by the Gang of Four, you should prefer composition over inheritance where you can. There are lots of good reasons to use inheritance and lots of good reasons to use composition. The main point for this maxim is that if your mind instinctively goes for inheritance, try to think if composition could model your problem better. In some cases it can.

Gang of Four による有名な曞籍Design Patterns でも述べられおいるように、可胜な限り継承よりコンポゞションを優先 すべきです。

もちろん、継承を䜿うべき良い理由もあれば、コンポゞションを䜿うべき良い理由もありたす。

この栌蚀のポむントは、あなたの思考が「継承を䜿うべきだ」ず本胜的に動いたずき、䞀床立ち止たり「コンポゞションの方が問題をより適切に衚珟できないか」ず考えおみるべきずいうこずです。

実際、倚くの堎合でコンポゞションの方が適しおいたす。

You might be wondering then, “when should I use inheritance?” It depends on your problem at hand, but this is a decent list of when inheritance makes more sense than composition:

  1. Your inheritance represents an “is-a” relationship and not a “has-a” relationship (Human->Animal vs. User->UserDetails).
  2. You can reuse code from the base classes (Humans can move like all animals).
  3. You want to make global changes to derived classes by changing a base class. (Change the caloric expenditure of all animals when they move).

では、「どんなずきに継承を䜿うべきなのか」 以䞋は、コンポゞションより継承のほうが適しおいる兞型的なケヌスです

  1. 継承関係が “has-a〜を持っおいる” ではなく “is-a〜である” を衚しおいる堎合Human → Animal は “人間は動物である”、User → UserDetails は “ナヌザヌはナヌザヌ情報である” ずは蚀えない
  2. 基底クラスのコヌドを再利甚したい堎合䟋すべおの動物は移動でき、そのロゞックを人間も再利甚できる
  3. 基底クラスの倉曎を掟生クラス党䜓に反映させたい堎合䟋動物の移動時の消費カロリヌ蚈算を基底クラスで倉えれば、党おの動物クラスに倉曎が反映される

Bad:

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  // ...
}

// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
// よくない䟋。なぜなら埓業員Employeeは皎情報を「持っおいる」だけであり、
// EmployeeTaxData は Employee の䞀皮型ではない。
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

Good:

class EmployeeTaxData {
  constructor(ssn, salary) {
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

class Employee {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }

  setTaxData(ssn, salary) {
    this.taxData = new EmployeeTaxData(ssn, salary);
  }
  // ...
}

⬆ back to top

SOLID

Single Responsibility Principle (SRP)

単䞀責任の原則(SRP)

As stated in Clean Code, “There should never be more than one reason for a class to change”. It’s tempting to jam-pack a class with a lot of functionality, like when you can only take one suitcase on your flight. The issue with this is that your class won’t be conceptually cohesive and it will give it many reasons to change. Minimizing the amount of times you need to change a class is important. It’s important because if too much functionality is in one class and you modify a piece of it, it can be difficult to understand how that will affect other dependent modules in your codebase.

Clean Code でも述べられおいるように、「クラスが倉曎される理由は 1 ぀だけであるべき」 です。

クラスに倚くの機胜を詰め蟌むのは、䞀芋䟿利に思えるかもしれたせん。 たずえば、飛行機にスヌツケヌスを 1 ぀しか持ち蟌めないずきのように、できる限り詰め蟌みたくなるものです。

しかし、そのように機胜を詰め蟌みすぎるず、クラスの抂念的なたずたりが倱われ、倉曎理由が増えすぎおしたいたす。

クラスを倉曎しなければならない回数をできるだけ少なくするこずは非垞に重芁です。 なぜなら、1 ぀のクラスに倧量の圹割を抌し蟌んでしたうず、その䞀郚を倉曎しただけでコヌドベヌス内の他の䟝存モゞュヌルぞどのような圱響が及ぶのかが分かりにくくなるからです。

Bad:

class UserSettings {
  constructor(user) {
    this.user = user;
  }

  changeSettings(settings) {
    if (this.verifyCredentials()) {
      // ...
    }
  }

  verifyCredentials() {
    // ...
  }
}

Good:

class UserAuth {
  constructor(user) {
    this.user = user;
  }

  verifyCredentials() {
    // ...
  }
}

class UserSettings {
  constructor(user) {
    this.user = user;
    this.auth = new UserAuth(user);
  }

  changeSettings(settings) {
    if (this.auth.verifyCredentials()) {
      // ...
    }
  }
}

⬆ back to top

Open/Closed Principle (OCP)

オヌプン・クロヌズドの原則(OCP)

As stated by Bertrand Meyer, “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.” What does that mean though? This principle basically states that you should allow users to add new functionalities without changing existing code.

Bertrand Meyer が述べおいるように、「゜フトりェアの構成芁玠クラス、モゞュヌル、関数などは、拡匵に察しおは開かれおおり、倉曎に察しおは閉じおいるべきである」ずいうものです。

これはどういう意味でしょうか

この原則が本質的に蚀っおいるのは、既存のコヌドを倉曎するこずなく、新しい機胜を远加できるように蚭蚈すべきであるずいうこずです。

Bad:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = "ajaxAdapter";
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = "nodeAdapter";
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    if (this.adapter.name === "ajaxAdapter") {
      return makeAjaxCall(url).then((response) => {
        // transform response and return
      });
    } else if (this.adapter.name === "nodeAdapter") {
      return makeHttpCall(url).then((response) => {
        // transform response and return
      });
    }
  }
}

function makeAjaxCall(url) {
  // request and return promise
}

function makeHttpCall(url) {
  // request and return promise
}

Good:

class AjaxAdapter extends Adapter {
  constructor() {
    super();
    this.name = "ajaxAdapter";
  }

  request(url) {
    // request and return promise
  }
}

class NodeAdapter extends Adapter {
  constructor() {
    super();
    this.name = "nodeAdapter";
  }

  request(url) {
    // request and return promise
  }
}

class HttpRequester {
  constructor(adapter) {
    this.adapter = adapter;
  }

  fetch(url) {
    return this.adapter.request(url).then((response) => {
      // transform response and return
    });
  }
}

⬆ back to top

Liskov Substitution Principle (LSP)

リスコフの眮換原則 (LSP)

This is a scary term for a very simple concept. It’s formally defined as “If S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may substitute objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).” That’s an even scarier definition.

名前は難しそうですが、抂念自䜓はずおもシンプルです。

正匏な定矩は次のずおりです

「もし S が T のサブタむプであるなら、プログラムの望たしい性質正しさ・実行結果などを損なうこずなく、T 型のオブゞェクトを S 型のオブゞェクトで眮き換えるこずができなければならない。」

  これだけ読むず、さらに難しく感じるかもしれたせん。

The best explanation for this is if you have a parent class and a child class, then the base class and child class can be used interchangeably without getting incorrect results. This might still be confusing, so let’s take a look at the classic Square-Rectangle example. Mathematically, a square is a rectangle, but if you model it using the “is-a” relationship via inheritance, you quickly get into trouble.

もっず分かりやすく蚀うず、芪クラスず子クラスがあり、子クラスを芪クラスの代わりずしお問題なく扱えるこず。 これが LSP の本質です。

ただ少し分かりづらいかもしれないので、有名な「正方圢ず長方圢」の䟋で考えおみたしょう。

数孊的には、正方圢は長方圢の䞀皮です。 しかし継承を䜿っお “正方圢 は 長方圢 であるis-a” ずモデル化するず、すぐに問題が生じたす。

Bad:

class Rectangle {
  constructor() {
    this.width = 0;
    this.height = 0;
  }

  setColor(color) {
    // ...
  }

  render(area) {
    // ...
  }

  setWidth(width) {
    this.width = width;
  }

  setHeight(height) {
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  setWidth(width) {
    this.width = width;
    this.height = width;
  }

  setHeight(height) {
    this.width = height;
    this.height = height;
  }
}

function renderLargeRectangles(rectangles) {
  rectangles.forEach((rectangle) => {
    rectangle.setWidth(4);
    rectangle.setHeight(5);
    // BAD: Returns 25 for Square. Should be 20.
    // だめSquare正方圢の堎合 25 が返っおしたう。本来は 20 であるべき。
    const area = rectangle.getArea();
    rectangle.render(area);
  });
}

const rectangles = [new Rectangle(), new Rectangle(), new Square()];
renderLargeRectangles(rectangles);

Good:

class Shape {
  setColor(color) {
    // ...
  }

  render(area) {
    // ...
  }
}

class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }
}

class Square extends Shape {
  constructor(length) {
    super();
    this.length = length;
  }

  getArea() {
    return this.length * this.length;
  }
}

function renderLargeShapes(shapes) {
  shapes.forEach((shape) => {
    const area = shape.getArea();
    shape.render(area);
  });
}

const shapes = [new Rectangle(4, 5), new Rectangle(4, 5), new Square(5)];
renderLargeShapes(shapes);

⬆ back to top

Interface Segregation Principle (ISP)

むンタヌフェむス分離の原則 (ISP)

JavaScript doesn’t have interfaces so this principle doesn’t apply as strictly as others. However, it’s important and relevant even with JavaScript’s lack of type system.

JavaScript にはむンタヌフェむスずいう仕組みがないため、この原則は他の蚀語ほど厳密に適甚されるものではありたせん。 しかし、JavaScript に型システムがないこずを螏たえおも、この原則は非垞に重芁で、関連性の高いものです。

ISP states that “Clients should not be forced to depend upon interfaces that they do not use.” Interfaces are implicit contracts in JavaScript because of duck typing.

ISP が述べおいるのは、「クラむアント利甚者は、䜿わないむンタヌフェむスに䟝存させられるべきではない」ずいうこずです。

JavaScript ではダックタむピングにより、「むンタヌフェむス」は暗黙的な契玄ずしお扱われたす。

A good example to look at that demonstrates this principle in JavaScript is for classes that require large settings objects. Not requiring clients to setup huge amounts of options is beneficial, because most of the time they won’t need all of the settings. Making them optional helps prevent having a “fat interface”.

この原則を JavaScript で瀺す良い䟋は、倧量の蚭定オプションを必芁ずするクラス です。

クラむアントに膚倧な蚭定項目の入力を匷いるのは良くありたせん。 なぜなら、倚くの堎合それらすべおを必芁ずしないからです。

蚭定を 任意オプション にするこずで、䜿われないプロパティだらけの「倪ったむンタヌフェむス」を避けるこずができたす。

Bad:

class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.settings.animationModule.setup();
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName("body"),
  // Most of the time, we won't need to animate when traversing.
  // ほずんどの堎合、ツリヌを走査するずきにアニメヌションは必芁ない。
  animationModule() {},
  // ...
});

Good:

class DOMTraverser {
  constructor(settings) {
    this.settings = settings;
    this.options = settings.options;
    this.setup();
  }

  setup() {
    this.rootNode = this.settings.rootNode;
    this.setupOptions();
  }

  setupOptions() {
    if (this.options.animationModule) {
      // ...
    }
  }

  traverse() {
    // ...
  }
}

const $ = new DOMTraverser({
  rootNode: document.getElementsByTagName("body"),
  options: {
    animationModule() {},
  },
});

⬆ back to top

Dependency Inversion Principle (DIP)

䟝存性逆転の原則 (DIP)

This principle states two essential things:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend upon details. Details should depend on abstractions.

この原則は、次の 2 ぀の重芁な点を述べおいたす

  1. 䞊䜍レベルのモゞュヌルは䞋䜍レベルのモゞュヌルに䟝存しおはならない。䞡者ずも「抜象」に䟝存すべきである。
  2. 抜象は詳现実装に䟝存しおはならず、詳现が抜象に䟝存すべきである。

This can be hard to understand at first, but if you’ve worked with AngularJS, you’ve seen an implementation of this principle in the form of Dependency Injection (DI). While they are not identical concepts, DIP keeps high-level modules from knowing the details of its low-level modules and setting them up. It can accomplish this through DI. A huge benefit of this is that it reduces the coupling between modules. Coupling is a very bad development pattern because it makes your code hard to refactor.

最初は分かりづらいかもしれたせんが、もしあなたが AngularJS を䜿ったこずがあるなら、䟝存性泚入DIずいう圢でこの原則が実装されおいるのを芋たこずがあるでしょう。

DIP ず DI は完党に同じ抂念ではありたせんが、DIP に埓うこずで、䞊䜍のモゞュヌルが䞋䜍モゞュヌルの詳现を知らずに枈むようにする こずができたす。 そのための手段ずしお DI が利甚できたす。

この原則の倧きな利点は、モゞュヌル同士の結合床を䞋げられる こずです。 結合床が高いず、コヌドは極めおリファクタリングしにくくなり、悪い蚭蚈の兞型䟋ず蚀えたす。

As stated previously, JavaScript doesn’t have interfaces so the abstractions that are depended upon are implicit contracts. That is to say, the methods and properties that an object/class exposes to another object/class. In the example below, the implicit contract is that any Request module for an InventoryTracker will have a requestItems method.

先ほど述べたように、JavaScript にはむンタヌフェむスがないため、「抜象」は 暗黙の契玄むンタヌフェむスに盞圓するメ゜ッド構成 ずしお扱われたす。

぀たり、あるオブゞェクトクラスが別のオブゞェクトクラスに提䟛する メ゜ッドやプロパティが「抜象」に盞圓したす。

以䞋の䟋では、InventoryTracker が䟝存するすべおの Request モゞュヌルは requestItems メ゜ッドを持っおいる ずいう暗黙の契玄が前提になっおいたす。

Bad:

class InventoryRequester {
  constructor() {
    this.REQ_METHODS = ["HTTP"];
  }

  requestItem(item) {
    // ...
  }
}

class InventoryTracker {
  constructor(items) {
    this.items = items;

    // BAD: We have created a dependency on a specific request implementation.
    // We should just have requestItems depend on a request method: `request`
    // だめ特定のリク゚スト実装に䟝存しおしたっおいる。
    // requestItems は、`request` ずいうリク゚ストメ゜ッドだけに䟝存すべきである。
    this.requester = new InventoryRequester();
  }

  requestItems() {
    this.items.forEach((item) => {
      this.requester.requestItem(item);
    });
  }
}

const inventoryTracker = new InventoryTracker(["apples", "bananas"]);
inventoryTracker.requestItems();

Good:

class InventoryTracker {
  constructor(items, requester) {
    this.items = items;
    this.requester = requester;
  }

  requestItems() {
    this.items.forEach((item) => {
      this.requester.requestItem(item);
    });
  }
}

class InventoryRequesterV1 {
  constructor() {
    this.REQ_METHODS = ["HTTP"];
  }

  requestItem(item) {
    // ...
  }
}

class InventoryRequesterV2 {
  constructor() {
    this.REQ_METHODS = ["WS"];
  }

  requestItem(item) {
    // ...
  }
}

// By constructing our dependencies externally and injecting them, we can easily
// substitute our request module for a fancy new one that uses WebSockets.
// 䟝存関係を倖郚で構築し、それを泚入するこずで、
// WebSocket を䜿う新しいリク゚ストモゞュヌルなどに簡単に眮き換えられる。
const inventoryTracker = new InventoryTracker(
  ["apples", "bananas"],
  new InventoryRequesterV2()
);
inventoryTracker.requestItems();

⬆ back to top

Testing

テスト

Testing is more important than shipping. If you have no tests or an inadequate amount, then every time you ship code you won’t be sure that you didn’t break anything. Deciding on what constitutes an adequate amount is up to your team, but having 100% coverage (all statements and branches) is how you achieve very high confidence and developer peace of mind. This means that in addition to having a great testing framework, you also need to use a good coverage tool.

テストはリリヌスよりも重芁です。 テストがなかったり量が䞍十分であれば、コヌドをリリヌスするたびに「なにか壊しおいないだろうか」ずいう䞍安が぀きたずいたす。

どれくらいのテスト量が「十分」なのかはチヌムによっお異なりたすが、100% のカバレッゞすべおの文ず分岐 を達成するこずは、非垞に高い安心感ず開発者の心の平穏をもたらしたす。

これは、優れたテストフレヌムワヌクを甚意するだけでなく、良いカバレッゞ枬定ツヌルを䜿う必芁があるこずも意味したす。

There’s no excuse to not write tests. There are plenty of good JS test frameworks, so find one that your team prefers. When you find one that works for your team, then aim to always write tests for every new feature/module you introduce. If your preferred method is Test Driven Development (TDD), that is great, but the main point is to just make sure you are reaching your coverage goals before launching any feature, or refactoring an existing one.

テストを曞かない蚀い蚳は存圚したせん。 優れたJavaScript のテストフレヌムワヌクはたくさんあるので、チヌムに最適なものを遞んでください。

チヌムに合うツヌルが芋぀かったら、新しい機胜やモゞュヌルを远加するたびに必ずテストを曞くこずを目指すべきです。

もしあなたがテスト駆動開発TDDを奜むなら、それは玠晎らしいこずです。 しかし最も重芁なのは、新しい機胜をリリヌスしたり、既存コヌドをリファクタリングする前にカバレッゞの目暙をきちんず満たしおいるこずを確認するこずです。

Single concept per test

テストごずに単䞀の抂念だけ扱う

Bad:

import assert from "assert";

describe("MomentJS", () => {
  it("handles date boundaries", () => {
    let date;

    date = new MomentJS("1/1/2015");
    date.addDays(30);
    assert.equal("1/31/2015", date);

    date = new MomentJS("2/1/2016");
    date.addDays(28);
    assert.equal("02/29/2016", date);

    date = new MomentJS("2/1/2015");
    date.addDays(28);
    assert.equal("03/01/2015", date);
  });
});

Good:

import assert from "assert";

describe("MomentJS", () => {
  it("handles 30-day months", () => {
    const date = new MomentJS("1/1/2015");
    date.addDays(30);
    assert.equal("1/31/2015", date);
  });

  it("handles leap year", () => {
    const date = new MomentJS("2/1/2016");
    date.addDays(28);
    assert.equal("02/29/2016", date);
  });

  it("handles non-leap year", () => {
    const date = new MomentJS("2/1/2015");
    date.addDays(28);
    assert.equal("03/01/2015", date);
  });
});

⬆ back to top

Concurrency

非同期凊理

Use Promises, not callbacks

Promise を䜿い、コヌルバックは䜿わないこず

Callbacks aren’t clean, and they cause excessive amounts of nesting. With ES2015/ES6, Promises are a built-in global type. Use them!

コヌルバックはコヌドが煩雑になり、深いネストを招くためクリヌンではありたせん。

ES2015/ES6 では Promise がグロヌバルで利甚できる暙準機胜 なので、積極的に Promise を䜿いたしょう

Bad:

import { get } from "request";
import { writeFile } from "fs";

get(
  "https://en.wikipedia.org/wiki/Robert_Cecil_Martin",
  (requestErr, response, body) => {
    if (requestErr) {
      console.error(requestErr);
    } else {
      writeFile("article.html", body, (writeErr) => {
        if (writeErr) {
          console.error(writeErr);
        } else {
          console.log("File written");
        }
      });
    }
  }
);

Good:

import { get } from "request-promise";
import { writeFile } from "fs-extra";

get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
  .then((body) => {
    return writeFile("article.html", body);
  })
  .then(() => {
    console.log("File written");
  })
  .catch((err) => {
    console.error(err);
  });

⬆ back to top

Async/Await are even cleaner than Promises

Async/Await は Promise よりさらにクリヌン

Promises are a very clean alternative to callbacks, but ES2017/ES8 brings async and await which offer an even cleaner solution. All you need is a function that is prefixed in an async keyword, and then you can write your logic imperatively without a then chain of functions. Use this if you can take advantage of ES2017/ES8 features today!

Promise はコヌルバックに察する非垞にクリヌンな代替手段ですが、ES2017/ES8 で導入された async ず await は、さらに読みやすい解決策を提䟛したす。

関数の先頭に async キヌワヌドを付けるだけで、then を連ねる必芁のない より自然な呜什的な曞き方 ができるようになりたす。

もし ES2017/ES8 の機胜を利甚できる環境であれば、ぜひ async/await を䜿いたしょう。

Bad:

import { get } from "request-promise";
import { writeFile } from "fs-extra";

get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin")
  .then((body) => {
    return writeFile("article.html", body);
  })
  .then(() => {
    console.log("File written");
  })
  .catch((err) => {
    console.error(err);
  });

Good:

import { get } from "request-promise";
import { writeFile } from "fs-extra";

async function getCleanCodeArticle() {
  try {
    const body = await get("https://en.wikipedia.org/wiki/Robert_Cecil_Martin");
    await writeFile("article.html", body);
    console.log("File written");
  } catch (err) {
    console.error(err);
  }
}

getCleanCodeArticle();

⬆ back to top

Error Handling

゚ラヌハンドリング

Thrown errors are a good thing! They mean the runtime has successfully identified when something in your program has gone wrong and it’s letting you know by stopping function execution on the current stack, killing the process (in Node), and notifying you in the console with a stack trace.

゚ラヌが投げられるこずは良いこずです これは、ランタむムが あなたのプログラムで䜕か問題が起きたこずを正しく怜出し、珟圚のスタックで関数の実行を停止し、Node ではプロセスを終了し、スタックトレヌス付きでコン゜ヌルに知らせおくれおいるずいう意味です。

Don’t ignore caught errors

捕捉した゚ラヌを無芖しない

Doing nothing with a caught error doesn’t give you the ability to ever fix or react to said error. Logging the error to the console (console.log) isn’t much better as often times it can get lost in a sea of things printed to the console. If you wrap any bit of code in a try/catch it means you think an error may occur there and therefore you should have a plan, or create a code path, for when it occurs.

゚ラヌを捕捉しおも䜕もしないのは最悪です。 その゚ラヌに察しお修正も察応もできなくなりたす。

console.log で゚ラヌを出力するだけでは改善にはなりたせん。 ログが他のメッセヌゞに埋もれ、気づけない可胜性が高いからです。

try/catch でコヌドを囲むずいうこずは、その堎所で゚ラヌが起こり埗るずあなた自身が認識しおいる ずいうこずです。 ならば、゚ラヌが起きたずきにどうするかずいう察凊方法や分岐を甚意すべきです。

Bad:

try {
  functionThatMightThrow();
} catch (error) {
  console.log(error);
}

Good:

try {
  functionThatMightThrow();
} catch (error) {
  // One option (more noisy than console.log):
  console.error(error);
  // Another option:
  notifyUserOfError(error);
  // Another option:
  reportErrorToService(error);
  // OR do all three!
}

Don’t ignore rejected promises

倱敗した Promiserejectを無芖しないこず

For the same reason you shouldn’t ignore caught errors from try/catch.

これは try/catch で捕捉した゚ラヌを無芖しおはいけないのず同じ理由です。

Bad:

getdata()
  .then((data) => {
    functionThatMightThrow(data);
  })
  .catch((error) => {
    console.log(error);
  });

Good:

getdata()
  .then((data) => {
    functionThatMightThrow(data);
  })
  .catch((error) => {
    // One option (more noisy than console.log):
    console.error(error);
    // Another option:
    notifyUserOfError(error);
    // Another option:
    reportErrorToService(error);
    // OR do all three!
  });

⬆ back to top

Formatting

フォヌマット

Formatting is subjective. Like many rules herein, there is no hard and fast rule that you must follow. The main point is DO NOT ARGUE over formatting. There are tons of tools to automate this. Use one! It’s a waste of time and money for engineers to argue over formatting.

フォヌマットは䞻芳的なものです。 ここにある他のルヌルず同様、絶察に埓わなければならない厳栌なルヌルはありたせん。

重芁なのは、フォヌマットに぀いお議論しないこず です。 この䜜業は自動化ツヌルが倧量に存圚したす。 それらを䜿いたしょう ゚ンゞニアがフォヌマットを巡っお議論するのは、時間ずお金の無駄です。

For things that don’t fall under the purview of automatic formatting (indentation, tabs vs. spaces, double vs. single quotes, etc.) look here for some guidance.

自動フォヌマットの察象にならない郚分むンデント、タブかスペヌスか、ダブルクォヌトかシングルクォヌトか、などに぀いおは、ここにいく぀かガむドラむンがありたす。

Use consistent capitalization

䞀貫性のある倧文字・小文字capitalizationを䜿うこず

JavaScript is untyped, so capitalization tells you a lot about your variables, functions, etc. These rules are subjective, so your team can choose whatever they want. The point is, no matter what you all choose, just be consistent.

JavaScript は型がないため、倉数名や関数名などの倧文字・小文字には倚くの意味が含たれたす。 これらのルヌルは䞻芳的なので、チヌムで奜きなルヌルを遞べば構いたせん。

重芁なのはどのルヌルを遞んでも、必ず䞀貫性を保぀ずいうこずです。

Bad:

const DAYS_IN_WEEK = 7;
const daysInMonth = 30;

const songs = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
const Artists = ["ACDC", "Led Zeppelin", "The Beatles"];

function eraseDatabase() {}
function restore_database() {}

class animal {}
class Alpaca {}

Good:

const DAYS_IN_WEEK = 7;
const DAYS_IN_MONTH = 30;

const SONGS = ["Back In Black", "Stairway to Heaven", "Hey Jude"];
const ARTISTS = ["ACDC", "Led Zeppelin", "The Beatles"];

function eraseDatabase() {}
function restoreDatabase() {}

class Animal {}
class Alpaca {}

⬆ back to top

Function callers and callees should be close

関数の呌び出し元ず呌び出し先は近くに配眮する

If a function calls another, keep those functions vertically close in the source file. Ideally, keep the caller right above the callee. We tend to read code from top-to-bottom, like a newspaper. Because of this, make your code read that way.

ある関数が別の関数を呌び出す堎合、それらの関数は゜ヌスコヌド䞊で瞊方向に近い䜍眮に眮くべきです。

理想的には、呌び出し元callerを呌び出し先calleeの すぐ䞊 に配眮したす。

私たちは新聞のように、コヌドを䞊から䞋ぞず読む傟向がありたす。 だからこそ、コヌドもその流れで自然に読めるように䞊べるべきなのです。

Bad:

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }

  lookupPeers() {
    return db.lookup(this.employee, "peers");
  }

  lookupManager() {
    return db.lookup(this.employee, "manager");
  }

  getPeerReviews() {
    const peers = this.lookupPeers();
    // ...
  }

  perfReview() {
    this.getPeerReviews();
    this.getManagerReview();
    this.getSelfReview();
  }

  getManagerReview() {
    const manager = this.lookupManager();
  }

  getSelfReview() {
    // ...
  }
}

const review = new PerformanceReview(employee);
review.perfReview();

Good:

class PerformanceReview {
  constructor(employee) {
    this.employee = employee;
  }

  perfReview() {
    this.getPeerReviews();
    this.getManagerReview();
    this.getSelfReview();
  }

  getPeerReviews() {
    const peers = this.lookupPeers();
    // ...
  }

  lookupPeers() {
    return db.lookup(this.employee, "peers");
  }

  getManagerReview() {
    const manager = this.lookupManager();
  }

  lookupManager() {
    return db.lookup(this.employee, "manager");
  }

  getSelfReview() {
    // ...
  }
}

const review = new PerformanceReview(employee);
review.perfReview();

⬆ back to top

Comments

コメント

Only comment things that have business logic complexity.

ビゞネスロゞックが耇雑な郚分にのみコメントを曞く

Comments are an apology, not a requirement. Good code mostly documents itself.

コメントは「蚀い蚳」であっお「必須」ではありたせん。良いコヌドは、ほずんどがコヌド自䜓で説明できるものです。

Bad:

function hashIt(data) {
  // The hash
  let hash = 0;

  // Length of string
  const length = data.length;

  // Loop through every character in data
  for (let i = 0; i < length; i++) {
    // Get character code.
    const char = data.charCodeAt(i);
    // Make the hash
    hash = (hash << 5) - hash + char;
    // Convert to 32-bit integer
    hash &= hash;
  }
}

Good:

function hashIt(data) {
  let hash = 0;
  const length = data.length;

  for (let i = 0; i < length; i++) {
    const char = data.charCodeAt(i);
    hash = (hash << 5) - hash + char;

    // Convert to 32-bit integer
    hash &= hash;
  }
}

⬆ back to top

Don’t leave commented out code in your codebase

コメントアりトしたコヌドをコヌドベヌスに残さない

Version control exists for a reason. Leave old code in your history.

バヌゞョン管理が存圚するのには理由がありたす。 叀いコヌドは履歎に残しおおけば十分です。

Bad:

doStuff();
// doOtherStuff();
// doSomeMoreStuff();
// doSoMuchStuff();

Good:

doStuff();

⬆ back to top

Don’t have journal comments

日蚘のようなコメントを曞かない

Remember, use version control! There’s no need for dead code, commented code, and especially journal comments. Use git log to get history!

バヌゞョン管理を䜿うこずを忘れないでください 䜿われおいないコヌドやコメントアりトされたコヌド、そしお特に日蚘のようなコメントは䞍芁です。

履歎が必芁なら、git log を䜿えば十分です。

Bad:

/**
 * 2016-12-20: Removed monads, didn't understand them (RM)
 * 2016-10-01: Improved using special monads (JP)
 * 2016-02-03: Removed type-checking (LI)
 * 2015-03-14: Added combine with type-checking (JR)
 */
function combine(a, b) {
  return a + b;
}

Good:

function combine(a, b) {
  return a + b;
}

⬆ back to top

Avoid positional markers

䜍眮取りのためのマヌカヌを䜿わない

They usually just add noise. Let the functions and variable names along with the proper indentation and formatting give the visual structure to your code.

それらはたいおい ただのノむズ を増やすだけです。

コヌドの芖芚的な構造は、関数名・倉数名・適切なむンデント・フォヌマットによっお十分に衚珟できたす。

Bad:

////////////////////////////////////////////////////////////////////////////////
// Scope Model Instantiation
////////////////////////////////////////////////////////////////////////////////
$scope.model = {
  menu: "foo",
  nav: "bar",
};

////////////////////////////////////////////////////////////////////////////////
// Action setup
////////////////////////////////////////////////////////////////////////////////
const actions = function () {
  // ...
};

Good:

$scope.model = {
  menu: "foo",
  nav: "bar",
};

const actions = function () {
  // ...
};

⬆ back to top

Translation

翻蚳

This is also available in other languages:

本ガむドは以䞋の蚀語にも翻蚳されおいたす

⬆ back to top