clean-code-javascript - 🛁 JavaScript に適合したクリーンコードの概念

(:bathtub: Clean Code concepts adapted for JavaScript)

Created at: 2016-11-26 06:25:41
Language: JavaScript
License: MIT

クリーンコードジャバスクリプト

目次

  1. 紹介
  2. 変数
  3. 関数
  4. オブジェクトとデータ構造
  5. クラス
  6. 固体
  7. テスティング
  8. 並行 処理
  9. エラー処理
  10. フォーマッティング
  11. コメント
  12. 翻訳

紹介

コードを読むときに叫ぶ罵倒の数としてのソフトウェア品質推定のユーモラスなイメージ

ソフトウェア工学の原則、ロバートC.マーティンの著書「クリーンコード」から、 JavaScriptに適応しました。これはスタイルガイドではありません。これは、JavaScriptで読み取り可能で、再利用可能で、リファクタリング可能なソフトウェアを作成するためのガイドです。

ここにあるすべての原則に厳密に従う必要はなく、さらに少なくなります 普遍的に合意されました。これらはガイドラインであり、それ以上のものではありませんが、 クリーンコードの作者による長年の集合的な経験にわたって成文化されたもの。

私たちのソフトウェアエンジニアリングの技術は50年強前のものであり、私たちは まだたくさん学んでいます。ソフトウェアアーキテクチャがアーキテクチャと同じくらい古い場合 それ自体、多分それから私たちは従うべきより難しい規則を持つでしょう。今のところ、これらをしましょう ガイドラインは、の品質を評価するための試金石として機能します あなたとあなたのチームが作成する JavaScript コード。

もう1つ:これらを知っていてもすぐにはより良いソフトウェアになるわけではありません 開発者、そして長年彼らと協力することはあなたが作らないという意味ではありません 間違い。すべてのコードは、湿った粘土のように、最初のドラフトとして始まります 最終的な形に形作られました。最後に、次のときに欠陥を彫り落とします 私たちは仲間と一緒にそれをレビューします。必要な最初のドラフトで自分を殴らないでください 改善。代わりにコードを打ち負かしてください!

変数

意味のある発音可能な変数名を使用する

悪い:

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

よし:

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

⬆ 一番上に戻る

同じタイプの変数に同じ語彙を使用する

悪い:

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

よし:

getUser();

⬆ 一番上に戻る

検索可能な名前を使用する

これまでに書くよりも多くのコードを読みます。私たちが 書き込みは読み取り可能で検索可能です。最終的に変数に名前を付けないことによって 私たちのプログラムを理解するために意味があるので、私たちは読者を傷つけます。 名前を検索可能にします。buddy.js や ESLint などのツールは、名前のない定数を識別するのに役立ちます。

悪い:

// What the heck is 86400000 for?
setTimeout(blastOff, 86400000);

よし:

// Declare them as capitalized named constants.
const MILLISECONDS_PER_DAY = 60 * 60 * 24 * 1000; //86400000;

setTimeout(blastOff, MILLISECONDS_PER_DAY);

⬆ 一番上に戻る

説明変数の使用

悪い:

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

よし:

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

⬆ 一番上に戻る

メンタルマッピングを避ける

明示的は暗黙的よりも優れています。

悪い:

const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(l => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  // Wait, what is `l` for again?
  dispatch(l);
});

よし:

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

⬆ 一番上に戻る

不要なコンテキストを追加しない

あなたのクラス/オブジェクト名があなたに何かを教えてくれるなら、あなたの中でそれを繰り返さないでください 変数名。

悪い:

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

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

よし:

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

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

⬆ 一番上に戻る

短絡や条件の代わりにデフォルトのパラメータを使用する

デフォルトのパラメータは、多くの場合、短絡よりもクリーンです。次のことに注意してください それらを使用すると、関数は引数のデフォルト値のみを提供します。、、などの他の「ファルシー」値は、デフォルト値に置き換えられません。

undefined
''
""
false
null
0
NaN

悪い:

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

よし:

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

⬆ 一番上に戻る

関数

関数の引数 (理想的には 2 つ以下)

関数パラメータの量を制限することは、次の理由で非常に重要です。 関数のテストが簡単になります。3つ以上を持つことは、 さまざまなケースを大量にテストする必要がある組み合わせ爆発 それぞれの別々の引数。

1つまたは2つの引数が理想的なケースであり、可能であれば3つの引数は避ける必要があります。 それ以上のものはすべて統合する必要があります。通常、あなたが持っている場合 2つ以上の引数がある場合、あなたの関数はあまりにも多くのことをしようとしています。場合 そうでない場合、ほとんどの場合、上位レベルのオブジェクトで十分です。 引数。

JavaScriptを使用すると、多くのクラスなしでその場でオブジェクトを作成できます 定型文、あなたは自分自身が必要だと思っているならオブジェクトを使うことができます たくさんの議論。

関数が期待するプロパティを明確にするために、ES2015 / ES6を使用できます 構文の破壊。これにはいくつかの利点があります。

  1. 誰かが関数シグネチャを見ると、すぐに何が プロパティが使用されています。
  2. 名前付きパラメーターをシミュレートするために使用できます。
  3. 分解すると、引数の指定されたプリミティブ値も複製されます オブジェクトが関数に渡されました。これは副作用を防ぐのに役立ちます。手記: 引数オブジェクトから構造化解除されたオブジェクトと配列は NOT です クローン。
  4. リンターは未使用のプロパティについて警告することができますが、これは不可能です 破壊せずに。

悪い:

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

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

よし:

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

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

⬆ 一番上に戻る

関数は1つのことを行う必要があります

これは、ソフトウェアエンジニアリングにおいて群を抜いて最も重要なルールです。関数が機能する場合 複数のことを行うと、構成、テスト、および推論が難しくなります。 関数を 1 つのアクションだけに分離できる場合は、リファクタリングできます。 簡単に、あなたのコードははるかにきれいに読むでしょう。あなたが他に何も奪わないなら このガイド これ以外に、あなたは多くの開発者に先んじるでしょう。

悪い:

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

よし:

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

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

⬆ 一番上に戻る

関数名は、その機能を示す必要があります

悪い:

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

const date = new Date();

// It's hard to tell from the function name what is added
addToDate(date, 1);

よし:

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

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

⬆ 一番上に戻る

関数は1つのレベルの抽象化のみである必要があります

複数の抽象化レベルがある場合、関数は通常 やりすぎです。機能を分割することで、再利用性と使いやすさにつながります テスティング。

悪い:

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

よし:

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

⬆ 一番上に戻る

重複するコードを削除する

コードの重複を避けるために最善を尽くしてください。重複コードは悪いです 変更する必要がある場合に何かを変更する場所が複数あることを意味します いくつかのロジック。

あなたがレストランを経営していて、あなたがあなたの在庫を追跡していると想像してみてください:すべてのあなたの トマト、玉ねぎ、ニンニク、スパイスなど複数のリストがある場合 あなたはこれをオンに保ちます、そしてあなたが料理を出すときすべてが更新されなければなりません それらの中のトマト。リストが 1 つしかない場合は、更新する場所が 1 つだけです。

多くの場合、2つ以上のコードがわずかにあるため、コードが重複しています 多くの共通点を共有するさまざまなもの、しかしそれらの違いはあなたを強制します ほとんど同じことを行う2つ以上の別々の関数を持つこと。削除 重複するコードは、このセットを処理できる抽象化を作成することを意味します 1つの関数/モジュール/クラスだけで異なるもの。

抽象化を正しく行うことが重要であるため、 クラスセクションに示されているSOLIDの原則。悪い抽象化は 重複するコードよりも悪いので、注意してください!そうは言っても、あなたが作ることができれば 良い抽象化、それをしてください!繰り返さないでください、さもなければあなたは自分自身を見つけるでしょう 1つのことを変更したいときはいつでも複数の場所を更新します。

悪い:

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

よし:

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

⬆ 一番上に戻る

オブジェクトで既定のオブジェクトを設定する。

悪い:

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

よし:

