JavaScript基本數(shù)據(jù)類(lèi)型和引用數(shù)據(jù)類(lèi)型
ECMAScript包含兩種不同數(shù)據(jù)類(lèi)型的值:基本類(lèi)型值和引用類(lèi)型值。基本類(lèi)型值指的是簡(jiǎn)單的數(shù)據(jù)段,而引用類(lèi)型值那些可能有多個(gè)值構(gòu)成的對(duì)象。
在進(jìn)行變量賦值時(shí),解析器必須確定這個(gè)值是基本類(lèi)型值還是引用類(lèi)型值。常用的5中基本數(shù)據(jù)類(lèi)型為:Undefined、Null、Boolean、Number和String?;緮?shù)據(jù)類(lèi)型是按值訪問(wèn)的,因?yàn)榭梢圆僮鞅4嬖谧兞恐械膶?shí)際的值。而引用類(lèi)型的值是保存在內(nèi)存中的對(duì)象。JavaScript不允許直接訪問(wèn)內(nèi)存中的位置,即不能直接操作對(duì)象的內(nèi)存空間。在操作對(duì)象時(shí),實(shí)際上是在操作對(duì)象的引用而不是實(shí)際的對(duì)象。因此,引用類(lèi)型的值是按引用訪問(wèn)的。
1. 動(dòng)態(tài)的屬性
對(duì)于引用類(lèi)型的值,我們可以為其添加屬性和方法,也可以改變和刪除其屬性和方法。例如:
- var person = new Object();
- person.name = "John";
- alert(person.name); //"John"
只要對(duì)象不被銷(xiāo)毀或者這個(gè)屬性不被刪除,這個(gè)屬性就一直存在。但我們不能給基本類(lèi)型的值添加屬性,盡管并不會(huì)導(dǎo)致任何錯(cuò)誤。
2. 復(fù)制變量值
如果從一個(gè)變量向另一個(gè)變量復(fù)制基本類(lèi)型的值,會(huì)在變量對(duì)象上創(chuàng)建一個(gè)新值,然后將該值復(fù)制到為新變量分配的位置上,例如:
- var num1 = 5;
- var num2 = num1; //5
下圖形象地展示了復(fù)制基本類(lèi)型值的過(guò)程:
當(dāng)從一個(gè)變量向另一個(gè)變量復(fù)制引用類(lèi)型的值時(shí),同樣會(huì)將存儲(chǔ)在變量對(duì)象中的值復(fù)制一份放到為新變量分配的空間中。不同的是,這個(gè)值的副本實(shí)際上是一個(gè)指針,而這個(gè)指針指向存儲(chǔ)在堆中的一個(gè)對(duì)象。復(fù)制操作結(jié)束后,兩個(gè)變量實(shí)際上將引用同一個(gè)對(duì)象。因此,改變其中一個(gè)變量,就會(huì)影響另一個(gè)變量,例如:
- var obj1 = new Object();
- var obj2 = obj1;
- obj1.name = "John";
- alert(obj2.name); //"John"
3. 傳遞參數(shù)
ECMAScript中所有函數(shù)的參數(shù)都是按值傳遞的。把函數(shù)外部的值復(fù)制給函數(shù)內(nèi)部的參數(shù),就和把一個(gè)變量的值復(fù)制給另一個(gè)變量一樣。無(wú)論傳遞的是基本類(lèi)型的值還是引用類(lèi)型的值。這里需要注意,訪問(wèn)變量有按值和按引用兩種方式,但參數(shù)只能按值傳遞。
在向參數(shù)傳遞基本類(lèi)型的值時(shí),被傳遞的值會(huì)被復(fù)制給一個(gè)局部變量(即命名參數(shù))。例如:
- function addTen(num){
- num += 10;
- return num;
- }
- var count = 20;
- var result = addTen(count);
- alert(count); //20,沒(méi)有變化
- alert(result); //30
參數(shù)實(shí)際上是函數(shù)的局部變量。參數(shù)num和變量count互不認(rèn)識(shí),它們只是具有相同的值。假如num是按引用傳遞的,那么count的值也會(huì)變成30,從而反應(yīng)函數(shù)內(nèi)部的變化。
在向參數(shù)傳遞引用類(lèi)型的值時(shí),會(huì)把這個(gè)值在內(nèi)存中的地址復(fù)制給一個(gè)局部變量,因此這個(gè)局部變量的變化會(huì)反應(yīng)在函數(shù)外部。例如:
- function setName(obj){
- obj.name = "John";
- }
- var person = new Object();
- setName(person);
- alert(person.name); //"John"
在這個(gè)函數(shù)內(nèi)部,obj和person引用的是同一個(gè)對(duì)象。換句話說(shuō),即使這個(gè)值是按值傳遞的,obj也會(huì)按引用來(lái)訪問(wèn)同一個(gè)對(duì)象。于是,在函數(shù)內(nèi)容為obj添加nama屬性在函數(shù)外的person對(duì)象也有所反應(yīng),因?yàn)閜erson指向的對(duì)象在堆內(nèi)存中只有一個(gè),而且是全局對(duì)象。有許多開(kāi)發(fā)者認(rèn)為,在函數(shù)內(nèi)容修改的對(duì)象會(huì)在全局作用域中反應(yīng)出來(lái),說(shuō)明參數(shù)是按引用傳遞的,這個(gè)觀點(diǎn)其實(shí)是錯(cuò)誤的,看下面的代碼:
- function setName(obj){
- obj.name = "John";
- obj = new Object();
- obj.name = "Evan";
- }
- var person = new Object();
- setName(person);
- alert(person.name); //"John"
如果person是按引用傳遞的,那么person就會(huì)自動(dòng)被修改為指向其name屬性為"Evan"的新對(duì)象,但我們?cè)L問(wèn)person.name時(shí)發(fā)現(xiàn)其值仍然為"John",這說(shuō)明即使在函數(shù)內(nèi)部修改了參數(shù)的值,但原始的引用仍然保持不變。實(shí)際上,在函數(shù)內(nèi)部重寫(xiě)obj時(shí),這個(gè)變量引用的就是一個(gè)局部對(duì)象了。而這個(gè)局部對(duì)象在函數(shù)執(zhí)行完成之后會(huì)立即銷(xiāo)毀。
4. 檢測(cè)類(lèi)型
要檢測(cè)一個(gè)值是不是基本數(shù)據(jù)類(lèi)型,typeof操作符是***工具。它可以檢測(cè)一個(gè)變量是字符串、數(shù)值、布爾值還是undefined。如果一個(gè)變量是對(duì)象或null,則typeof操作符會(huì)返回object。
但是在檢測(cè)引用類(lèi)型的值時(shí),typeof用處就不那么大了,通常我們并不想知道某個(gè)值是對(duì)象,而想知道它是什么類(lèi)型的對(duì)象,此時(shí)可以使用instanceof操作符,如果變量是給定引用類(lèi)型的實(shí)例,那么instanceof操作符就會(huì)返回true。例如:
- alert(person instanceof Object); //變量person是Object嗎?
- alert(colors instanceof Array); //變量colors是Array嗎?
- alert(pattern instanceof RegExp); //變量pattern是RegExp嗎?
所有引用類(lèi)型的值都是Object的實(shí)例。因此,在檢測(cè)一個(gè)引用類(lèi)型的值或Object構(gòu)造函數(shù)時(shí),instanceof都會(huì)返回true。當(dāng)用instanceof操作符檢測(cè)一個(gè)基本類(lèi)型的值時(shí),都會(huì)返回false,因?yàn)榛绢?lèi)型不是對(duì)象。