Javascript Patterns

About Javascript Patterns , you can see this github:

http://shichuan.github.com/javascript-patterns/

Issue1:Function Declarations

在談談這個主題之前,我想要先了解js架構:

function a(){
    alert('123');
}
console.log(a);

這時候如果把console.log(a)這行搬到第一行時,也就是如下:

console.log(a);
function a(){
    alert('123');
}

他仍輸出一樣的結果,原因是js在讀取時會先把所有funciton先讀,然後再讀其他的東西.

不夠清楚?

在舉個例子,如果今天出現如下狀況:

console.log(a.toString());
function a(){
    alert('123');
}
function a(){
    alert('567');
}

則出來的結果是:

function a(){
    alert('456');
}
[Finished in 0.1s]

也就是說 如果遇到兩個都是相同命名的function時,後面的會覆蓋掉前面的同名function.

OK,直接切入這個主體,先看看這個程式碼:

function getData() {
}

這個程式碼本身來說是沒有問題的,但是原因在於js開發者必須要盡量把所有定義的東西用object去定義,這是一個良好的習慣,因為這樣可以幫助開發者去理解和使用他,本主題有討論兩種改善方式:

第一種:

var getData = function () {
            };

第二種:

var getData = function getData() {
        };

第二種的最大好處是在於,可以讓getData function 可以去做內部call迴圈,比如說:

var a = function a(){
    console.log('123'+a);
};
a();

結果為:

123function a(){
    console.log('123'+a);
}

但是,有件奇怪的事情來了,如果我有個代碼如下:

console.log(a.toString());
var a = function a(){
    alert('123');
};

竟然出現錯誤!若改成:

var a = function a(){
    alert('123');
};
console.log(a.toString());

則成功,這樣的例子可以說明,js在讀取時事先把function先讀完,而已經被定義過的object不會先讀取.

Issue2:Conditionals

  • pattern and antipattern of using if else

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/conditionals.html

先來看第一個程式碼:

if (type === 'foo' || type === 'bar') {
}

=== 其實就是”格式符合”且”內容物完全一樣”則就是true 不符合則就是false

console.log(1 === 1) 結果就是true 因為兩邊都是number

console.log(“1” === 1) 結果就是false 因為前者是string 後者為number

console.log(“1” === “2”) 結果就是false,雖然都是string,但是string 的1與2不同

作者建議的寫法一:

if (/^(foo|bar)$/.test(type)) {
        }

這部份就是regex test,詳情我建議看這網站,很詳細

http://blog.roodo.com/rocksaying/archives/2670695.html

這個作法其實就是直接轉成machine language,好處是在簡短的句子比對效能很快,但是他的時間複雜度為O(N),所以在長句子的比較會不見得比第一種或第二種快,可以參考這個demo:

http://jsbin.com/uzuxi4/4/edit

不過我覺得這個在解讀程式上時真的有點難懂,在寫時還是要考慮一下你的partner看不看的懂

作者建議寫法二:

if (({foo:1, bar:1})[type]) {
        }

其實這個就是最簡單的hash table search,他的時間複雜度平均為O(1),在分析超長子句時在理論來說較上述來的好,以上相關的討論可以參考這個,非常的詳細:

http://stackoverflow.com/questions/3945092/why-the-third-option-is-better-than-regex

接下來在line36~97部分在講關於BST(Binary search tree),作者範例很清楚就不多說,line103~114部分,則是建議使用array的形式取代掉多種if else的假設,我認為這個方式對於工程師解讀上是很有幫助的。

另外除了array方式,我也推薦另外一種作法:

var hi = {
    a:function () {
        console.log("123")
    },
    b:function(){
        console.log("456")
    }
}
hi.a()

印出的結果為:

123

這樣的形式也可以幫助工程師能夠理解,而line120~138部分則是盡量使用logical operators可以幫助我們解讀code.

我在參加js討論會,講者tonyQ提過這個有趣的example:

function test(option){
    option = option || {};
    console.log(option.start)
}
test(null);

這個印出來的結果是:

undefined

原因是null不屬於obj,裡面也沒有start參數,當然undefined,若改成:

function test(option){
    //option = option || {};
    option.test = option.test || {};
    console.log(option.test.start)
}
test({test:{start:1}});

則印出的結果是1,這個方法可以幫助我們快速去理解obj內部的解析,值得學習!另外有個議題還蠻有趣的,請看:

console.log(true || false);
console.log(true && false);

以上的結果竟是….

true
false

不難理解,請自行想想 …

Issue3:Access to the Global Object

  • access the global object without hard-coding the identifier window

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/access-to-global-object.html

這邊是指如何去高效率的調用global object,並且讓他們可以通過ES3, ES5 and ES5-strict.

var global = (function () {
    return this || (1, eval)('this');
}());

效能展示可以看這個:

