優雅で耐久性のあるJavaScriptコードはどう書きますか?

12570 ワード

前言
私達の普段の業務開発の多くは大人が共同開発した公共プロジェクトです.私たちが普段開発しているコードコードコードコードコードコードコードコードコードコードコードコードコードコードの .
きれいなコードは品質の面で信頼できるだけでなく、後期メンテナンス、アップグレードのために良い基礎を打ち立てました.
以下の点から検討します.
変数
1、変数名
私たちは変数を定義するには、意味のある語彙コマンドを使います.
//bad code
const yyyymmdstr = moment().format('YYYY/MM/DD');
//better code
const currentDate = moment().format('YYYY/MM/DD');
2、説明できる
変数によって新しい変数が生成されます.この新しい変数に名前を付ける必要があります.つまり、各変数は彼の最初の目を見たら、彼が何をしているか分かります.
//bad code
const ADDRESS = 'One Infinite Loop, Cupertino 95014';
const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
saveCityZipCode(ADDRESS.match(CITY_ZIP_CODE_REGEX)[1], ADDRESS.match(CITY_ZIP_CODE_REGEX)[2]);

//better code
const ADDRESS = 'One Infinite Loop, Cupertino 95014';
const CITY_ZIP_CODE_REGEX = /^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;
const [, city, zipCode] = ADDRESS.match(CITY_ZIP_CODE_REGEX) || [];
saveCityZipCode(city, zipCode);
3、形参の命名
for、forEach、mapのサイクルの中で私達は命名する時直接に
//bad code
const locations = ['Austin', 'New York', 'San Francisco'];
locations.map((l) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  //             'l'      。
  dispatch(l);
});

//better code
const locations = ['Austin', 'New York', 'San Francisco'];
locations.forEach((location) => {
  doStuff();
  doSomeOtherStuff();
  // ...
  // ...
  // ...
  dispatch(location);
});
4、無意味なプレフィックスを避ける
例えば、オブジェクトを作成するだけで、オブジェクトごとの属性にオブジェクト名を追加する必要はありません.
//bad code
const car = {
  carMake: 'Honda',
  carModel: 'Accord',
  carColor: 'Blue'
};

function paintCar(car) {
  car.carColor = 'Red';
}

//better code
const car = {
  make: 'Honda',
  model: 'Accord',
  color: 'Blue'
};

function paintCar(car) {
  car.color = 'Red';
}
5、標準値
//bad code
function createMicrobrewery(name) {
  const breweryName = name || 'Hipster Brew Co.';
  // ...
}

//better code
function createMicrobrewery(name = 'Hipster Brew Co.') {
  // ...
}
関数
1、パラメータ
一般的にパラメータが多い場合はES 6の解凍式を使います.
//bad code
function createMenu(title, body, buttonText, cancellable) {
  // ...
}

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

//better code
createMenu({
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true
});
2、単一化処理
一つの方法の中で一番いいのは一つのことだけで、多すぎないように処理してください.このようなコードの可読性はとても高いです.
//bad code
function emailClients(clients) {
  clients.forEach((client) => {
    const clientRecord = database.lookup(client);
    if (clientRecord.isActive()) {
      email(client);
    }
  });
}

//better code
function emailActiveClients(clients) {
  clients
    .filter(isActiveClient)
    .forEach(email);
}
function isActiveClient(client) {
  const clientRecord = database.lookup(client);    
  return clientRecord.isActive();
}
3、オブジェクト設定のデフォルト属性
//bad code
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);


//better code
const menuConfig = {
  title: 'Order',
  // 'body' key   
  buttonText: 'Send',
  cancellable: true
};

function createMenu(config) {
  config = Object.assign({
    title: 'Foo',
    body: 'Bar',
    buttonText: 'Baz',
    cancellable: true
  }, config);

  // config     : {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}
  // ...
}

createMenu(menuConfig);
4、副作用を避ける
関数は、値を受信したら新しい値を返します.これ以外の行為は、グローバル変数の変更、ファイルのIO操作などの副作用と呼びます.
関数に副作用が必要な場合、例えばファイルをIO操作する場合、複数の関数/クラスでファイル操作しないでください.つまり副作用は唯一の場所で処理する必要があります.
副作用の三大ピット:可変データタイプを任意に修正し、データ構造がない状態を自由に共有し、副作用を統一的に処理していない.
//bad code
//            
//                ,          ,          。
var name = 'Ryan McDermott';
function splitIntoFirstAndLastName() {
  name = name.split(' ');
}
splitIntoFirstAndLastName();
console.log(name); // ['Ryan', 'McDermott'];


//better code
var name = 'Ryan McDermott';
var newName = splitIntoFirstAndLastName(name)

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

console.log(name); // 'Ryan McDermott';
console.log(newName); // ['Ryan', 'McDermott'];
JavaScriptでは、基本的なタイプは、賦値によって伝達され、オブジェクトと配列は参照によって伝達される.例として参照を渡す:
買い物車を書いたら、addItemToCart()方法で買い物車に商品を追加して、 を修正します.このとき、purchase()方法を呼び出して購入すると、引用伝達のために取得された は、ちょうど最新のデータである.
大丈夫そうですか?
ユーザーがクリックして購入した場合、ネットワークが故障し、purchase()方法が繰り返し起動していると同時に、ユーザーが新たな商品を追加した場合、ネットワークは再び回復した.purchase()方法で を取得するのはエラーです.
このような問題を避けるためには、商品を追加するたびに、クローン を新たに作成し、新しい配列に戻す必要があります.
//bad code
const addItemToCart = (cart, item) => {
  cart.push({ item, date: Date.now() });
};

