[TOC]
## 基本類型和引用類型的值
ECMAScript變量可能包含兩種不同數據類型的值:基本類型值和引用類型值。
- 基本類型值指的是簡單的數據段
按值訪問,可操作保存在變量中的實際的值。基本類型值指的是簡單的數據段。
基本數據類型有這五種:Undefined、Null、String、Number、Boolean。
```js
var str = "String";
```
- 引用類型指那些可能由多個值構成的對象
當復制保存著對象的某個變量時,操作的是對象的引用,但在為對象添加屬性時,操作的是實際的對象。引用類型值指那些可能為多個值構成的對象。
引用類型值有這幾種:object、Array、RegExp、Date、Function、特殊的基本包裝類型(String、Number、Boolean)以及單體內置對象(Global、Math)。
```js
var person = new Object();
```
### 1. 動態屬性
定義基本類型和引用類型值的方式是類似的:創建一個變量并為該變量賦值。但是,當這個值保存到變量中后,對不同類型值可以執行的操作則大相徑庭。對于引用類型的值,我們可以為其添加屬性和方法,也可以改變和刪除其屬性和方法。
- 基本類型值
```js
var nmae = "SpiritLing";
name.age = 22;
console.log(name.age); //undefined
```
我們為字符串 `name` 定義了一個 `age` 的屬性,并為該對象添加了一個名為 `age` 的屬性。并為屬性賦值,但在下一行訪問時返回 `undefined` ,說明基本類型值無法動態的添加屬性
- 引用類型值
```js
var person=new Object();
person.name = "SpiritLing";
console.log(person.name); //"SpiritLing"
```
以上創建一個對象,并將其保存在變量 `person` 中。然后,我們為該對象添加一個屬性 `name` ,并將其字符串賦值給這個屬性。緊接著,又通過 `console.log()` 輸出訪問的結果,如果對象不被銷毀,或者屬性不被刪除,則這個屬性將一直存在。,說明引用類型值時可以動態添加屬性
### 2. 復制變量值
基本類型和引用類型不至只有這個可以區分,還有好多,現在我們講復制變量值兩者的不同
- 基本類型值
```js
var num1=5;
var num2=num1;
num1=10;
console.log(num1); //10
console.log(num2); //5
```
定義 `num1` 時
| | |
| --- | --- |
| num1 | 5 |
將 `num1` 賦值給 `num2` 后
| | |
| --- | --- |
| num2 | 5 |
| num1 | 5 |
改變 `num1` 值后
| | |
| --- | --- |
| num2 | 5 |
| num1 | 10 |
初始時, `num1` 中保存的值是5,。當使用 `num1` 的值來初始化 `num2` 時,`num2` 中也保存了值5,。但 `num2` 中的5與 `num1` 中的5是完全獨立的,該值只是 `num1` 中5的副本。此后,這兩個變量可以參與任何操作而不會影響。
- 引用類型
```js
var obj1=new Object();
var obj2=obj1;
obj2.name="SpiritLing";
console.log(obj1.name); //SpiritLing
console.log(obj2.name); //SpiritLing
```

