首頁 >web前端 >js教程 >現代 JavaScript 開發程式設計風格Idiomatic.js指南中文版_javascript技巧

現代 JavaScript 開發程式設計風格Idiomatic.js指南中文版_javascript技巧

WBOY
WBOY原創
2016-05-16 16:46:341465瀏覽

你為專案所擇風格都應為最高準則。作為一個描述放置於你的專案中,並連結到這個文件作為程式碼風格一致性、可讀性和可維護性的保證。

一、空白

1.永遠不要混用空格和Tab。
2.開始一個項目,在寫程式碼之前,選擇軟縮排(空格)或 Tab(作為縮排方式),並將其作為最高準則。
a).為了可讀, 我總是推薦在你的編輯中設計2個字母寬度的縮進 — 這等同於兩個空格或者兩個空格替代一個 Tab。
3.如果你的編輯器支持,請總是打開 “顯示不可見字元” 這個設定。好處是:
a).保證一致性
b).去掉行末的空格
c).去掉空行的空格
d).提交和對比更具可讀性

二、美化文法

A. 小括號, 花括號, 換行

複製代碼 代碼如下:

// if/else/for/while/try 通常都有小括號、花括號和多行
// 這有助於可讀

// 2.A.1.1
// 難辨語法(cramped syntax)的例子

if(condition) doSomething();

while(condition) iterating ;

