JavaScript規範

目次

  1. 型
  2. オブジェクト
  3. 配列
  4. 文字列
  5. 関数
  6. プロパティ
  7. 変数
  8. 条件式と等号
  9. 块
  10. コメント
  11. 空白
  12. カンマ
  13. セミコロン
  14. 型変換
  15. 命名規則
  16. アクセサ
  17. コンストラクタ
  18. イベント
  19. モジュール
  20. jQuery
  21. ES5互換性
  22. HTML、CSS、JavaScriptの分離
  23. jsHintを使用
  24. フロントエンドツール

型

  • プリミティブ値: 値渡しに相当します(JavaScriptのオブジェクトにはすべてリテラルが用意されています)。リテラルを使用してオブジェクトを作成してください。
    • string
    • number
    • boolean
    • null
    • undefined
var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9

複合型: 参照渡しに相当します

  • object
  • array
  • function
var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

オブジェクト

  • リテラルを使用してオブジェクトを作成してください。
    // bad
    var item = new Object();
    
    // good
    var item = {};
    
  • 予約語をreserved wordsキーとして使用しないでください。
    // bad
    var superman = {
      class: 'superhero',
      default: { clark: 'kent' },
      private: true
    };
    
    // good
    var superman = {
      klass: 'superhero',
      defaults: { clark: 'kent' },
      hidden: true
    };
    

配列

  • リテラルを使用して配列を作成してください。
    // bad
    var items = new Array();
    
    // good
    var items = [];
    
  • 配列の長さが不明な場合は、pushを使用してください。
    var someStack = [];
    
    // bad
    someStack[someStack.length] = 'abracadabra';
    
    // good
    someStack.push('abracadabra');
    
  • 配列をコピーする必要がある場合は、sliceを使用してください。jsPerf。
    var len = items.length,
        itemsCopy = [],
        i;
    
    // bad
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // good
    itemsCopy = items.slice();
    
  • sliceを使用して、配列のようなオブジェクトを配列に変換してください。
    function trigger() {
      var args = [].slice.apply(arguments);
      ...
    }
    