http://jsperf.com/globalx

我們來看這個例子:

var x = 'outer';
(function() {
  var x = 'inner';
  eval('console.log("direct call: " + x)'); 
  (1,eval)('console.log("indirect call: " + x)'); 
})();

輸出的結果為:

direct call: inner
indirect call: outer

我曾經想過如果把(1,eval)的1改成null or 0 or undefined 結果通通都是這個,至於為何以下的討論串就有行這樣說:

http://stackoverflow.com/questions/9107240/1-evalthis-vs-evalthis-in-javascript

“The expression (1, eval) is just a fancy way to force the eval to be indirect and return the global object.”

Issue4:Single var Pattern

  • use one var statement and declare multiple variables

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/single-var-pattern.html

一般而言,我們通常會這樣定義變數:

function func() {
    var a = 1,
    var b = 2,

    // function body...
}

這是沒有問題的,但是對於多寫一個var實在沒有額外好處,因此建議這樣寫:

function func() {
    var a = 1,
        b = 2,

    // function body...
}

這樣的好處可以幫助自己去強迫把宣告變數的地方放在同一個,可以幫助理解,也可以幫助自己減少忘記宣告變數的動作, 那如果今天宣告的變數裡面有長字串(或者是包html的話),因為包這些字串每次讀取會增加很多空間,因此也建議類似這種東西要先去做宣告變數,如下:

function updateElement() {
    var el = document.getElementById("result"),
    style = el.style;
    // do something with el and style...
}

Issue 5:Hoisting

– var statements anywhere in a function act as if the variables were declared at the top of the function

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/hoisting.html

Issue 6:for loops

– optimized for loops

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/hoisting.html

這是一個很基本的javascript 變數宣告的觀念,首先,看如下代碼:

myname = "global"; // global variable
function func() {
    console.log(myname); // "undefined"
    var myname = "local";
    console.log(myname); // "local"
}
func();

第三行會出現undefined,這是因為在function外宣告的global variable並不會被導進function內變成local variable,因此第三行的console.log因為在他之前沒有宣告local variable,已故呈現undefined

myname = "global"; // global variable
function func() {
    var myname; // same as -> var myname = undefined;
    console.log(myname); // "undefined"
    myname = "local";
    console.log(myname); // "local"
}
func();

這個情況是,他先宣告了某個local variable,但是因為他並沒有定義這個變數,以至於第四行的console.log輸出為undefined.

Issue 7:for-in loops

  • optimized for-in loops

Issue 8:(Not) Augmenting Built-in Prototypes

  • only augment built-in prototypes when certain conditions are met

Issue 9:switch Pattern

– improve the readability and robustness of your switch statements

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/switch-pattern.html

盡量多使用switch 方式,也請謹守下面switch的格式,break;和default:千萬別忘記打

var inspect_me = 0,
        result = '';
switch (inspect_me) {
    case 0:
        result = "zero";
        break;
    case 1:
        result = "one";
        break;
    default:
        result = "unknown";
}

Issue 10:Implied Typecasting

– avoid implied typecasting

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/avoiding-implied-typecasting.html

其實這邊在討論javascript中,要注意隱式類型的轉換,比如說0在==比較中代表的是false,而1代表的是true,他的範例如下:

var zero = 0;
if (zero == false) {
    console.log('123');
}

輸出的結果為:123,另外我試個有趣的實驗,代碼:

var zero =[] ;
if (zero == false){
    console.log('23');
}
//Output:23

以上當我設定var zero ={} 或者 ‘’ 或者 []時全部都是一樣的結果,那麼若我試試:

var zero =['123'] ;
if (zero == false){
    console.log('23');
}
//竟然沒有console.log的值!在試試true
if (zero == true){
    console.log('23');
}
//一樣沒有console.log的值!

同樣試過string.object也是一樣,這個小實驗蠻有趣的!

Issue 11:Avoiding eval()

  • avoid using eval()

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/avoiding-eval.html

避免使用eval();

Issue 12:Number Conversions with parseInt()

  • use the second radix parameter

Issue 13:Minimizing Globals

  • create and access a global variable in a browser environment

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/minimizing-globals.html

盡量減少宣告global variable,否則很容易經過多種方法就讓他被讀出來

myglobal = "hello"; // antipattern
console.log(myglobal); // "hello"
console.log(window.myglobal); // "hello"
console.log(window["myglobal"]); // "hello"
console.log(this.myglobal); // "hello"

Issue 14:The Problem with Globals

– various problems with globals

https://github.com/shichuan/javascript-patterns/blob/master/general-patterns/globals.html

這是補充issue13

function sum(x, y) {
    // implied global
    result = x + y;
    return result;
}
sum(1,2);
console.log(result);

Comments

Copyright © 2013 Mr.Blue Design credit: Blue Chen