for(var i=0;i

// 2.A.1.1
// 使用空格來提升可讀性

if ( condition ) {
  // 語句
}

while ( condition ) {
  // 語句
}

for ( var i = 0; i   // 語句
}

// 更好的做法:

var i,
  length = 100;

for ( i = 0; i   // 語句
}

// 或...

var i = 0,
  length = 100;

for ( ; i   // 語句
}

var prop;

for ( prop in object ) {
  // 語句
}

if ( true ) {
  // 語句
} else {
  // 語句
}


B. 賦值, 宣告, 函數( 命名函數, 函數表達式式, 建構函數)
複製程式碼 程式碼如下:

// 2.B.1. 🎜>// 變數
var foo = "bar",
  num = 1,
  undef;
// 字面量標示:

var array = [],
  object = {};

// 2.B.1.2

// 在一個作用域(函數)內只使用一個`var` 有助於提升可讀性
// 並且讓你的聲明列表變得有條不紊(還幫你省了幾次鍵盤敲擊)

// 不好

var foo = "";
var bar = "";
var qux;

// 好

var foo = "",
  bar = "",
  quux;

// 或..

var // 對這些變數的註解
foo = "",
bar = "",
quux;

// 2.B.1.3

// `var` 語句必須總是在各自作用域(函數)頂部
// 同樣適應於來自 ECMAScript 6 的常數

// 不好

function foo() {

  // 變數前有語句

  var bar = "",

    qux;
}

// 好

function foo() {
  var bar = "",
    qux;

  // 所有語句都在變數之後

}
// 2.B.2.1
// 命名函數宣告
function foo( arg1, argN ) {

}

// 使用方法

foo( arg1, argN );

// 2.B.2.2

// 命名函數宣告
function square( number ) {
  return number * number;
}

// 使用方法

square( 10 );

// 非常不自然的連帶傳參(continuation passing)風格

function square( number, callback ) {
  callback( number * number );
}

square( 10, function( square ) {

  // 回呼內容
});

// 2.B.2.3

// 函數表達式
var square = function( number ) {
  // 傳回有價值的、相關的內容
  return number * number;
};

// 帶標識符的函數表達式

// 這種首選形式有附加的函數讓其可以呼叫自身
// 並且在堆疊中有標識符
var factorial = function factorial ( number ) {
  if ( number     return 1;
  }

  return number * factorial( number-1 );

};

// 2.B.2.4

// 建構子宣告
function FooBar( options ) {

  this.options = options;

}

// 使用方法

var fooBar = new FooBar({ a: "alpha" });

fooBar.options;

// { a: "alpha" }

C. 異常, 細節


複製程式碼 程式碼如下:

// 2.C.1.1
// 帶迴調的函數
foo(function() {
  // 注意:第一函數呼叫的小括號和`function`處並沒有空格
});

// 函數接受 `array` 作為參數,沒有空格
foo([ "alpha", "beta" ]);

// 2.C.1.2
// 函數接受`object` 作為參數,沒有空格
foo({
  a: "alpha",
  b: "beta"
});

// 函數接受 `string` 字面量為參數,沒有空格
foo("bar");

// 分組用的小括號內部,沒有空格
if ( !("foo" in obj) ) {

}


D. 一致性(統一)總是笑到最後的(Consistency Always Wins)

在 2.A-2.C 節,留白作為一個推薦方式被提出,基於單純的、更高的目的:統一。值得注意的是格式化偏好,像是「內部留白」必須是可選的,但在整個專案的原始碼中必須只存在一種。

複製程式碼 程式碼如下:

// 2.D.1.1


// 2.D.1.1

if (condition) {

  // 語句
}

while (condition) {

  // 語句
}

for (var i = 0; i   // 語句
}

if (true) {
  // 語句
} else {
  // 語句
}

E. 引號

無論你選擇單引號還是雙引號都無所謂,在 JavaScript 中它們在解析上沒有區別。而絕對需要強制的是一致性。 永遠不要在同一個項目中混用兩種引號,選擇一種,並保持一致。

F. 行末和空白行

留白會破壞差異並使用變更不可讀。考慮包括一個預先提交的 hook 自動刪除行末和空白行中的空格。

三、型別偵測 (源自 jQuery Core Style Guidelines)


A. 直接型別(實際型,Actual Types)

String:複製程式碼

程式碼如下:


type vtype v. "
Number:複製程式碼

程式碼如下:


程式碼如下:複製程式碼

代碼如下:

代碼如下:typeof variable === "boolean"Object:
複製程式碼

程式碼如下:
🎜>
typeof variable === "object"Array:
複製程式碼


複製程式碼

複製程式碼複製程式碼

複製程式碼


複製程式碼 程式碼Array.isArray( arrayLikeObject )
(如果可能的話)

Node:


複製代碼 代碼如下:
elem.nodeType === 1

null:


複製代碼

程式碼如下:variable === null
null or undefined:

null or undefined:

複製程式碼 程式碼如下:
variable == null
undefined:

全域變數: 程式碼如下:局部變數:複製程式碼 程式碼如下: 程式碼如下: 代碼如下:屬性:複製程式碼 程式碼如下:

object.prop === undefined
object.hasOwnProperty( prop )
"prop" in object

B. 轉換類型(強制型,Coerced Types)考慮下面這個的意思...

給定的HTML:


複製程式碼 程式碼如下:
程式碼如下:

// 3.B.1.1


// `foo` 已被賦予值 `0`,型別為 `number`

var foo = 0;


// typeof foo;
// "number"

...

// 在後續的程式碼中,你需要更新 `foo`,賦予在 input 元素中得到的新值

foo = document.getElementById("foo-input").value;


// 如果你現在測試 `typeof foo`, 結果將是 `string`

// 這表示你在 if 語句偵測 `foo` 有類似此的邏輯:

if ( foo === 1 ) {

  importantTask();

}

// `importantTask()` 將永遠不會被執行,即使 `foo` 有一個值 "1"

// 3.B.1.2

// 你可以巧妙地使用 / - 一元運算子強制轉換類型以解決問題:


foo = document.getElementById("foo-input").value;

//    ^ 一元運算子將它右邊的運算子轉換為 `number`


// typeof foo;

// "number"

if ( foo === 1 ) {

  importantTask();

}

// `importantTask()` 將被調用

對於強制類型轉換這裡有幾個例子: 代碼如下:

// 3.B.2.1


var number = 1,
  string = "1",

  bool = false;


number;

// 1


number "";

// "1"


string;

// "1"


string;

// 1


string ;

// 1


string;

// 2


bool;

// false


bool;

// 0


bool "";
// "false"

// 3.B.2.2


var number = 1,
  string = "1",

  bool = true;


string === number;

// false


string === number "";

// true


string === number;

// true


bool === number;

// false


bool === number;

// true


bool === string;

// false


bool === !!string;
// true

// 3.B.2.3

var array = [ "a", "b", "c" ];


!!~array.indexOf("a");

// true


!!~array.indexOf("b");

// true


!!~array.indexOf("c");

// true


!!~array.indexOf("d");

// false


// 值得注意的是上述都是 "不必要的聰明"
// 採用明確的方案來比較傳回的值

// 如 indexOf:


if ( array.indexOf( "a" ) >= 0 ) {
  // ...
}

// 3.B.2.3

var num = 2.5;

parseInt( num, 10 );

// 等價於...

~~num;

num >> 0;

num >>> 0;

// 結果都是 2

// 時時牢記心底, 負值將被區別對待...

var neg = -2.5;

parseInt( neg, 10 );

// 等價於...

~~neg;

neg >> 0;


// 結果都是 -2

// 但是...

neg >>> 0;

// 結果即是 4294967294

四、對比運算

程式碼如下:

// 4.1.1
// 當只是判斷一個 array 是否有長度,相對於使用這個:
if ( array.length > 0 ) ...

// ...判斷真偽, 請使用這種:
if ( array.length ) ...

// 4.1.2
// 當只是判斷一個 array 是否為空,相對於使用這個:
if ( array.length === 0 ) ...

// ...判斷真偽, 請使用這種:
if ( !array.length ) ...

// 4.1.3
// 當只是判斷一個 string 是否為空,相對於使用這個:
if ( string !== "" ) ...

// ...判斷真偽, 請使用這種:
if ( string ) ...

// 4.1.4
// 當只是判斷一個 string 是為空,相對於使用這個:
if ( string === "" ) ...

// ...判斷真偽, 請使用這種:
if ( !string ) ...

// 4.1.5
// 當只是判斷一個引用是為真,相對於使用這個:
if ( foo === true ) ...

// ...判斷只需像你所想,享受內建功能的好處:
if ( foo ) ...

// 4.1.6
// 當只是判斷一個引用是為假,相對於使用這個:
if ( foo === false ) ...

// ...使用嘆號將其轉換為真
if ( !foo ) ...

// ...要注意的是:這個將會匹配0, "", null, undefined, NaN
// 如果你_必須_ 是布林類型的false,請這樣用:
if ( foo === false ) ...

// 4.1.7
// 如果想要計算一個引用可能是null 或undefined,但並不是false, "" 或0,
// 相對於使用這個:
if ( foo === null || foo === undefined ) ...

// ...享受 == 類型強制轉換的好處,像這樣:
if ( foo == null ) ...

// 謹記,使用== 將會令`null` 符合`null` 和`undefined`
// 但不是`false`,"" 或0
null == undefined

總是判斷最好、最精確的數值,上述是指南而非教條。

複製程式碼 代碼如下:

// 4.2.1

// 4.2.1

// 類型轉換和對比運算說明

// 首次 `===`,`==` 次之 (除非需要鬆散類型的對比)


// `===` 總是不做型別轉換,這表示:

"1" === 1;

// false


// `==` 會轉換類型,這表示:

"1" == 1;
// true

// 4.2.2
// 布爾, 真 & 偽

// 布林:
true, false

// 真:
"foo", 1

// 偽:

"", 0, null, undefined, NaN, void 0

五、實用風格複製程式碼

代碼如下:

代碼如下:
// 5.1.1

// 一個實用的模組

(function( global ) {

  var Module = (function() {

    var data = "secret";

    return {
      // 這是一個布林值
      bool: true,
     一個數組
array: [ 1, 2, 3, 4 ],
      // 一個物件
      object: {
        object: {
        object: {
      getData: function() {
        // 得到`data` 的值
        return data;
      },        return ( data = value );
      }
    };
  })();

  // 其他一些將會出現在這裡

  // 把你的模組變成全域物件
  global.Module = Module;

})( this );

// 5.2.1
// 一個實用的建構子

(function( global ) {

  function Ctor( foo ) {

    this.foo = foo;

    return this;
  }

  Ctor.prototype.getFoo = function() {
    return this.foo;
  };

  Ctor.prototype.setFoo = function( val ) {
    return ( this.foo = val );
  };

  // 不使用 `new` 來呼叫建構函數,你可能會這樣做:
  var ctor = function( foo ) {
    return new Ctor( foo );
  };

;

;
;

;

;

;;;;;;;;;;;;;;;;;;   // 把我們的建構函數變成全域物件  global.ctor = ctor; })( this );

六、命名

A. 你不是一個人肉 編譯器/壓縮器,所以試著去變身為其一。

下面的代碼是一個極糟命名的典範:

複製代碼 代碼如下:

// 6.A.1.1
// 糟糕命名的範例程式碼

function q(s) {
  return document.querySelectorAll(s);
}
var i,a=[],els=q("#foo");
for( i=0;i
毫無疑問,你寫過這樣的程式碼- 希望從今天它不再出現。

這裡有一個相同邏輯的程式碼,但擁有更健壯、貼切的命名(和一個可讀的結構):

複製程式碼


程式碼如下:


// 6.A.2.1
// 改善過命名的範例程式碼

function query( selector ) {
  return document.querySelectorAll( selector );
}

var idx = 0,

  elements = [],
  matches = query("#foo"),
  length = matches.length;

for ( ; idx   elements.push( matches[ idx ] );
}複製程式碼


程式碼如下:

// 6.A.3.1

// 命名字串
// 6.A.3.1

// 命名字串

`dog` 是一個 string


// 6.A.3.2

// 命名 arrays

`['dogs']` 是一個包含 `dog 字串的 array


// 6.A.3.3

// 命名函數、物件、實例,等

camlCase; function 和 var 聲明


// 6.A.3.4

// 命名建構器、原型,等

PascalCase; 建構函數


// 6.A.3.5

// 命名正規表示式


rDesc = //;

// 6.A.3.6
// 來自 Google Closure Library Style Guide

functionNamesLikeThis;

variableNamesLikeThis;
ConstructorNamesLikeThis;EnumNamesLikeThis;

methodNamesLikeThisSYTSS453; this

除使用眾所周知的 call 和 apply 外,總是優先選擇 .bind( this ) 或一個功能上等價於它的。建立 BoundFunction 聲明供後續調用,當沒有更好的選擇時才使用別名。 複製程式碼
程式碼如下:

// 6.B.1

// 6.B.1
function Device ( opts ) {

  this.value = null;

  // 新建一個非同步的 stream,這個會持續呼叫

  stream.read( opts.path, function( data ) {

    // 使用 stream 傳回 data 最新的值,更新實例的值
    this.value = data;

  }.bind(this) );

  // 控制事件觸發的頻率

  setInterval(function() {

    // 發出一個被控制的事件

    this.emit("event");

  }.bind(this), opts.freq || 100 );

}

複製程式碼

程式碼如下:

// 6.B.2

// 6.B.2

// 例:lodash/underscore,_.bind()

function Device( opts ) {

  this.value = null;

  stream.read( opts.path, _.bind(function( data ) {

    this.value = data;

  }, this) );


  setInterval(_.bind(function() {

    this.emit("event");

  }, this), opts.freq || 100 );

}

// 範例:jQuery.proxy

function Device( opts ) {

  this.value = null;

  stream.read( opts.path, jQuery.proxy(function( data ) {

    this.value = data;

  }, this) );


  setInterval( jQuery.proxy(function() {

    this.emit("event");

  }, this), opts.freq || 100 );

}

// 例:dojo.hitch

function Device( opts ) {

  this.value = null;

  stream.read( opts.path, dojo.hitch( this, function( data ) {

    this.value = data;

  }) );

  setInterval( dojo.hitch( this, function() {     this.emit("event");   }), opts.freq || 100 );}
提供一個候選,建立一個 this 的別名,以 self 作為標識符。這很有可能出 bug,應盡量避免。
複製程式碼 程式碼如下:

// 6.B.3


// 6.B.3

function Device( opts ) {

  var self = this;

  this.value = null;

  stream.read( opts.path, function( data ) {

    self.value = data;

  });

  setInterval(function() {


    self.emit("event");

  }, opts.freq || 100 );}


C. 使用 thisArg

好幾個ES 5.1 中的原型的方法都內建了一個特殊的thisArg 標記,盡可能多地使用它複製程式碼

複製程式碼

程式碼如下:

// 6.C.1

var obj;

obj = { f: "foo", b: "bar", q: "qux" };

Object.keys( obj ).forEach(function( key ) {

  // |this| 現在是 `obj`


  console.log( this[ key ] );

}, obj ); //
// 印出來...

// "foo"// "bar"// "qux"

thisArg 在Array.prototype.every、 Array.prototype.forEach、 Array.prototype .some、 Array.prototype.map、 Array.prototype.filter 中都可以使用。

七、Misc


這個部分將要說明的想法和理念都並非教條。相反更鼓勵對現存實踐保持好奇,以嘗試提供完成一般 JavaScript 程式設計任務的更好方案。

A. 避免使用 switch,現代方法追蹤(method tracing)將會把帶有 switch 表達式的函數列為黑名單。 似乎在最新版本的 Firefox 和 Chrome 都對 switch 語句有重大改進。 http://jsperf.com/switch-vs-object-literal-vs-module 值得注意的是,改進可以這裡看到: https://github.com/rwldrn/idiomatic.js/issues/13


複製程式碼


程式碼如下:


// 7.A.1.1
// switch 語句範例

switch( foo ) {
  case "alpha":
    alpha();
    break;

  case "beta":

   break;
  case "beta":
 >  default:

    // 預設分支

    break;

}


// 7.A.1.2
// 一個可支援組合、重用的方法是使用一個物件來儲存 “cases”,
// 使用一個 function 來做委派:

var cases, delegator;

// 回傳值僅供說明用
cases = {
  alpha: function() {
    // 語句
    // 一個回傳值 .length ];
  },
  beta: function() {
    // 語句
    // 一個回傳值
  > return [ "Beta  l. ,
  _default: function() {

    // 語句

    // 一個回傳值
    return [ "Default", arguments.length ];

delegator = function() {

  var args, key, delegate;

  // 把 `argument` 轉換成陣列

  args = [].slice.call( arguments );

  // 從 `argument` 中抽出最前一個值

  key = args.shift();

  // 呼叫預設分支

  delegate = cases._default;

  // 從物件中委派運算方法
  if ( cases.hasOwnProperty( key ) ) {

    delegate = cases[ key ];

  }

  // arg 的作用域可以設定成特定值,
  // 這種情況下,|null| 就可以了

  return delegate.apply( null, args );

};

// 7.A.1.3

// 使用 7.A.1.2 中的 API:

delegator( "alpha", 1, 2, 3, 4, 5 );

// [ "Alpha", 5 ]

// 當然 `case` key 的值可以輕鬆地換成任意值

var caseKey, someUserInput;

// 有沒有可能是某種形式的輸入?

someUserInput = 9;

if ( someUserInput > 10 ) {
  caseKey = "alpha";
} else {

  caseKey = "beta";

}

// 或...

caseKey = someUserInput > 10 ? "alpha" : "beta";

// 然後...

delegator( caseKey, someUserInput );

// [ "Beta", 1 ]

// 當然還可以這樣搞...

delegator();// [ "Default", 0 ]

B. 提前回傳值提升程式碼的可讀性且沒有太多效能上的差異

複製程式碼 程式碼如下:

// 7.B.1.1
// 不好:
function returnLate( foo ) {
  var ret;

  if ( foo ) {
    ret = "foo";
  } else {
    ret = "quux";
  }

// 好:

function returnEarly( foo ) {

  if ( foo ) {

    return "foo";

  }
  return "quux";
}

八、原生 & 宿主物件(註:其實一直覺得 Host Objects 真不應該翻譯過來,這是就照一般書的寫法翻出來吧)

最基本的原則是:

不要做任何蠢事,事情總是會變好的。

為了加強這個觀念,請觀看這個示範:

「一切都被允許: 原生擴充」 by Andrew Dupont (JSConf2011, Portland, Oregon)

http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542

九、註

單行註解放於程式碼上方為首選

多行也可以

行末註解應被避免!
JSDoc 的方式也不錯,但需要比較多的時間

十、單用一門語言

無論是什麼語言程式維護者(或團隊)規定使用何種語言,程式都應只用同一種語言書寫。

 

附錄

前置逗號(Comma First)

所有使用此文件作為基本風格指南的項目都不允許前置逗號的程式碼格式,除非明確指定或作者要求。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn