用更合理的方式寫 JavaScript

吳青強 8年前發布 | 11K 次閱讀 編碼規范 JavaScript開發 JavaScript

類型

原始值: 存取直接作用于它自身。

var foo = 1;
var bar = foo;

bar = 9;

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

  • string
  • number
  • boolean
  • null
  • undefined

復雜類型: 存取時作用于它自身值的引用。

var foo = [1, 2];
var bar = foo;

bar[0] = 9;

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

  • object
  • array
  • function

對象

使用直接量創建對象。

// bad
var item = new Object();

// good var item = {};</code></pre>

不要使用保留字作為鍵名,它們在 IE8 下不工作。更多信息

// bad
var superman = {
  default: { clark: 'kent' },
  private: true
};

// good var superman = { defaults: { clark: 'kent' }, hidden: true };</code></pre>

使用同義詞替換需要使用的保留字。

// bad
var superman = {
  class: 'alien'
};

// bad var superman = { klass: 'alien' };

// good var superman = { type: 'alien' };</code></pre>

數組

使用直接量創建數組。

// bad
var items = new Array();

// good var items = [];</code></pre>

向數組增加元素時使用 Array#push 來替代直接賦值。

var someStack = [];

// bad someStack[someStack.length] = 'abracadabra';

// good someStack.push('abracadabra');</code></pre>

當你需要拷貝數組時,使用 Array#slice。jsPerf

var len = items.length;
var itemsCopy = [];
var i;

// bad for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; }

// good itemsCopy = items.slice();</code></pre>

使用 Array#slice 將類數組對象轉換成數組。

function trigger() {
  var args = Array.prototype.slice.call(arguments);
  ...
}

字符串

使用單引號 '' 包裹字符串。

// bad
var name = "Bob Parr";

// good var name = 'Bob Parr';

// bad var fullName = "Bob " + this.lastName;

// good var fullName = 'Bob ' + this.lastName;</code></pre>

超過 100 個字符的字符串應該使用連接符寫成多行。

注:若過度使用,通過連接符連接的長字符串可能會影響性能。jsPerf & 討論.

// 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.';</code></pre>

程序化生成的字符串使用 Array#join 連接而不是使用連接符。尤其是 IE 下:jsPerf.

var items;
var messages;
var length;
var 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++) { // use direct assignment in this case because we're micro-optimizing. items[i] = '<li>' + messages[i].message + '</li>'; }

return '<ul>' + items.join('') + '</ul>'; }</code></pre>

函數

函數表達式:

// 匿名函數表達式
var anonymous = function() {
  return true;
};

// 命名函數表達式 var named = function named() { return true; };

// 立即調用的函數表達式(IIFE) (function() { console.log('Welcome to the Internet. Please follow me.'); })();</code></pre>

永遠不要在一個非函數代碼塊(if、while 等)中聲明一個函數,把那個函數賦給一個變量。瀏覽器允許你這么做,但它們的解析表現不一致。

注: ECMA-262 把 定義為一組語句。函數聲明不是語句。閱讀對 ECMA-262 這個問題的說明

// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good var test; if (currentUser) { test = function test() { console.log('Yup.'); }; }</code></pre>

永遠不要把參數命名為 arguments。這將取代函數作用域內的 arguments 對象。

// bad
function nope(name, options, arguments) {
  // ...stuff...
}

// good function yup(name, options, args) { // ...stuff... }</code></pre>

屬性

使用 . 來訪問對象的屬性。

var luke = {
  jedi: true,
  age: 28
};

// bad var isJedi = luke['jedi'];

// good var isJedi = luke.jedi;</code></pre>

當通過變量訪問屬性時使用中括號 []

var luke = {
  jedi: true,
  age: 28
};

function getProp(prop) { return luke[prop]; }

var isJedi = getProp('jedi');</code></pre>

變量

總是使用 var 來聲明變量。不這么做將導致產生全局變量。我們要避免污染全局命名空間。

// bad
superPower = new SuperPower();

// good var superPower = new SuperPower();</code></pre>

使用 var 聲明每一個變量。 這樣做的好處是增加新變量將變的更加容易,而且你永遠不用再擔心調換錯 ;,

// bad
var items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad // (跟上面的代碼比較一下,看看哪里錯了) var items = getItems(), goSportsTeam = true; dragonball = 'z';

// good var items = getItems(); var goSportsTeam = true; var dragonball = 'z';</code></pre>

最后再聲明未賦值的變量。當你需要引用前面的變量賦值時這將變的很有用。

// bad
var i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad var i; var items = getItems(); var dragonball; var goSportsTeam = true; var len;

// good var items = getItems(); var goSportsTeam = true; var dragonball; var length; var i;</code></pre>

在作用域頂部聲明變量。這將幫你避免變量聲明提升相關的問題。

// 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() { var name = getName();

if (!arguments.length) { return false; }

this.setFirstName(name);

return true; }

// good function() { var name;

if (!arguments.length) { return false; }

name = getName(); this.setFirstName(name);

return true; }</code></pre>

提升

變量聲明會提升至作用域頂部,但賦值不會。

// 我們知道這樣不能正常工作(假設這里沒有名為 notDefined 的全局變量)
function example() {
  console.log(notDefined); // => throws a ReferenceError
}

// 但由于變量聲明提升的原因,在一個變量引用后再創建它的變量聲明將可以正常工作。 // 注:變量賦值為 true 不會提升。 function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; }

// 解釋器會把變量聲明提升到作用域頂部,意味著我們的例子將被重寫成: function example() { var declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; }</code></pre>

匿名函數表達式會提升它們的變量名,但不會提升函數的賦值。

function example() {
  console.log(anonymous); // => undefined

anonymous(); // => TypeError anonymous is not a function

var anonymous = function() { console.log('anonymous function expression'); }; }</code></pre>

命名函數表達式會提升變量名,但不會提升函數名或函數體。

function example() {
  console.log(named); // => undefined

named(); // => TypeError named is not a function

superPower(); // => ReferenceError superPower is not defined

var named = function superPower() { console.log('Flying'); }; }

// 當函數名跟變量名一樣時,表現也是如此。 function example() { console.log(named); // => undefined

named(); // => TypeError named is not a function

var named = function named() { console.log('named'); } }</code></pre>

函數聲明提升它們的名字和函數體。

function example() {
  superPower(); // => Flying

function superPower() { console.log('Flying'); } }</code></pre>

了解更多信息在 JavaScript Scoping & Hoisting by Ben Cherry.

比較運算符 & 等號

優先使用 ===!== 而不是 ==!=.

條件表達式例如 if 語句通過抽象方法 ToBoolean 強制計算它們的表達式并且總是遵守下面的規則:

if ([0]) {
  // true
  // 一個數組就是一個對象,對象被計算為 true
}
  • 對象 被計算為 true
  • Undefined 被計算為 false
  • Null 被計算為 false
  • 布爾值 被計算為 布爾的值
  • 數字 如果是 +0、-0 或 NaN 被計算為 false,否則為 true
  • 字符串 如果是空字符串 '' 被計算為 false,否則為 true

使用快捷方式。

// bad
if (name !== '') {
  // ...stuff...
}

// good if (name) { // ...stuff... }

// bad if (collection.length > 0) { // ...stuff... }

// good if (collection.length) { // ...stuff... }</code></pre>

了解更多信息在 Truth Equality and JavaScript by Angus Croll.

使用大括號包裹所有的多行代碼塊。

// bad
if (test)
  return false;

// good if (test) return false;

// good if (test) { return false; }

// bad function() { return false; }

// good function() { return false; }</code></pre>

如果通過 ifelse 使用多行代碼塊,把 else 放在 if 代碼塊關閉括號的同一行。

// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}

// good if (test) { thing1(); thing2(); } else { thing3(); } </code></pre>

注釋

使用 /** ... */ 作為多行注釋。包含描述、指定所有參數和返回值的類型和值。

// 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; }</code></pre>

    使用 // 作為單行注釋。在評論對象上面另起一行使用單行注釋。在注釋前插入空行。

    // 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; }</code></pre>

