ECMAScript變數包含兩種不同資料型別的值:基本型別值和引用型別值。基本型別值是簡單的資料段,而引用型別值指那些可能由多個值構成的物件。
在將一個值賦給變數時,解析器必須確定這個值是基本型別還是引用型別。基本類型包括如Undefined、Null、Boolean、Number和String,這5種基本類型資料類型是按值存取的,因此可以操作保存在變數中的實際的值;引用類型類型的值是保存在記憶體中的對象。與其他語言不同,JavaScript不允許直接存取記憶體中的位置,也就是說不能直接操作物件的記憶體空間。在操作對象時,實際上實在操作對象的引用而不是實際的對象,因此,引用類型的值是按引用訪問的。
1、動態的屬性
定義基本型別和定義引用型別的方法大相近庭。對於引用類型的值,我們可以為其新增屬性和方法,也可以改變和刪除其屬性和方法,如下:
2、複製變數值
如果從一個變數複製基本型別的值,會在變數物件上建立一個新值,然後將該值複製到為新變數指派的位置。
當從一個變數複製引用型別的值到另一個變數時,同樣也會將儲存在變數物件中的值複製一份放到為新變數分配的記憶體空間。不同的是,這個值其實是一個指針,而這個指針執行儲存在堆中的一個物件。複製結束後,兩個變數實際上將引用同一個物件。因此,改變其中一個變量,就會影響到另一個變量,如下所示:
3、傳遞參數
ESMAScript中所有函數的參數都是以值傳遞的。也就是說,把函數外部的值複製給函數內部的參數,就是把值從一個變數複製到另一個變數一樣。基本類型值的傳遞如同基本類型變數的複製。引用類型值的傳遞,則如同引用類型變數的複製一樣。有不少開發者在這一點上可能感到困惑,因為訪問變數有按值和按引用兩種方式,而參數只能按值傳遞。
在傳遞基本型別的值給參數時,被傳遞的值會被複製給一個局部變數(即命名參數)。如下程式碼所示:
在傳遞參考類型的值給參數時,會把這個值在記憶體中的位址複製一個給局部變量,因此這個局部變數的變化會反映在函數的外部。這裡我們使用引用型來看看:
在這個函數內部,obj和person引用的是同一個物件。換句話說,即使這個物件是按值傳遞的,obj也會按引用來存取同一個物件。於是,當函數內部為obj添加name屬性後,函數外部的person也會有所反映,因為person指向的物件在堆記憶體中只有一個,而且是全域物件。很多開發者錯誤的認為:在局部作用域中修改的物件會在全域作用域中反映出來,就表示參數是按引用傳遞的。為了證明物件是按值傳遞的,我們在看看下面這個進過修改的例子:
上面這個例子可以看出,如過person是按引用傳遞的,那麼person就會自動被修改為指向其name屬性值為"sdf"的新物件。但是,接下來再造訪person.name時,顯示的仍然是"zxj"。這說明即使在函數內部修改了參數的值,但原始的參考仍然保存不變。實際上,當在函數內部重寫obj時,這個變數引用的就是一個局部物件了。而這個局部物件會在函數執行完畢時立即銷毀。
可以將ECMAScript函數的參數想像成局部變數。
4、偵測類型
雖然在偵測基本資料型別時typeof是個得力助手,但是在偵測引用型別時,這個運算子用處卻不大。通常,我們並不想知道某個值是對象,而是想知道他是什麼類型的對象。為此ECMAScript提供了instanceof操作符,其語法如下: