JavaScriptコードのクリーンな道

9025 ワード

きれいなコードは快適に見えるだけではなく、いくつかの仕様に従って、コードを維持しやすくし、バグの確率を下げることができます.
1.配列の下付きを名前付きの変数で置換します.
// bad
const address = "One Infinite Loop, Cupertino 95014";
const cityZipCodeRegex = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(
  //   1,2     
  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);
2.関数のパラメータが一番いいです.
多くのパラメータがあるならば、objectを使って伝達し、解を使います.
3.一つの関数は一つのことしかしない.
メリットはcompose、test、and reason aboutです.
4.自分で原型を広げない
原型を拡張したいなら、まず引き継いでから方法を加えて汚染を防ぐことができます.
// 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));
  }
}
5.条件文の代わりに多形を使う
// bad
if (type === 'text') {
    // do something
} else if (type === 'select') {
    // do something else
}
個人がこのコードを書くのによく使われるのは、
const control = {
    text: {
        mapper() {},
        restore(){},
        name: 'this is a text field',
    },
    select: {
        mapper() {},
        restore(){},
        name: 'this is a select field',
    }
}
control[type].mapper();
実際には多状態です.classを使う方法も考えられます.
class Field {
    ...
}
class TextField extends Field {
    mapper(){}
    restore(){}
    name = 'this is a text field';
}
class SelectField extends Field {
    mapper(){}
    restore(){}
    name = 'this i```s a select field';
}
getterとsetter関数を使用します.
// bad
function makeBankAccount() {
  // ...
  return {
    balance: 0
    // ...
  };
}
const account = makeBankAccount();
account.balance = 100;
// good
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);
getterとsetterの中で多くのことをすることができます.一つ一つのbalanceを修正する必要はありません.
7.Prefer compsition over inherityance
なるべくグループで相続の代わりに、どのような場合に継承が使われますか?
Your inhearitance represents an“is-a”relatinship and not a“has-a”relatitionnship(Human->Animal vs.User->UserDetails).You can reuse code from the base clases(Humans can can mon momove clcleagggggggggggggggggclclclclclinininininininininininininininingggggggggclclclclclclclclclass.mamamamamamamamagggggggggggclclclclclass)))))))You ure of all animls when they move)
8.SOLID
Single Resonsibility Principle単一職責原則
The e e shoud never be more than one reason for a class to changeは、一つの種類が変更された原因の数はできるだけ低くするべきです.一つのクラスの中で、機能が多すぎると、その中の一点を修正すると、このクラスのモジュールを引用して受けた影響は計り知れません.
Open/Closed Principle開放閉鎖原則
ユーザーは、内部実装を修正せずに機能を自己拡張することができる.例えばHttpモジュールがありますが、内部は環境によってどのアドホットを使うかを判断します.ユーザーがアドホップを追加するなら、Httpモジュールを修正しなければなりません.
// 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
    });
  }
}
Liskov Substitution Principle内式置換原則
親類と子類は交換されて使っても大丈夫です.
// 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);
上記のRectangleはSquareを直接に置き換えることができません.計算面積が間違っているので、面積を計算する方法を抽象的に考えています.
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);
インターフェース分離原則
Clients shound not be forced to depend up on interfaces that they do use.例えば、機能モジュールは、必須のパラメータおよびオプションのパラメータを設計する必要があり、オプションのパラメータをユーザに強制的に使用させるべきではない.
Dependency Inversion Principle依存注入原則
// 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();
上記の例は、InventryTracker内部でInventry Requesterを実装していること、すなわちhigh-levelのモジュールはlow-levelモジュールの詳細を知る必要があることを意味している(例えば、実用化されたInventryRequesterはその構造パラメータを知る必要があるなど、あるいはimportこのモジュールを必要とし、結合を作る).
// 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();
low-levelに直接入ってきた例は、どのように実用化されるかを考える必要はなく、high-levelは抽象的なインターフェースに依存するだけでサブモジュールへの呼び出しを完了することができます.
9.コメント
Comments are an applogy,not a requirement.Good code mostly documents itself.良いコードは自己解釈です.
あなたはよくバグや他の問題に会います.落ち込んでしまうかもしれませんが、できるだけ冷静に、系統的に考えてください.実践を覚えることは問題を解決する一番いい方法です.
私たちが採集したのは石ですが、いつも未来の大聖堂を展望しなければなりません.
「ITPUBブログ」から、リンク:http://blog.itpub.net/69901074/viewspace-2639805/転載する場合は、出典を明記してください.そうしないと法律責任を追及します.
転載先:http://blog.itpub.net/69901074/viewspace-2639805/