//better code
const addItemToCart = (cart, item) => {
  return [...cart, {item, date: Date.now()}]
};
5、全体の方法
JavaScriptの中で、永遠に全体を汚染しないでください.生産環境の中で予想できないバグが発生します.例えば、Array.prototypediff方法を追加して、2つの配列の違いを判断します.あなたの同僚も同じようなことをするつもりですが、彼のdiff方法は2つの配列のトップ要素の違いを判断するためです.明らかにお宅の方法は衝突が発生します.このような問題に遭遇したら、ES 2015/ES 6の文法でArrayを拡張できます.
//bad code
Array.prototype.diff = function diff(comparisonArray) {
  const hash = new Set(comparisonArray);
  return this.filter(elem => !hash.has(elem));
};

//better code
class SuperArray extends Array {
  diff(comparisonArray) {
    const hash = new Set(comparisonArray);
    return this.filter(elem => !hash.has(elem));        
  }
}
6、型式検査を避ける
JavaScriptは無タイプで、任意のタイプのパラメータを伝えることができるという意味で、この自由度は人を困らせやすく、無意識にタイプをチェックしてしまいます.よく考えてみると、あなたは本当にタイプをチェックする必要がありますか?それともAPIのデザインに問題がありますか?
//bad code
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'));
  }
}

//better code
function travelToTexas(vehicle) {
  vehicle.move(this.currentLocation, new Location('texas'));
}
静的なタイプの検査が必要なら、文字列、整数など、Type Scriptを推奨します.そうでないと、あなたのコードは臭くて長いです.
//bad code
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');
}

//better code
function combine(val1, val2) {
  return val1 + val2;
}
複雑な条件判断
私達がjsコードを書く時、複雑な論理判断の場合がよくあります.if/elseまたはswitchで複数の条件の判断ができますが、このように問題があります.論理の複雑さが増すにつれて、コードの中のif/else/switchはますます太って見えなくなります.どうやってもっと優雅に判断ロジックを書きますか?
1、if/else
リストボタンのイベントをクリックします.
/**
 *       
 * @param {number} status     :1       2      3      4      5     
 */
const onButtonClick = (status)=>{
  if(status == 1){
    sendLog('processing')
    jumpTo('IndexPage')
  }else if(status == 2){
    sendLog('fail')
    jumpTo('FailPage')
  }else if(status == 3){
    sendLog('fail')
    jumpTo('FailPage')
  }else if(status == 4){
    sendLog('success')
    jumpTo('SuccessPage')
  }else if(status == 5){
    sendLog('cancel')
    jumpTo('CancelPage')
  }else {
    sendLog('other')
    jumpTo('Index')
  }
}
上から見ることができます.様々な状態でいろいろなことをしています.コードはとても面白くないです.このコードの書き換え案を簡単に提案できます.スイッチは登場します.
2、switch/case
/**
 *       
 * @param {number} status     :1       2      3      4      5     
 */
const onButtonClick = (status)=>{
  switch (status){
    case 1:
      sendLog('processing')
      jumpTo('IndexPage')
      break
    case 2:
    case 3:
      sendLog('fail')
      jumpTo('FailPage')
      break  
    case 4:
      sendLog('success')
      jumpTo('SuccessPage')
      break
    case 5:
      sendLog('cancel')
      jumpTo('CancelPage')
      break
    default:
      sendLog('other')
      jumpTo('Index')
      break
  }
}
このように見えるのはif/elseよりずっとはっきりしています.細心の注意深いクラスメートも小さい技術を発見しました.case 2とcase 3の論理が同じ時、文とbreakを実行しなくてもいいです.case 2の場合は自動的にcase 3のロジックを実行します.
3、Objectに保管する
判定条件を対象の属性名とし、処理ロジックを対象の属性値とし、ボタンをクリックしたときに対象属性を検索する方式で論理判定を行うという書き方は、特に一元条件判定に適しています.
const actions = {
  '1': ['processing','IndexPage'],
  '2': ['fail','FailPage'],
  '3': ['fail','FailPage'],
  '4': ['success','SuccessPage'],
  '5': ['cancel','CancelPage'],
  'default': ['other','Index'],
}
/**
 *       
 * @param {number} status     :1      2     3      4      5     
 */
const onButtonClick = (status)=>{
  let action = actions[status] || actions['default'],
      logName = action[0],
      pageName = action[1]
  sendLog(logName)
  jumpTo(pageName)
}
4、Mapに保管する
const actions = new Map([
  [1, ['processing','IndexPage']],
  [2, ['fail','FailPage']],
  [3, ['fail','FailPage']],
  [4, ['success','SuccessPage']],
  [5, ['cancel','CancelPage']],
  ['default', ['other','Index']]
])
/**
 *       
 * @param {number} status     :1       2      3      4      5     
 */
const onButtonClick = (status)=>{
  let action = actions.get(status) || actions.get('default')
  sendLog(action[0])
  jumpTo(action[1])
}
このように書いてes 6のMap対象を使ったほうがもっといいですか?MapオブジェクトとObjectオブジェクトの違いは何ですか?
  • オブジェクトは通常自分のプロトタイプがありますので、一つのオブジェクトにはいつも「プロトタイプ」キーがあります.
  • オブジェクトのキーは文字列またはSymboolsのみであるが、一つのMapのキーは任意の値であってもよい.
  • あなたはsize属性を通じて、Mapのキーの対数を簡単に得ることができますが、対象のキーの数は個数に対して手動で確認することができます.
  • コードスタイル
    通常の大文字
    //bad code
    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 {}
    
    //better code
    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 {}
    
    先に声明して呼び出します
    //bad code
    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();
    
    //better code
    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();