2017年11月30日 星期四

[ JavaScript Gossip ] JavaScript 核心 : 這些東西不簡單 (變數)

轉載自 這裡 
在程式語言的分類中,依所宣告的變數是否帶有型態,可區分為靜態(Static-type)語言動態(Dynamic-type)語言
Java、C/C++ 等皆為靜態語言,其宣告變數必然同時宣告變數之型態。以 Java 為例 : 
  1. int number = 10;  
  2. String id = "john";  
上例中,number 變數本身帶有 int 型態資訊,而 id 變數帶有 String 型態資訊,在指定時,變數型態資訊與值的型態資訊必須符合,否則會發生編譯失敗. 
JavaScript 則為動態語言,其變數本身使用者無需宣告型態,型態資訊僅在值或物件本身,變數只用來作為取得值或物件的參考。例如 : 
  1. var some = 10;  
  2. some = 'caterpillar';  
由於變數本身不帶型態資訊,同一個變數可以指定不同型態的值,實際操作時,是在執行時期才透過變數來參考至物件或值,才得知物件或值上有操作之方法. 靜態語言由於變數本身帶有型態資訊,好處就是編譯時期,可由編譯器確認變數與實際參考之值是否符合,可在編譯時期就檢查出許多型態指定不符的錯誤。相對地,動態語言就必須等到執行時期,才能發現所操作的對象並非預期型態之錯誤,這是靜態語言優點動態語言的地方. 
然而,靜態語言宣告變數時,必須同時宣告型態,因而容易造成語法上的冗長。例如在 Java 中,若要使用同一陣列儲存多種物件,則一個例子如下 : 
  1. Object[] objects = {"caterpillar"new Integer(100), new Date()};  
  2. String name = (String) objects[0];  
  3. Integer score = (Integer) objects[1];  
  4. Data time = (Date) objects[2];  
反觀 JavaScript 若要達到相同目的,所需的程式碼較為簡短。例如 : 
  1. var objects = ['caterpillar'100new Date()];  
  2. var name = objects[0];  
  3. var score = objects[1];  
  4. var time = objects[2];  
就程式撰寫速度上,動態語言著實有著比靜態語言快速的優點! 
再回頭看看 JavaScript 變數 宣告的討論。在 JavaScript 中要宣告變數,可以使用 var 來宣告。這是先前一直都有看到的,事實上,你也可以不用var宣告,直接指定某個名稱的值,該名稱會自動成為全域範圍,其實也就是在全域global物件上建立特性. 這很方便,也很危險,因為是在全域物件上建立特性。全域變數若在瀏覽器中,就是 window 物件,在 Rhino Shell 中,也可以在全域範圍中使用 this 來取得。例如 : 
> some = 10
10
> function test(some){ console.log('some=', some); console.log('this.some=',this.some);}
undefined
> test(20)
some= 20
this.some= 10
undefined


使用 var 所宣告的變數,作用範圍是在當時所在環境,不使用 var 直接指定值而建立的變數,則是全域物件上的一個特性,也就是俗稱的全域範圍。可以先以這樣的觀念理解,如果寫下 : 
some = 10;

執行時可先以直譯器會直接這麼作來理解 : 
this.some = 10;

如果你在全域使用 var 宣告變數,也相當於在全域物件上建立特性. 如果全域與區域中有同名的變數,則區域會暫時覆蓋全域. 你可以使用 delete 來刪除物件上的特性。由於未使用 var 宣告的變數,會是全域物件上的特性,就某些意義來說,對未使用 var 宣告的變數使用 delete,就相當於所謂刪除變數 : 
> a = 20
20
> if(delete a) console.log('Delete a success!')
Delete a success!

上例中, delete 會傳回 true 表示特性刪除成功,false 表示無法刪除。使用 var 宣告的變數就無法用 delete 刪除! 可觀察以下在函式中使用 var 與不使用 var 宣告的變數之差別: 
> function func() { var x = 10; y = 20; }
undefined
> func()
undefined
> x
ReferenceError: x is not defined
> y
20
> this.y
20

x 在函式中使用 var 宣告,所以在函式外不可見,但 y 並非使用 var 宣告,所以 y 是全域物件上的特性,在函式外依舊可見,俗稱全域範圍。 

如果你在全域使用 var 宣告變數,也相當於在全域物件上建立特性; 如果全域與區域中有同名的變數,則區域會暫時覆蓋全域. 例如: 
> var x = 10
undefined
> function func() { var x = 20; console.log(x); }
undefined
> func()
20 # 此時列印出來的是區域的 x
undefined
> console.log(x)
10


你可以重複使用 var 宣告變數,但不會覆蓋原有的指定值. 要注意的是,var宣告的變數是當時作用範圍中整個都是有作用的,並沒有所謂區塊範圍。例如 : 
> function func() { if(true) { var x = 10; } return x; }
undefined
> func()
10

var 宣告的變數是當時作用範圍中整個都是有作用的,這會產生令人驚奇的結果。例如下例不意外的,會產生直譯錯誤 
> function func() { print(m); }
undefined
> func()
ReferenceError: m is not defined

但下例中並不會直譯錯誤: 
> function func() { console.log(m); var m = 10; console.log(m); }
undefined
> func()
undefined
10
undefined

所 有 var 宣告的變數,在整個函式區塊中都是可見的,因而在上例中第一個 console.log(m) 時是可找到 m 特性,只不過 是 undefined 的值。 

如果你有興趣,範圍鏈(Scope chain) 會深入說明一件事,使用 var 所宣告的變數,會是當時執行環境(Execute context)中呼叫物件(call object)上的特性,也因此沒有所謂區塊範圍!

沒有留言:

張貼留言

[Git 常見問題] error: The following untracked working tree files would be overwritten by merge

  Source From  Here 方案1: // x -----删除忽略文件已经对 git 来说不识别的文件 // d -----删除未被添加到 git 的路径中的文件 // f -----强制运行 #   git clean -d -fx 方案2: 今天在服务器上  gi...