2012年8月27日 星期一

JavaScript Global variables的問題

JavaScript 的變數:
  • JavaScript以函數來管理作用域,定義在函數區塊內的變數是屬於該函數的區域變數。
  • 全域變數是定義在函式作用域外或是沒經過宣告就直接被使用的變數。
  • 一般我們所接觸的執行環境中全域變數指的是指window上的屬性。

全域變數的問題:
  • 程式中的程式碼會共用全域變數,他們存在於相同命名空間中,非常容易發生衝突。
衝突原因:
  • 使用第三方的javascript lib。
  • 團隊其他成員或廣告合作夥伴所撰寫的程式碼。
  • 第三方用來追蹤分析使用者操作的程式碼。
  • 其他等等等等
這些程式碼或lib都有可能使用到同名的全域變數,例如以前的IE7並沒有winsow.JSON這個全域物件,所以我們會抓取網路上知名的lib JSON2來處理JSON的序列化與解序列化問題,但是到了IE8卻原生支援了JSON物件,如此反而會造成衝突使得程式發生錯誤。

因此我們因該盡量避免使用全域變數,使用的越少越好。

note:在宣告變數時,要養成使用var宣告的習慣。

JavaScript 正常使用下如:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Javascript Global Variables</title>
<script>
console.log(this); //window
console.log(window); //window
console.log('this === window 結果:' + (this === window)); //true
console.log("window.hasOwnProperty('name')= " + window.hasOwnProperty('name')); //true

//Dog is Class
function Dog(){
this.color = '白色'; //public Property
var name = "小米"; //private Property
this.getColor=function(){
    return this.color;
};
this.getName=function(){
return name;
};
}

//cat is function
function cat(color ,age, name){
var color = color;
var name = name;
return {color:color, name:name};
}
//create Dog instance
var myDog = new Dog();

console.log('myDog.color= ' + myDog.color); //白色
console.log('myDog.getColor()=' + myDog.getColor());//白色
console.log("window.hasOwnProperty('color')= " + window.hasOwnProperty('color'));  //false
console.log('window.color= ' + window.color); //is undefined 
console.log('myDog.getColor.apply(window)= ' + myDog.getColor.apply(window));
window.color = "橘色";
console.log('window.color= ' + window.color); //橘色
console.log('myDog.color= ' + myDog.color); //白色

//javascript的this是看呼叫function的人是誰,而apply會指定呼叫者,所以會改變了this。
console.log('myDog.getColor.apply(window)= ' + myDog.getColor.apply(window)); //橘色
console.log('myDog.getColor= ' + myDog.getColor()); //白色

console.log('myDog.name= ' + myDog.name); //這是private Property 所以取不到, is undefined.
console.log('myDog.getName()= ' + myDog.getName()); //小米

console.log('window.name=' + window.name); //is empty string 


//get cat
var myCat = cat('黑色','咕嘰');
console.log('myCat.color' + myCat.color);
console.log('window.color=' + window.color);
</script>
</head>
<body>
</body>
</html>


JavaScript誤用造成全域變數生成的範例:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Javascript Global Variables</title>
<script>
console.log(this); //window
console.log(window); //window
console.log('this === window 結果:' + (this === window)); //true

//function mobile
//false
console.log("window.hasOwnProperty('bookName')= " + window.hasOwnProperty('bookName')); 
//false
console.log("window.hasOwnProperty('bookWeight')= " + window.hasOwnProperty('bookWeight'));
//false 
console.log("window.hasOwnProperty('author')= " + window.hasOwnProperty('author'));
//Book is Class
function Book(bookName , bookWeight , bookAuthor){
var bookName = bookName;      //private Property Local variable
this.bookWeight = bookWeight; //public Property Local variable
author = bookAuthor;       //* author is globa variable
this.getName=function(){
    return bookName;
};
this.getWeight=function(){
    return this.bookWeight;
};
this.getAuthor=function(){
    return author;
}
}

var myBook = new Book("深入淺出JavaScript" , '1kg' , '麥克先生');
//false
console.log("window.hasOwnProperty('bookName')= " + window.hasOwnProperty('bookName')); 
//false
console.log("window.hasOwnProperty('bookWeight')= " + window.hasOwnProperty('bookWeight'));
//true 
console.log("window.hasOwnProperty('author')= " + window.hasOwnProperty('author'));

console.log('myBook.getName()= ' + myBook.getName()); ///深入淺出JavaScript
console.log('myBook.getWeight()= ' + myBook.getWeight()); //1kg
console.log('myBook.getAuthor()= ' + myBook.getAuthor()); //麥克先生


//function mobile
console.log("window.hasOwnProperty('type')= " + window.hasOwnProperty('type')); //false
console.log("window.hasOwnProperty('price')= " + window.hasOwnProperty('price')); //false

function mobile(type,price){
        //會因為使用方式沒注意而變成全域變數
this.type = type; //注意這個this是看誰呼叫這個function決定的,所以有可能會變成全域變數
var price = price;
return {type:type,price:price}
}
//注意這個function是直接呼叫而非使用new,這樣他的this會依呼叫者變化
var myMobile = mobile('IPhone4S' , '18000'); 
console.log("window.hasOwnProperty('type')= " + window.hasOwnProperty('type')); //ture
console.log("window.hasOwnProperty('price')= " + window.hasOwnProperty('price')); //false
</script>
</head>
<body>
</body>
</html>


JavaScript不良特性,會造成開發者不小心的全域變數生成:

function area(pi , redius){
   return  result = redius * redius * pi;
}
如此,result因為沒有使用var宣告到,將會變成全域變數。


function init (){
    var eclipseX = cubeX = 10;
}
因為等號是右結合會從右邊先運算所以會等同是,var eclipseX = (cubeX = 10);,如此cubeX未被宣告就被賦值,因JavaScript的特性關西將會變成全域變數。


關於var宣告全域變數的差別:

  • 使用var宣告的全域變數(在function外宣告),是不可以使用delete運算符號刪除的。
  • 非使用var宣告的全域變數(不小心在函式中未宣告var創造出來的)是可以使用delete來刪除。

參考資料來源:
JavaScript 設計模式  (oreilly公司)

關於Console的使用方式請參考http://program-rock.blogspot.tw/2012/08/chrome-developer-tools-console.html

Chrome Developer Tools: Console

前言:
          一開始學Javascript時因為對其環境不了解,所以不曉得如何使用console輸出功能,只好使用alert來輸出除錯,但這樣其實很難用,找了找網路資源找到了如何使用chrome的console輸出功能。

官方參考文件:

Windows環境中開啟Chrome Devloper Tools的方式:
  • 按下F12
  • Ctrl + Shift + i 
  •   →  工具(L)  → 開發人員工具(D)

在Devloper Tools中打開Console面板方式:
Example:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Chrome Developer Tools Console</title>
<script>
var animal={dog:'邊境牧羊犬' , cat:'咕嘰' , fish:'台灣鬥魚'};
console.log(this);
console.log(this.animal.dog);
</script>
</head>
<body>
</body>
</html>

如此打開Console Panel後可見到輸出了window與邊境牧羊犬,而因為window是物件還可以點擊看其物件的屬性明細。