給注釋增加 FIXMETODO 的前綴可以幫助其他開發者快速了解這是一個需要復查的問題,或是給需要實現的功能提供一個解決方式。這將有別于常見的注釋,因為它們是可操作的。使用 FIXME -- need to figure this out 或者 TODO -- need to implement

使用 // FIXME: 標注問題。

function Calculator() {

// FIXME: shouldn't use a global here total = 0;

return this; }</code></pre>

使用 // TODO: 標注問題的解決方式。

function Calculator() {

// TODO: total should be configurable by an options param this.total = 0;

return this; }</code></pre>

空白

使用 2 個空格作為縮進。

// bad
function() {
????var name;
}

// bad function() { ?var name; }

// good function() { ??var name; }</code></pre>

在大括號前放一個空格。

// 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' });</code></pre>

在控制語句(ifwhile 等)的小括號前放一個空格。在函數調用及聲明中,不在函數的參數列表前加空格。

// bad
if(isJedi) {
  fight ();
}

// good if (isJedi) { fight(); }

// bad function fight () { console.log ('Swooosh!'); }

// good function fight() { console.log('Swooosh!'); }</code></pre>

使用空格把運算符隔開。

// bad
var x=y+5;

// good var x = y + 5;</code></pre>

在文件末尾插入一個空行。

// bad
(function(global) {
  // ...stuff...
})(this);
// bad
(function(global) {
  // ...stuff...
})(this);?
?
// good
(function(global) {
  // ...stuff...
})(this);?

在使用長方法鏈時進行縮進。使用前面的點 . 強調這是方法調用而不是新語句。

// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// 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').classed('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') .classed('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led);</code></pre>

在塊末和新語句前插入空行。

// bad
if (foo) {
  return bar;
}
return baz;

// good if (foo) { return bar; }

return baz;

// bad var obj = { foo: function() { }, bar: function() { } }; return obj;

// good var obj = { foo: function() { },

bar: function() { } };

return obj;</code></pre>

逗號

行首逗號: 不需要

// bad
var story = [
    once
  , upon
  , aTime
];

// good var story = [ 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' };</code></pre>

額外的行末逗號:不需要。這樣做會在 IE6/7 和 IE9 怪異模式下引起問題。同樣,多余的逗號在某些 ES3 的實現里會增加數組的長度。在 ES5 中已經澄清了 (source):

Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.

  // bad
  var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn',
  };

var heroes = [ 'Batman', 'Superman', ];

// good var hero = { firstName: 'Kevin', lastName: 'Flynn' };

var heroes = [ 'Batman', 'Superman' ];</code></pre>

分號

使用分號。

// bad
(function() {
  var name = 'Skywalker'
  return name
})()

// good (function() { var name = 'Skywalker'; return name; })();

// good (防止函數在兩個 IIFE 合并時被當成一個參數 ;(function() { var name = 'Skywalker'; return name; })();</code></pre>

了解更多.

類型轉換

在語句開始時執行類型轉換。

字符串:

//  => 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';</code></pre>

使用 parseInt 轉換數字時總是帶上類型轉換的基數。

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);</code></pre>

如果因為某些原因 parseInt 成為你所做的事的瓶頸而需要使用位操作解決性能問題時,留個注釋說清楚原因和你的目的。

// 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;</code></pre>

    注: 小心使用位操作運算符。數字會被當成 64 位值,但是位操作運算符總是返回 32 位的整數(source)。位操作處理大于 32 位的整數值時還會導致意料之外的行為。討論。最大的 32 位整數是 2,147,483,647:

    2147483647 >> 0 //=> 2147483647
    2147483648 >> 0 //=> -2147483648
    2147483649 >> 0 //=> -2147483647

    布爾:

    var age = 0;

// bad var hasAge = new Boolean(age);

// good var hasAge = Boolean(age);

// good var hasAge = !!age;</code></pre>

命名規則

避免單字母命名。命名應具備描述性。

// bad
function q() {
  // ...stuff...
}

// good function query() { // ..stuff.. }</code></pre>

使用駝峰式命名對象、函數和實例。

// bad
var OBJEcttsssss = {};
var this_is_my_object = {};
var o = {};
function c() {}

// good var thisIsMyObject = {}; function thisIsMyFunction() {}</code></pre>

使用帕斯卡式命名構造函數或類。

// 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' });</code></pre>

使用下劃線 _ 開頭命名私有屬性。

// bad
this.firstName = 'Panda';
this.firstName_ = 'Panda';

// good this._firstName = 'Panda';</code></pre>

使用 _this 保存 this 的引用。

// bad
function() {
  var self = this;
  return function() {
    console.log(self);
  };
}

// bad function() { var that = this; return function() { console.log(that); }; }

// good function() { var _this = this; return function() { console.log(_this); }; }</code></pre>

給函數命名。這在做堆棧軌跡時很有幫助。

// bad
var log = function(msg) {
  console.log(msg);
};

// good var log = function log(msg) { console.log(msg); };</code></pre>

注: IE8 及以下版本對命名函數表達式的處理有些怪異。了解更多信息到 http://kangax.github.io/nfe/

如果你的文件導出一個類,你的文件名應該與類名完全相同。

// file contents
class CheckBox {
  // ...
}
module.exports = CheckBox;

// in some other file // bad var CheckBox = require('./checkBox');

// bad var CheckBox = require('./check_box');

// good var CheckBox = require('./CheckBox');</code></pre>

存取器

屬性的存取函數不是必須的。

如果你需要存取函數時使用 getVal()setVal('hello')

// bad
dragon.age();

// good dragon.getAge();

// bad dragon.age(25);

// good dragon.setAge(25);</code></pre>

如果屬性是布爾值,使用 isVal()hasVal()

// bad
if (!dragon.age()) {
  return false;
}

// good if (!dragon.hasAge()) { return false; }</code></pre>

創建 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]; };</code></pre>

構造函數

給對象原型分配方法,而不是使用一個新對象覆蓋原型。覆蓋原型將導致繼承出現問題:重設原型將覆蓋原有原型!

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'); };</code></pre>

方法可以返回 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);</code></pre>

寫一個自定義的 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(); };</code></pre>

事件

當給事件附加數據時(無論是 DOM 事件還是私有事件),傳入一個哈希而不是原始值。這樣可以讓后面的貢獻者增加更多數據到事件數據而無需找出并更新事件的每一個處理器。例如,不好的寫法:

// bad
$(this).trigger('listingUpdated', listing.id);

...

$(this).on('listingUpdated', function(e, listingId) { // do something with listingId });</code></pre>

更好的寫法:

// good
$(this).trigger('listingUpdated', { listingId : listing.id });

...

$(this).on('listingUpdated', function(e, data) { // do something with data.listingId });</code></pre>

模塊

模塊應該以 ! 開始。這樣確保了當一個不好的模塊忘記包含最后的分號時,在合并代碼到生產環境后不會產生錯誤。詳細說明

文件應該以駝峰式命名,并放在同名的文件夾里,且與導出的名字一致。

增加一個名為 noConflict() 的方法來設置導出的模塊為前一個版本并返回它。

永遠在模塊頂部聲明 'use strict';

// 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);</code></pre>

jQuery

使用 $ 作為存儲 jQuery 對象的變量名前綴。

// bad
var sidebar = $('.sidebar');

// good var $sidebar = $('.sidebar');</code></pre>

緩存 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' }); }</code></pre>

對 DOM 查詢使用層疊 $('.sidebar ul') 或 父元素 > 子元素 $('.sidebar > ul')jsPerf

對有作用域的 jQuery 對象查詢使用 find

// bad
$('ul', '.sidebar').hide();

// bad $('.sidebar').find('ul').hide();

// good $('.sidebar ul').hide();

// good $('.sidebar > ul').hide();

// good $sidebar.find('ul').hide();</code></pre>

ECMAScript 5 兼容性

參考 Kangax 的 ES5 兼容表.

測試

Yup.

function() {
  return true;
}

來自:https://github.com/sivan/javascript-style-guide/blob/master/es5/README.md

 本文由用戶 吳青強 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!