const menuConfig = {
  title: "Order",
  // User did not include 'body' key
  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}
  // ...
}

createMenu(menuConfig);

⬆ 一番上に戻る

フラグを関数パラメーターとして使用しないでください

フラグは、この関数が複数のことを行うことをユーザーに通知します。関数は1つのことを行う必要があります。関数がブール値に基づいて異なるコードパスをたどっている場合は、関数を分割します。

悪い:

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

よし:

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

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

⬆ 一番上に戻る

副作用を避ける(パート1)

関数は、値を取る以外のことをすると副作用を生成します 別の値を返します。副作用は、ファイルへの書き込みである可能性があります。 グローバル変数を変更するか、誤ってすべてのお金を 他人。

今、あなたは時々プログラムで副作用を持つ必要があります。前のように たとえば、ファイルに書き込む必要がある場合があります。あなたがしたいことは これを行う場所を一元化します。複数の関数やクラスを持たない 特定のファイルに書き込むこと。それを行うサービスを1つ用意してください。唯一無二。

主なポイントは、オブジェクト間で状態を共有するなどの一般的な落とし穴を回避することです 構造なしで、何にでも書き込むことができる可変データ型を使用して、 副作用が発生する場所を一元化しません。あなたがこれを行うことができれば、あなたは 他の大多数のプログラマーよりも幸せになりましょう。

悪い:

// 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'];

よし:

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

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

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

⬆ 一番上に戻る

副作用を避ける(パート2)

JavaScript では、変更できない (不変) 値と変更可能な値があります。 (変更可能)。オブジェクトと配列は2種類の変更可能な値なので重要です 関数にパラメーターとして渡されるときに慎重に処理します。ある JavaScript 関数は、オブジェクトのプロパティを変更したり、 他の場所で簡単にバグを引き起こす可能性のある配列。

を表す配列パラメータを受け取る関数があるとします。 ショッピングカート。関数がそのショッピングカート配列に変更を加えた場合 - たとえば、購入するアイテムを追加することで、次に他の関数を追加します。 同じ配列を使用すると、この追加の影響を受けます。それは 素晴らしいですが、悪い可能性もあります。悪い状況を想像してみましょう:

cart

ユーザーは、次の関数を呼び出す「購入」ボタンをクリックします。 ネットワーク要求を生成し、アレイをサーバーに送信します。というのは ネットワーク接続が悪い場合、関数は再試行を続ける必要があります 依頼。さて、その間にユーザーが誤って「カートに追加」をクリックした場合はどうなりますか? ネットワーク要求が始まる前に、実際には必要のないアイテムのボタンですか? それが起こり、ネットワーク要求が開始された場合、その購入機能 配列が変更されたため、誤って追加されたアイテムを送信します。

purchase
cart
purchase
cart

優れた解決策は、関数が常にクローンを作成し、編集し、クローンを返すことです。これにより、まだ機能している 古いショッピング カートを使用しても、変更の影響を受けません。

addItemToCart
cart

このアプローチに言及すべき2つの注意点:

  1. 実際に入力オブジェクトを変更したい場合があるかもしれませんが、 しかし、このプログラミング手法を採用すると、これらのケースが かなりまれです。ほとんどのものは、副作用がないようにリファクタリングできます!

  2. 大きなオブジェクトのクローン作成は、パフォーマンスの点で非常にコストがかかる可能性があります。おかげさまで これは実際には大きな問題ではありません この種のプログラミングアプローチは、高速で、メモリを大量に消費しません オブジェクトと配列を手動で複製します。

悪い:

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

よし:

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

⬆ 一番上に戻る

グローバル関数に書き込まない

グローバルを汚染することは、他のグローバルと衝突する可能性があるため、JavaScriptでは悪い習慣です ライブラリとあなたのAPIのユーザーは、彼らが得るまで賢明ではないでしょう 本番環境では例外です。例を考えてみましょう:あなたがしたい場合はどうなりますか JavaScript のネイティブ配列メソッドを拡張して、次のようなメソッドを持つことができます。 2つの配列の違いを表示しますか?あなたはあなたの新しい関数を書くことができます に、しかしそれは試みた別のライブラリと衝突する可能性があります 同じことをする。その他のライブラリが検索に使用していた場合はどうなりますか 配列の最初と最後の要素の違いは?これが理由です ES2015 / ES6クラスを使用し、単にグローバルを拡張する方がはるかに優れています。