文字列

  • 文字列にはシングルクォートを使用してください。''(なぜなら、私たちの文字列はほとんどの場合、特にHTMLではダブルクォートが現れるからです)")。
    // bad
    var name = "Bob Parr";
    
    // good
    var name = 'Bob Parr';
    
    // bad
    var fullName = "Bob " + this.lastName;
    
    // good
    var fullName = 'Bob ' + this.lastName;
    
  • 80文字(140文字と規定する場合もあります。プロジェクトに応じて決められます)を超える文字列は、文字列連結で改行してください。
  • 注:過度に使用すると、長い文字列連結はパフォーマンスに影響を与える可能性があります。
    // bad
    var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
    
    // bad
    var errorMessage = 'This is a super long error that \
    was thrown because of Batman. \
    When you stop to think about \
    how Batman had anything to do \
    with this, you would get nowhere \
    fast.';
    
    // good
    var errorMessage = 'This is a super long error that ' +
      'was thrown because of Batman.' +
      'When you stop to think about ' +
      'how Batman had anything to do ' +
      'with this, you would get nowhere ' +
      'fast.';
    
  • 文字列を構築する際は、文字列連結ではなくjoinを使用してください。
    var items,
        messages,
        length, i;
    
    messages = [{
        state: 'success',
        message: 'This one worked.'
    },{
        state: 'success',
        message: 'This one worked as well.'
    },{
        state: 'error',
        message: 'This one did not work.'
    }];
    
    length = messages.length;
    
    // bad
    function inbox(messages) {
      items = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // good
    function inbox(messages) {
      items = [];
    
      for (i = 0; i < length; i++) {
        items[i] = messages[i].message;
      }
    
      return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
    }
    

関数

  • 関数式:
    // 匿名函数表达式
    var anonymous = function() {
      return true;
    };
    
    // 有名函数表达式
    var named = function named() {
      return true;
    };
    
    // 立即调用函数表达式
    (function() {
      console.log('Welcome to the Internet. Please follow me.');
    })();
    
  • 非関数ブロック内で関数を宣言しないでください。その関数は変数に代入してください。ブラウザはこれを許可していますが、解析方法が異なります。
  • 注:ECMA-262は「ブロック」を塊一連の文として定義しており、関数宣言は文ではありません。ECMA-262のこの問題に関する説明を読んでください。。
    // bad
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // good
    if (currentUser) {
      var test = function test() {
        console.log('Yup.');
      };
    }
    
  • パラメータを「arguments」と命名しないでください。arguments, これは関数スコープ内で渡されるarguments「arguments」オブジェクトを上書きすることになります。
    // bad
    function nope(name, options, arguments) {
      // ...stuff...
    }
    
    // good
    function yup(name, options, args) {
      // ...stuff...
    }
    

プロパティ

  • 変数や特別な不正な変数名を使用する場合、プロパティへのアクセスにはブラケット記法(.優先)を使用できます。
    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');
    

変数

  • 常に「var」を使用してvar変数を宣言してください。そうしないとグローバル変数が生成される原因になります。グローバル名前空間の汚染を避ける必要があります。
    // bad
    superPower = new SuperPower();
    
    // good
    var superPower = new SuperPower();
    
  • 1つの「var」を使用してvar改行で複数の変数を宣言し、4スペースでインデントしてください。
    // bad
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
    
  • 未代入の変数は最後に宣言します。これは、以前に代入した変数を参照したい場合に役立ちます。
    // bad
    var i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // bad
    var i, items = getItems(),
        dragonball,
        goSportsTeam = true,
        len;
    
    // good
    var items = getItems(),
        goSportsTeam = true,
        dragonball,
        length,
        i;
    
  • スコープの先頭で変数を宣言し、変数の宣言と代入に関連する問題を回避してください。
    // bad
    function() {
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      var name = getName();
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // good
    function() {
      var name = getName();
    
      test();
      console.log('doing stuff..');
    
      //..other stuff..
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // bad
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }
    
    // good
    function() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    

条件式と等号

  • 「==」を適切に使用し、===と!==および==と!=。
  • 「===」を式の論理演算とともに適切に使用してください。
  • 条件式の強制型変換は以下の規則に従います:
    • オブジェクトは「true」と評価されますtrue
    • Undefined「undefined」は「false」と評価されますfalse
    • Null「null」は「false」と評価されますfalse
    • ブール値はそのままのブール値と評価されますブールの値
    • 数値もし「+0」「-0」「NaN」の場合は+0, -0, or NaN「false」と評価されますfalse、それ以外は「true」ですtrue
    • 文字列空文字列「''」の場合は''「false」と評価されますfalse、それ以外は「true」ですtrue
      if ([0]) {
        // true
        // An array is an object, objects evaluate to true
      }
      
  • ショートカットを使用してください。
    // bad
    if (name !== '') {
      // ...stuff...
    }
    
    // good
    if (name) {
      // ...stuff...
    }
    
    // bad
    if (collection.length > 0) {
      // ...stuff...
    }
    
    // good
    if (collection.length) {
      // ...stuff...
    }
    
  • 参照Truth Equality and JavaScript詳細をご覧ください。

块

  • 複数行のブロックには中括弧を使用してください。
    // bad
    if (test)
      return false;
    
    // good
    if (test) return false;
    
    // good
    if (test) {
      return false;
    }
    
    // bad
    function() { return false; }
    
    // good
    function() {
      return false;
    }
    
  • 「/* */」を使用して/** ... */複数行コメントを記述し、説明、指定型、パラメータ値、戻り値を含めてください。
    // bad
    // make() returns a new element
    // based on the passed in tag name
    //
    // @param <String> tag
    // @return <Element> element
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
    // good
    /**
     * make() returns a new element
     * based on the passed in tag name
     *
     * @param <String> tag
     * @return <Element> element
     */
    function make(tag) {
    
      // ...stuff...
    
      return element;
    }
    
  • 「//」を使用して//単行コメントを記述し、コメント対象の上に置き、コメントの前に空行を入れます。
    // bad
    var active = true;  // is current tab
    
    // good
    // is current tab
    var active = true;
    
    // bad
    function getType() {
      console.log('fetching type...');
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
    // good
    function getType() {
      console.log('fetching type...');
    
      // set the default type to 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
  • 問題を再確認する必要がある場合や、実装が必要な解決策を提案する場合は、コメントの前に「TODO」または「FIXME」を追加してください。FIXME 或 TODO他の人がすぐに理解できるようにします。
    function Calculator() {
    
      // FIXME: shouldn't use a global here
      total = 0;
    
      return this;
    }
    
    function Calculator() {
    
      // TODO: total should be configurable by an options param
      this.total = 0;
    
      return this;
    }
    
  • 規範に沿ったドキュメントとして、ドキュメントが必要な場合は「jsDoc」を試してください。jsdoc。

空白

  • インデントやフォーマットは、チームがコードのバグをより早く特定・修正するのに役立ちます。
  • タブは4スペースに設定してください。
    // bad
    function() {
    ∙∙var name;
    }
    
    // bad
    function() {
    ∙var name;
    }
    
    // good
    function() {
    ∙∙∙∙var name;
    }
    
  • 中括弧の前にスペースを1つ入れます。
    // bad
    function test(){
      console.log('test');
    }
    
    // good
    function test() {
      console.log('test');
    }
    
    // bad
    dog.set('attr',{
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
    // good
    dog.set('attr', {
      age: '1 year',
      breed: 'Bernese Mountain Dog'
    });
    
  • 長いメソッドチェーンを使用する場合はインデントを使用してください。
    // bad
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // good
    $('#items')
      .find('.selected')
        .highlight()
        .end()
      .find('.open')
        .updateCount();
    
    // bad
    var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
        .attr('width',  (radius + margin) * 2).append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
    
    // good
    var leds = stage.selectAll('.led')
        .data(data)
      .enter().append('svg:svg')
        .class('led', true)
        .attr('width',  (radius + margin) * 2)
      .append('svg:g')
        .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
        .call(tron.led);
    

カンマ

  • カンマを先頭に置かないでください。
    // bad
    var once
      , upon
      , aTime;
    
    // good
    var once,
        upon,
        aTime;
    
    // bad
    var hero = {
        firstName: 'Bob'
      , lastName: 'Parr'
      , heroName: 'Mr. Incredible'
      , superPower: 'strength'
    };
    
    // good
    var hero = {
      firstName: 'Bob',
      lastName: 'Parr',
      heroName: 'Mr. Incredible',
      superPower: 'strength'
    };
    
  • 不要なカンマを追加しないでください。IEでエラーを引き起こす可能性があり、またカンマが1つ多いと一部のES3実装では配列の長さが1つ多く計算されます。
    // bad
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn',
    };
    
    var heroes = [
      'Batman',
      'Superman',
    ];
    
    // good
    var hero = {
      firstName: 'Kevin',
      lastName: 'Flynn'
    };
    
    var heroes = [
      'Batman',
      'Superman'
    ];
    

セミコロン

  • 文の最後には必ずセミコロンを付けてください。
    // bad
    (function() {
      var name = 'Skywalker'
      return name
    })()
    
    // good
    (function() {
      var name = 'Skywalker';
      return name;
    })();
    
    // good
    ;(function() {
      var name = 'Skywalker';
      return name;
    })();
    

型変換

  • 文の先頭で型変換を実行してください。
  • 文字列:
    //  => this.reviewScore = 9;
    
    // bad
    var totalScore = this.reviewScore + '';
    
    // good
    var totalScore = '' + this.reviewScore;
    
    // bad
    var totalScore = '' + this.reviewScore + ' total score';
    
    // good
    var totalScore = this.reviewScore + ' total score';
    
  • 数値には「String」を使用してください。parseIntまた、常に型変換の基数を指定してください。例:「parseInt」。parseInt(value, 10)。
  • var inputValue = '4';
    
    // bad
    var val = new Number(inputValue);
    
    // bad
    var val = +inputValue;
    
    // bad
    var val = inputValue >> 0;
    
    // bad
    var val = parseInt(inputValue);
    
    // good
    var val = Number(inputValue);
    
    // good
    var val = parseInt(inputValue, 10);
    
    // good
    /**
     * parseInt was the reason my code was slow.
     * Bitshifting the String to coerce it to a
     * Number made it a lot faster.
     */
    var val = inputValue >> 0;
    
  • ブール値:
    var age = 0;
    
    // bad
    var hasAge = new Boolean(age);
    
    // good
    var hasAge = Boolean(age);
    
    // good
    var hasAge = !!age;
    

命名規則

  • 1文字の名前を避け、変数名に説明的な意味を持たせてください。
    // bad
    function q() {
      // ...stuff...
    }
    
    // good
    function query() {
      // ..stuff..
    }
    
  • オブジェクト、関数、インスタンスに名前を付けるときは、キャメルケースを使用してください。
    // bad
    var OBJEcttsssss = {};
    var this_is_my_object = {};
    var this-is-my-object = {};
    function c() {};
    var u = new user({
      name: 'Bob Parr'
    });
    
    // good
    var thisIsMyObject = {};
    function thisIsMyFunction() {};
    var user = new User({
      name: 'Bob Parr'
    });
    
  • コンストラクタやクラスに名前を付けるときは、パスカルケース(先頭大文字のキャメルケース)を使用してください。
    // bad
    function user(options) {
      this.name = options.name;
    }
    
    var bad = new user({
      name: 'nope'
    });
    
    // good
    function User(options) {
      this.name = options.name;
    }
    
    var good = new User({
      name: 'yup'
    });
    
  • プライベートプロパティには先頭にアンダースコアを付けます。_:
    // bad
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    
    // good
    this._firstName = 'Panda';
    
  • 「this」への参照を保存する場合、this「self」を使用してください。self(python 风格)「that」を避けてください。this issue.Angularでは「$scope」の使用を推奨しています。vm(MVVM模式中view-model):
    // good
    function() {
      var self = this;
      return function() {
        console.log(self);
      };
    }
    

アクセサ

  • プロパティのアクセサ関数は必須ではありません。
  • アクセサ関数を実装する場合は、getVal() と setVal('hello') を使用してください。java getter、setter风格またはjQuery风格。
  • プロパティがブール値の場合は、isVal() または hasVal() を使用してください。
    // bad
    if (!dragon.age()) {
      return false;
    }
    
    // good
    if (!dragon.hasAge()) {
      return false;
    }
    
  • get() と set() 関数を作成してもかまいませんが、一貫性を保ってください。
    function Jedi(options) {
      options || (options = {});
      var lightsaber = options.lightsaber || 'blue';
      this.set('lightsaber', lightsaber);
    }
    
    Jedi.prototype.set = function(key, val) {
      this[key] = val;
    };
    
    Jedi.prototype.get = function(key) {
      return this[key];
    };
    

コンストラクタ

  • オブジェクトのプロトタイプにメソッドを割り当ててください。新しいオブジェクトでプロトタイプを上書きすると、継承に問題が発生します。
    function Jedi() {
      console.log('new jedi');
    }
    
    // bad
    Jedi.prototype = {
      fight: function fight() {
        console.log('fighting');
      },
    
      block: function block() {
        console.log('blocking');
      }
    };
    
    // good
    Jedi.prototype.fight = function fight() {
      console.log('fighting');
    };
    
    Jedi.prototype.block = function block() {
      console.log('blocking');
    };
    
  • メソッドは「this」を返すことができます。thisメソッドチェーンを可能にします。
    // bad
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return true;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
    };
    
    var luke = new Jedi();
    luke.jump(); // => true
    luke.setHeight(20) // => undefined
    
    // good
    Jedi.prototype.jump = function() {
      this.jumping = true;
      return this;
    };
    
    Jedi.prototype.setHeight = function(height) {
      this.height = height;
      return this;
    };
    
    var luke = new Jedi();
    
    luke.jump()
      .setHeight(20);
    
  • カスタムのtoString()メソッドを書いてもかまいませんが、正常に動作し、副作用がないことを確認してください。
    function Jedi(options) {
      options || (options = {});
      this.name = options.name || 'no name';
    }
    
    Jedi.prototype.getName = function getName() {
      return this.name;
    };
    
    Jedi.prototype.toString = function toString() {
      return 'Jedi - ' + this.getName();
    };
    

イベント

  • イベントにデータを添付するときは、プリミティブ値ではなくハッシュを渡してください。これにより、後から貢献する人がイベントハンドラを探して更新することなく、イベントデータにデータを追加できます。
    // bad
    $(this).trigger('listingUpdated', listing.id);
    
    ...
    
    $(this).on('listingUpdated', function(e, listingId) {
      // do something with listingId
    });
    

    より良い例:

    // good
    $(this).trigger('listingUpdated', { listingId : listing.id });
    
    ...
    
    $(this).on('listingUpdated', function(e, data) {
      // do something with data.listingId
    });
    

モジュール

  • このファイルはキャメルケースで命名し、同じ名前のフォルダ内に置き、エクスポート時も同じ名前にしてください。
  • 公開APIライブラリでは、noConflict() という名前のメソッドを追加して、エクスポートしたモジュールを以前のバージョンに設定し、それを返すことを検討してください。
  • 常にモジュールの先頭で「'use strict';」を宣言してください。'use strict';、導入する際は[JSHint规范](http://jshint.com/):
    // fancyInput/fancyInput.js
    
    (function(global) {
      'use strict';
    
      var previousFancyInput = global.FancyInput;
    
      function FancyInput(options) {
        this.options = options || {};
      }
    
      FancyInput.noConflict = function noConflict() {
        global.FancyInput = previousFancyInput;
        return FancyInput;
      };
    
      global.FancyInput = FancyInput;
    })(this);
    

jQuery

  • jQueryオブジェクトには先頭に「$」を付けて$、ネイティブDOMノードと区別してください。
    // bad
    var menu = $(".menu");
    
    // good
    var $menu = $(".menu");
    
  • jQueryのクエリ結果をキャッシュしてください。
    // bad
    function setSidebar() {
      $('.sidebar').hide();
    
      // ...stuff...
    
      $('.sidebar').css({
        'background-color': 'pink'
      });
    }
    
    // good
    function setSidebar() {
      var $sidebar = $('.sidebar');
      $sidebar.hide();
    
      // ...stuff...
    
      $sidebar.css({
        'background-color': 'pink'
      });
    }
    
  • DOMクエリにはカスケード方式の「$('.parent').find('.child')」を使用してください。$('.sidebar ul') 或 $('.sidebar ul'),jsPerf。
  • スコープ付きjQueryオブジェクトのクエリにはfind()を使用してください。find:
    // bad
    $('.sidebar', 'ul').hide();
    
    // bad
    $('.sidebar').find('ul').hide();
    
    // good
    $('.sidebar ul').hide();
    
    // good
    $('.sidebar > ul').hide();
    
    // good (slower)
    $sidebar.find('ul');
    
    // good (faster)
    $($sidebar[0]).find('ul');
    
  • 各ページでdocumentのreadyイベントは1回だけ使用してください。これにより、デバッグと動作フローの追跡が容易になります。
    $(function(){
       //do your page init.  
    });
    
  • イベントは「on」を利用してjQuery.onページからJavaScriptファイルに分離してください。
    // bad
    <a id="myLink" href="../index.html" onclick="myEventHandler();"></a>
    
    // good
    <a id="myLink" href="../index.html"></a>
    
    $("#myLink").on("click", myEventHandler);
    
  • Ajaxにはpromise方式を使用してください。
    // bad
        $.ajax({
            ...
            success : function(){
            },
            error : function(){
            } 
        })
    
        // good
        $.ajax({.
            ..
        }).then( function( ){
            // success
        }, function( ){
            // error
        })
    
  • promiseのdeferredオブジェクトを利用して、遅延登録の問題を解決してください。
    var dtd = $.Deferred(); // 新建一个deferred对象
      var wait = function(dtd){
        var tasks = function(){
          alert("执行完毕!");
          dtd.resolve(); // 改变deferred对象的执行状态
        };
        setTimeout(tasks,5000);
        return dtd;
      };
    
  • HTML内のStyle、およびJavaScript内のstyleをCSS内のclassに移し、HTML、JavaScriptではclassを導入し、直接styleを使わない。

ECMAScript 5互換性

できる限りES5メソッド、特に配列のmap、filter、forEachメソッドを採用して日常の開発を簡素化する。古いIEブラウザでは導入するES5-shim。あるいは、導入を検討することもできるunderscore、lodash常用の補助ライブラリ。
- 参考Kangaxの ES5compatibility table。

HTML、CSS、JavaScriptの分離

  • ページのDOM構造はHTMLを使用し、スタイルはCSSを採用し、動的なDOM操作はJavaScriptを使用する。HTML内で混用しないこと。
  • 異なるタイプのファイルに分離し、ファイルをリンクする。
  • HTML、CSS、JavaScriptの変数名はすべてビジネス上の価値を持つ必要がある。CSSはハイフンで区切られた全小文字で命名し、JavaScriptは先頭文字が小文字のキャメルケースで命名する。
  • CSSでは導入できるBootstrap、Foundationなどの有名なレスポンシブデザインフレームワーク。およびSASS、LESSツールでCSSを記述する。
  • CSS、JavaScriptについては単一ファイルに結合することを推奨し、Ajaxの接続数を減らす。AMD(Require.js)の読み込み方式を導入することもできる。
  • 内部の大部分の企業管理システムについては、フロントエンドの採用を試みることができるMVCフレームワークコードを組織化する。Angular、React + fluxアーキテクチャ、Knockoutなど。
  • 互換性については使用できるModernizr規範ライブラリの補助。

jsHintを使用

  • フロントエンドプロジェクトでは導入を推奨するjshintプラグインでプロジェクトのコーディング規約を標準化する。および完全なIDE設定一式。
  • 注意:jshintは、nodejsツールのgruntまたはgulpプラグインの導入が必要である。企業レベルのnodejs npmプライベートサーバーを推奨する。

フロントエンドツール

  • フロントエンドのサードパーティJavaScriptパッケージ管理ツールbower(bower install jQuery)、bowerはサードパーティライブラリの依存関係の解決、ダウンロード、アップグレード管理などを実現できる。企業レベルのbowerプライベートサーバーの構築を推奨する。
  • フロントエンド構築ツールは、gruntまたはgulpツールを採用でき、html、css、jsの圧縮、検証、テスト、ファイル結合、watchやliveloadなどすべてのフロントエンドタスクを実現できる。企業レベルのnodejs npmプライベートサーバーを推奨する。
  • フロントエンド開発IDE: WebStorm( Idea )、Sublimeが最適である。プロジェクトチームでIDEを統一する。IDEの統一設定が非常に重要である。

関連記事

出典:http://greengerong.github.io/blog/2015/05/09/qian-duan-javascriptgui-fan