天天看點

js 變量類型和作用域

一、變量的類型 

  Javascript和Java、C這些語言不同,它是一種無類型、弱檢測的語言。它對變量的定義并不需要聲明變量類型,我們隻要通過指派的形式,可以将各種類型的資料指派給同一個變量。例如: 

複制代碼代碼如下:

i=100;//Number類型 

i="variable";//String類型 

i={x:4};//Object類型 

i=[1,2,3];//Array類型 

  JS的這種特性雖然讓我們的編碼更加靈活,但也帶來了一個弊端,不利于Debug,編譯器的弱檢測讓我們維護冗長的代碼時相當痛苦。 

二、變量的聲明 

  JS中變量申明分顯式申明和隐式申明。 

  var i=100;//顯式申明 

  i=100;//隐式申明 

  在函數中使用var關鍵字進行顯式申明的變量是做為局部變量,而沒有用var關鍵字,使用直接指派方式聲明的是全局變量。   

  當我們使用通路一個沒有聲明的變量時,JS會報錯。而當我們給一個沒有聲明的變量指派時,JS不會報錯,相反它會認為我們是要隐式申明一個全局變量,這一點一定要注意。 

三、全局變量和局部變量 

  當JS解析器執行時,首先就會在執行環境裡建構一個全局對象,我們定義的全局屬性就是做為該對象的屬性讀取,在頂層代碼中我們使用this關鍵字和window對象都可以通路到它。而函數體中的局部變量隻在函數執行時生成的調用對象中存在,函數執行完畢時局部變量即刻銷毀。是以在程式設計中我們需要考慮如何合理聲明變量,這樣既減小了不必要的記憶體開銷,同時能很大程度地避免變量重複定義而覆寫先前定義的變量所造成的Debug麻煩。 

四、變量作用域 

  任何程式語言中變量的作用域都是一個很關鍵的細節。JS中變量的作用域相對與JAVA、C這類語言顯得更自由,一個很大的特征就是JS變量沒有塊級作用域,函數中的變量在整個函數都中有效,運作下面代碼: 

複制代碼代碼如下:

<SCRIPT LANGUAGE="JavaScript" type="text/javascript"> 

//定義一個輸出函數 

function outPut(s){ 

document.writeln(s) 

//全局變量 

var i=0; 

//定義外部函數 

function outer(){ 

//通路全局變量 

outPut(i); // 0 

//定義一個類部函數 

function inner(){ 

//定義局部變量 

var i = 1; 

// i=1; 如果用隐式申明 那麼就覆寫了全局變量i 

outPut(i); //1 

inner(); 

outPut(i); //0 

outer(); 

</SCRIPT> 

  輸出結果為0 1 0,從上面就可以證明JS如果用var在函數體中聲明變量,那麼此變量在且隻在該函數體内有效,函數運作結束時,本地變量即可銷毀了。 

  由于上面的這個JS特性,還有一個關鍵的問題需要注意。此前一直使用ActionScript,雖然它和JS都是基于ECMA标準的,但在這裡還是略有不同的。例如下面代碼: 

複制代碼代碼如下:

<SCRIPT LANGUAGE="JavaScript" type="text/javascript"> 

//定義一個輸出函數 

function outPut(s){ 

document.writeln(s) 

//全局變量 

var i=0; 

//定義外部函數 

function outer(){ 

//通路全局變量 

outPut(i); // 0 

//定義一個類部函數 

function inner(){ 

outPut(i); //undefiend 

var i=1; 

outPut(i); //1 

inner(); 

outPut(i); //0 

outer(); 

</SCRIPT> 

  你可能認為輸出結果是0 0 1 0,事實上在AS中确實是這樣的,而在JS中的輸入卻是0 undefined 1 0,為何會這樣了?剛才我們說到了JS函數體中聲明的本地變量在整個函數中都有效,是以在上面代碼中var i = 1 ;在inner函數中都有效,實際上顯式聲明的變量i是在預編譯時就已經編譯到調用對象中了,不同于隐式聲明變量在解釋時才被定義為全局變量,隻是在調用outPut(i)時,還沒有将它初始化變量,此時的本地變量i是未指派變量,而不是未定義變量,是以輸出了undefined。上面的代碼等效于下面代碼: 

複制代碼代碼如下:

function inner(){ 

var i; //定義但不指派 

outPut(i); //undefiend 

i=1; 

outPut(i); //1 

  為了避免上面的這類問題,是以在函數開始位置集中做函數聲明是一個極力推薦的做法。 

五、基本類型和引用類型 

  JS不同于JAVA、C這些語言,在變量申明時并不需要聲明變量的存儲空間。變量中所存儲的資料可以分為兩類:基本類型和引用類型。其中數值、布爾值、null和undefined屬于基本類型,對象、數組和函數屬于引用類型。 

  基本類型在記憶體中具有固定的記憶體大小。例如:數值型在記憶體中占有八個位元組,布爾值隻占有一個位元組。對于引用型資料,他們可以具有任意長度,是以他們的記憶體大小是不定的,是以變量中存儲的實際上是對此資料的引用,通常是記憶體位址或者指針,通過它們我們可以找到這個資料。 

  引用類型和基本類型在使用行為上也有不同之處: 

複制代碼代碼如下:

<SCRIPT LANGUAGE="JavaScript" type="text/javascript"> 

//定義一個輸出函數 

function outPut(s){ 

document.writeln(s) 

var a = 3; 

var b = a; 

outPut(b); 

//3 

a = 4; 

outPut(a); 

//4 

outPut(b); 

//3 

</SCRIPT> 

  對基本類型b進行指派時,實際上是又開辟了一塊記憶體空間,是以改變變量a的值對變量b沒有任何影響。 

複制代碼代碼如下:

<SCRIPT LANGUAGE="JavaScript" type="text/javascript"> 

//定義一個輸出函數 

function outPut(s){ 

document.writeln(s) 

var a_array = [1,2,3]; 

var b_array = a_array; 

outPut(b_array); //1,2,3 

a_array[3] = 4; 

outPut(b_array);//1,2,3,4 

</SCRIPT> 

上面是對引用類型的變量指派,實際上他們傳遞的是對記憶體位址的引用,是以對a_array和b_array的存取,實際上都是操作的同一塊記憶體區域。如果希望重新配置設定記憶體空間存儲引用型變量,那麼我就需要使用克隆方法或者自定義方法來複制引用變量的資料。 

JS變量作用域 

複制代碼代碼如下:

<script language ="javascript" type ="text/javascript" > 

var a = "change"; 

function fun() { 

alert(a);//輸出undefined 

var a = "改變了"; 

alert(a);//輸出改變了 

alert(a);//輸出change 

fun(); 

</script> 

var定義的是一個作用域上的變量,在第一次輸出a之前,JS在預編譯分析中已經将a指派為change,是以第一次輸出change,當調用到fun()函數的時候,JS建立一個新的作用域,在輸出a之前,初始化所有var變量的值為undefined,是以fun()中第一次輸出的是undefined,第二次輸出已經給a指派了,是以輸出新的值;兩個a在函數裡面和外面是不同的兩個變量,如: 

複制代碼代碼如下:

<script language ="javascript" type ="text/javascript" > 

var b; 

function fun() { 

b = "change"; 

alert(b);//輸出undefined 

</script> 

變量b在函數外面已經定義了,在函數中有給b指派,但輸出的卻是undefined。

繼續閱讀