diff
Array.prototype
diff
Array

悪い:

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

よし:

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

⬆ 一番上に戻る

命令型プログラミングよりも関数型プログラミングを優先する

JavaScriptは、Haskellのような関数型言語ではありませんが、 それに機能的な風味。関数型言語は、よりクリーンでテストが簡単です。 可能な場合は、このスタイルのプログラミングを優先します。

悪い:

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

よし:

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

⬆ 一番上に戻る

条件文のカプセル化

悪い:

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

よし:

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

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

⬆ 一番上に戻る

負の条件付きを避ける

悪い:

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

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

よし:

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

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

⬆ 一番上に戻る

条件文を避ける

これは不可能な作業のようです。これを最初に聞いたとき、ほとんどの人はこう言います。 「声明なしで何かをすることになっているのですか?」答えは、 多くの場合、ポリモーフィズムを使用して同じタスクを実行できます。2番目 質問は通常、「それは素晴らしいことですが、なぜ私はそれをしたいのですか?」です。ザ 答えは私たちが学んだ以前のクリーンコードの概念です:関数は唯一のことをするべきです 一つ。ステートメントを持つクラスと関数がある場合は、 あなたの関数が複数のことをすることをあなたのユーザーに伝えています。思い出す ただ一つのことをしてください。

if
if

悪い:

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

よし:

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

⬆ 一番上に戻る

型チェックを避ける (パート 1)

JavaScript は型指定されていないため、関数は任意の型の引数を受け取ることができます。 時々あなたはこの自由に噛まれ、それをしたくなる 関数の型チェック。これを行う必要がないようにする方法はたくさんあります。 最初に考慮すべきことは、一貫性のある API です。

悪い:

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

よし:

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

⬆ 一番上に戻る

型チェックを避ける (パート 2)

文字列や整数などの基本的なプリミティブ値を操作している場合は、 ポリモーフィズムを使用することはできませんが、それでも型チェックの必要性を感じています。 TypeScript の使用を検討する必要があります。それは通常の優れた代替品です JavaScriptは、標準のJavaScriptの上に静的な型付けを提供するためです。 構文。通常のJavaScriptを手動で型チェックすることの問題は、 それをうまくやるには、あなたが得る偽の「タイプセーフ」ほど多くの余分な言い回しが必要です 失われた読みやすさを補うものではありません。あなたのJavaScriptをきれいに保ち、書いてください 良いテストがあり、良いコードレビューがあります。それ以外の場合は、そのすべてを実行しますが、 TypeScript(私が言ったように、これは素晴らしい代替手段です!

悪い:

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

よし:

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

⬆ 一番上に戻る

過度に最適化しないでください

最新のブラウザは、実行時に内部で多くの最適化を行います。たくさんの 時間、あなたが最適化しているなら、あなたはあなたの時間を無駄にしているだけです。良いものがあります 最適化が不足している場所を確認するためのリソース。その間、それらまでターゲットにします 可能であれば修正されます。

悪い:

// On old browsers, each iteration with uncached `list.length` would be costly
// because of `list.length` recomputation. In modern browsers, this is optimized.
for (let i = 0, len = list.length; i < len; i++) {
  // ...
}

よし:

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

⬆ 一番上に戻る

デッドコードを削除する

デッドコードは重複コードと同じくらい悪いです。それを保持する理由はありません あなたのコードベース。呼び出されていない場合は、それを取り除きます!それはまだ安全です それでも必要な場合は、バージョン履歴に。

悪い:

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

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

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

よし:

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

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

⬆ 一番上に戻る

オブジェクトとデータ構造

ゲッターとセッターを使用する

ゲッターとセッターを使用してオブジェクトのデータにアクセスすることは、単に オブジェクトのプロパティを探しています。「なぜ?」とあなたは尋ねるかもしれません。さて、ここに 理由の整理されていないリスト:

  • オブジェクトプロパティを取得する以上のことをしたい場合は、 をクリックして、コードベース内のすべてのアクセサーを検索して変更します。
  • を実行するときに検証を簡単に追加できます。
    set
  • 内部表現をカプセル化します。
  • 取得および設定時にロギングとエラー処理を簡単に追加できます。
  • オブジェクトのプロパティを遅延ロードすることができます。 サーバー。

悪い:

function makeBankAccount() {
  // ...

  return {
    balance: 0
    // ...
  };
}

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

よし:

function makeBankAccount() {
  // this one is private
  let balance = 0;

  // a "getter", made public via the returned object below
  function getBalance() {
    return balance;
  }

  // a "setter", made public via the returned object below
  function setBalance(amount) {
    // ... validate before updating the balance
    balance = amount;
  }

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

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

⬆ 一番上に戻る

オブジェクトにプライベート メンバーを持たせる

これは、クロージャ(ES5以下)によって実現できます。

悪い:

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

よし:

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

⬆ 一番上に戻る

クラス

ES5プレーン関数よりもES2015 / ES6クラスを優先する

読みやすいクラスの継承、構築、メソッドを取得することは非常に困難です 古典的なES5クラスの定義。継承が必要な場合(および注意してください あなたがそうではないかもしれない)、そしてES2015 / ES6クラスを好む。ただし、よりも小さな機能を優先します クラスは、より大きく、より複雑なオブジェクトが必要になるまでです。

悪い:

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

よし:

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

⬆ 一番上に戻る

メソッドチェーンを使用する

このパターンはJavaScriptで非常に便利であり、次のような多くのライブラリで見られます。 jQueryとLodashとして。これにより、コードを表現力豊かにし、冗長性を低くすることができます。 そのため、メソッドチェーンを使用して、コードがどれほどクリーンであるかを見てみましょう となります。あなたのクラス関数では、単にすべての関数の最後に戻る、 さらにクラスメソッドをチェーンすることもできます。

this

悪い:

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

よし:

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

⬆ 一番上に戻る

継承よりも構成を優先する

4人のギャングによるデザインパターンで有名に述べられているように、 可能な場合は、継承よりも構成を優先する必要があります。たくさんあります 継承を使用する正当な理由と、コンポジションを使用する多くの正当な理由。 この格言の主なポイントは、あなたの心が本能的に 継承、構成があなたの問題をよりよくモデル化できるかどうかを考えてみてください。いくつかでは できるケース。

それでは、「いつ継承を使うべきか」と疑問に思われるかもしれません。それ 目前の問題に依存しますが、これは継承時のまともなリストです 構成よりも理にかなっています:

  1. あなたの相続は「has-a」ではなく「is-a」関係を表します 関係 (人間と>動物 vs. ユーザー>ユーザーの詳細)。
  2. 基本クラスのコードを再利用できます(人間はすべての動物のように動くことができます)。
  3. 基本クラスを変更して派生クラスにグローバルな変更を加える場合。 (移動時にすべての動物のカロリー消費量を変更します)。

悪い:

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

  // ...
}

// Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
class EmployeeTaxData extends Employee {
  constructor(ssn, salary) {
    super();
    this.ssn = ssn;
    this.salary = salary;
  }

  // ...
}

よし:

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);
  }
  // ...
}

⬆ 一番上に戻る

固体

Single Responsibility Principle (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.

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)

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.

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)

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.

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.

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);
    const area = rectangle.getArea(); // BAD: Returns 25 for Square. Should be 20.
    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)

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.

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.

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".

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"),
  animationModule() {} // Most of the time, we won't need to animate when traversing.
  // ...
});

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)

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.

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.

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 will have a method.

InventoryTracker
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`
    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.
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.

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.

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

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

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

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 keyword, and then you can write your logic imperatively without a chain of functions. Use this if you can take advantage of ES2017/ES8 features today!

async
then

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.

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

For the same reason you shouldn't ignore caught errors from .

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

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.

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.

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

これは他の言語でも利用可能です:

⬆ 一番上に戻る