當從一個變量向另一個變量復制引用類型時,同樣也會將存儲在變量對象中的值復制一份放在為新變量分配的空間中。不同的是,這個值的副本實際上是一個指針,而這個指針指向存儲在堆中的一個對象。復制操作結束后,兩個變量將引用同一個對象。因此,改變其中一個變量時,就會影響另外一個。
首先,變量 `obj1` 保存了一個對象的新實例。然后,這個值被復制到 `obj2` 中;換句話說, `obj1` 和 `obj2` 都指向同一個對象。這樣,當為 `obj2` 添加了 `name` 屬性后,可以通過 `obj1` 來進行訪問這個屬性,因為這兩個變量引用都指向同一個對象。上圖展示了保存在對象中的變量和保存在堆中的對象之間的這種關系。
### 3. 傳遞參數
ECMAScript中所有`函數`的`參數`都是`按值傳遞`的。也就是說,把函數外部的值復制給函數內部的參數,就和把值從一個變量賦值到另一個變量一樣。基本類型值的傳遞如同基本類型變量的復制一樣,而引用類型值的傳遞,則如同引用類型變量的復制一樣。
- 在這里有不少人會困惑,因為訪問變量時,有按值和按引用兩種方式,怎么參數只能按值傳遞呢??
在向參數傳遞基本類型時,被傳遞的值會被復制給一個局部變量(即命名參數,或者用ECMAScript的概念來說,就是 `arguments` 對象的一個元素)。在向參數傳遞引用類型的值時,會把這個值在內存的地址復制給一個局部變量,因此這個局部變量的變化會反映在函數的外部。
```js
function addTen(num){
num += 10;
return num;
}
var count = 20;
var result=addTen(count);
console.log(count); //20,沒有變化
console.log(result); //30
```
這里的函數 `addTen()` 有一個參數 `num` ,而參數實際上是函數的局部變量。在調用這個函數時,變量 `count` 作為參數被傳遞進去,這個變量的值是20。于是,數值20被復制給局部變量參數 `num` 以便在 `addTen()` 中使用。在函數中給 `num` 增加10,并不會改變原來的 `count` 的值,因為基本類型在變量復制時,不是引用其地址,而是在開辟一個空間,放入新復制的變量名和值,改變時,只會改變一個,而不會影響其他的變量。所以使用按值傳遞。
假設使用引用傳遞時,則改變 `num` 的值時,`count` 也會發生變化,從而反映函數內部的修改,當然,使用數值等基本類型值來說明按值傳遞參數比較簡單,但如果使用對象,那么問題不太好理解了
```js
function setName(obj){
obj.name = "SpiritLing";
}
var person = new Object();
setNmae(person);
console.log(person.name); //"SpiritLing"
```
在上面的代碼中,我們創建一個對象,并保存在變量 `person` 中,然后,這個變量被傳遞到 `setName()` 函數中之后就被復制給了 `obj` 。在函數的內部,`obj` 和 `person` 引用的是同一個對象。換句話說,即使這個變量時按值傳遞的,`obj` 也會按引用來訪問同一個對象。于是,在函數內部添加屬性,則在外面也會有所反應。因為 `person` 在堆內存中只有一個,而且是全局對象。
> 在這里,有許多人認為:在局部作用域中修改的對象會在全劇以作用域中反映出來,就說明參數是按引用傳遞的,但是,實際上是以值進行傳遞的。下面有個例子可以說明
```js
function setName(obj){
obj.name = "SpiritLing";
obj = new Object();
obj.name = "博客";
}
var person = new Object();
setName(person);
console.log(person.name); //"SpiritLing"
```
上面代碼和前面的代碼有一些區別,就是在 `setName()` 函數中添加了兩行代碼:
* 一行為 `obj` 重新定義了一個對象
* 另一行為該對象定義了一個帶有不同值的 `name` 屬性
在把 `person` 傳遞給 `setName()` 后,其 `name` 屬性被設置為 `"SpiritLing"` 。然后,又將一個新對象賦值給變量 `obj` ,同時將其 `name` 值改為 `"博客"` 。如果 `person` 使按照引用傳遞的,那么 `person` 會被自動修改為指向其 `name` 屬性值為 `"博客"` 的新對象。但是,接下來再訪問 `person.name` 時,顯示的值仍然為 `"SpiritLing"` 。這說明即使在函數內部修改了參數的值,但原始的引用仍然保持不變。實際上,當在函數內部重寫 `obj` 時,這個變量引用的就是一個局部對象。而這個局部對象會在函數執行完成后立即被銷毀。
> 可以把 ECMAScript 函數的參數想象成局部變量。
### 4. 檢測類型
在上面開始時,我們說了基本類型都有哪幾種,引用類型又有哪幾種,在其中,我們可以使用 `typeof` 對值進行類型判定,在對比種類即可確定屬于那種類型。`typeof` 可以確認出字符串、數值、布爾值。還是 `undefined` ,如果變量的值是一個對象或者 `null` ,則 `typeof` 操作符會返回 `object`。
```js
var s = "SpiritLing";
var b = true;
var m = 123;
var u;
var n = null;
var o = new Object();
console.log(typeof s); //string
console.log(typeof b); //boolean
console.log(typeof m); //number
console.log(typeof u); //undefined
console.log(typeof n); //object
console.log(typeof o); //object
```
雖然上面可以使用 `typeof` 來監測,但是,實際上,`typeof` 更適合于監測基本類型,但在監檢測引用類型的值時,這個操作符的用處不大。通常,我們并不是想知道某個值是對象,而是想知道它是什么類型的對象。為此,ECMAScript提供了 `instanceof` 操作符,其語法如下所示:
```js
result = variable instanceof constructor
```
如果變量是給定引用類型的實例,那么 `instanceof` 操作符就會返回 `true`
```js
var arr=[1,2,3,4];
var obj=new Object();
console.log(arr instanceof Array); //true
console.log(obj instanceof Object); //true
```
如果使用 `instanceof` 監測一個引用類型值和 `Object` 構建的函數時,`instanceof` 操作符始終會返回 `true`。當然,如果用來監測基本類型時,則始終返回 `false` ,因為基本類型不是對象。