1.原始類型和引用類型
1.1 什么是類型
原始類型 保存為簡(jiǎn)單數(shù)據(jù)值。
引用類型 保存為對(duì)象详炬,其本質(zhì)是指向內(nèi)存位置的引用魄健。
為了讓開發(fā)者能夠把原始類型和引用類型按相同的方式處理,JavaScript花費(fèi)了很大的努力來保證語言的一致性孟抗。
其他編程語言用棧存原始類型润匙,用對(duì)存儲(chǔ)引用類型诗眨。而JavaScript則完全不同:它使用一個(gè)變量對(duì)象追蹤變量的生存期。原始值被直接保存在變量對(duì)象內(nèi)孕讳,而引用值則作為一個(gè)指針保存在變量對(duì)象內(nèi)辽话,該指針指向?qū)嶋H對(duì)象在內(nèi)存中的存儲(chǔ)位置。
1.2 原始類型
原始類型代表照原樣保存的一些簡(jiǎn)單數(shù)據(jù)卫病。
JavaScript共有 5 種原始類型:
- boolean 布爾,值為
true
orfalse
- number 數(shù)字典徘,值為任何整型或浮點(diǎn)數(shù)值
- string 字符串蟀苛,值為由單引號(hào)或雙引號(hào)括住的單個(gè)字符或連續(xù)字符
- null 空類型,僅有一個(gè)值:null
- undefined 未定義逮诲,只有一個(gè)值:undefined(undefined會(huì)被賦給一個(gè)還沒有初始化的變量)
JavaScript和許多其他語言一樣帜平,原始類型的變量直接保存原始值(而不是一個(gè)指向?qū)ο蟮闹羔槪?/p>
var color1 = "red";
var color2 = color1;
console.log(color1); // "red"
console.log(color2); // "red"
color1 = "blue";
console.log(color1); // "blue"
console.log(color2); // "red"
鑒別原始類型
鑒別原始類型的最佳方式是使用 typeof
操作符。
console.log(typeof "Nicholas"); // "string"
console.log(typeof 10); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
至于空類型(null)則有些棘手梅鹦。
console.log(typeof null); // "object"
對(duì)于 typeof null裆甩,結(jié)果是"object"。(其實(shí)這已被設(shè)計(jì)和維護(hù)JavaScript的委員會(huì)TC39認(rèn)定是一個(gè)錯(cuò)誤齐唆。在邏輯上嗤栓,你可以認(rèn)為 null
是一個(gè)空的對(duì)象指針,所以結(jié)果為"object"箍邮,但這還是很令人困惑茉帅。)
判斷一個(gè)值是否為空類型(null)的最佳方式是直接和 null
比較:
console.log(value === null); // true or false
注意:以上這段代碼使用了三等號(hào)(全等===),因?yàn)槿忍?hào)(全等)不會(huì)將變量強(qiáng)制轉(zhuǎn)換為另一種類型锭弊。
console.log("5" == 5); // true
console.log("5" === 5); // false
console.log(undefined == null); // true
console.log(undefined === null); // false
原始方法
雖然字符串堪澎、數(shù)字和布爾值是原始類型,但是它們也擁有方法(null和undefined沒有方法)味滞。
var name = "Nicholas";
var lowercaseName = name.toLowerCase(); // 轉(zhuǎn)為小寫
var count = 10;
var fixedCount = count.toFixed(2); // 轉(zhuǎn)為10.00
var flag = true;
var stringFlag = flag.toString(); // 轉(zhuǎn)為"true"
console.log("YIBU".charAt(0)); // 輸出"Y"
盡管原始類型擁有方法樱蛤,但它們不是對(duì)象钮呀。JavaScript使它們看上去像對(duì)象一樣,以此來提高語言上的一致性體驗(yàn)昨凡。
1.3 引用類型
引用類型是指JavaScript中的對(duì)象爽醋,同時(shí)也是你在該語言中能找到最接近類的東西。
引用值是引用類型的實(shí)例土匀,也是對(duì)象的同義詞(后面將用對(duì)象指代引用值)子房。對(duì)象是屬性的無序列表。屬性包含鍵(始終是字符串)和值就轧。如果一個(gè)屬性的值是函數(shù)证杭,它就被稱為方法。除了函數(shù)可以運(yùn)行以外妒御,一個(gè)包含數(shù)組的屬性和一個(gè)包含函數(shù)的屬性沒有什么區(qū)別解愤。
創(chuàng)建對(duì)象
有時(shí)候,把JavaScript對(duì)象想象成哈希表可以幫助你更好地理解對(duì)象結(jié)構(gòu)乎莉。
JavaScript 有好幾種方法可以創(chuàng)建對(duì)象送讲,或者說實(shí)例化對(duì)象。第一種是使用 new
操作符和構(gòu)造函數(shù)惋啃。
構(gòu)造函數(shù)就是通過 new
操作符來創(chuàng)建對(duì)象的函數(shù)——任何函數(shù)都可以是構(gòu)造函數(shù)哼鬓。根據(jù)命名規(guī)范,JavaScript中的構(gòu)造函數(shù)用首字母大寫來跟非構(gòu)造函數(shù)進(jìn)行區(qū)分边灭。
var object = new Object();
因?yàn)橐妙愋筒辉僮兞恐兄苯颖4鎸?duì)象异希,所以本例中的 object
變量實(shí)際上并不包含對(duì)象的實(shí)例,而是一個(gè)指向內(nèi)存中實(shí)際對(duì)象所在位置的指針(或者說引用)绒瘦。這是對(duì)象和原始值之間的一個(gè)基本差別称簿,原始值是直接保存在變量中。
當(dāng)你將一個(gè)對(duì)象賦值給變量時(shí)惰帽,實(shí)際是賦值給這個(gè)變量一個(gè)指針憨降。這意味著,將一個(gè)變量賦值給另外一個(gè)變量時(shí)该酗,兩個(gè)變量各獲得了一份指針的拷貝授药,指向內(nèi)存中的同一個(gè)對(duì)象。
var obj1 = new Object();
var obj2 = obj1;
對(duì)象引用解除
JavaScript語言有垃圾收集的功能呜魄,因此當(dāng)你使用引用類型時(shí)無需擔(dān)心內(nèi)存分配烁焙。但最好在不使用對(duì)象時(shí)將其引用解除,讓垃圾收集器對(duì)那塊內(nèi)存進(jìn)行釋放耕赘。解除引用的最佳手段是將對(duì)象變量設(shè)置為 null
骄蝇。
var obj1 = new Object();
// dosomething
obj1 = null; // dereference
添加刪除屬性
在JavaScript中,你可以隨時(shí)添加和刪除其屬性操骡。
var obj1 = new Object();
var obj2 = obj1;
obj1.myCustomProperty = "Awsome!";
console.log(obj2.myCustomProperty); // "Awsome!" 因?yàn)閛bj1和obj2指向同一個(gè)對(duì)象九火。
1.4 內(nèi)建類型實(shí)例化
內(nèi)建類型如下:
- Array 數(shù)組類型赚窃,以數(shù)字為索引的一組值的有序列表
- Date 日期和時(shí)間類型
- Error 運(yùn)行期錯(cuò)誤類型
- Function 函數(shù)類型
- Object 通用對(duì)象類型
- RegExp 正則表達(dá)式類型
可使用 new
來實(shí)例化每一個(gè)內(nèi)建引用類型:
var items = new Array();
var new = new Date();
var error = new Error("Something bad happened.");
var func = new Function("console.log('HI');");
var object = new Object();
var re = new RegExp();
字面形式
內(nèi)建引用類型有字面形式。字面形式允許你在不需要使用 new
操作符和構(gòu)造函數(shù)顯示創(chuàng)建對(duì)象的情況下生成引用值岔激。屬性的鍵可以是標(biāo)識(shí)符或字符串(若含有空格或其他特殊字符)
var book = {
name: "Book_name",
year: 2016
}
上面代碼與下面這段代碼等價(jià):
var book = new Object();
book.name = "Book_name";
book.year = 2016;
雖然使用字面形式并沒有調(diào)用 new Object()勒极,但是JavaScript引擎背后做的工作和 new Object()一樣,除了沒有調(diào)用構(gòu)造函數(shù)虑鼎。其他引用類型的字面形式也是如此辱匿。
1.5 訪問屬性
可通過 .
和 中括號(hào)
訪問對(duì)象的屬性。
中括號(hào)[]
在需要?jiǎng)討B(tài)決定訪問哪個(gè)屬性時(shí)炫彩,特別有用匾七。因?yàn)槟憧梢杂?strong>變量而不是字符串字面形式來指定訪問的屬性。
1.6 鑒別引用類型
函數(shù)是最容易鑒別的引用類型江兢,因?yàn)閷?duì)函數(shù)使用 typeof
操作符時(shí)昨忆,返回"function"。
function reflect(value){
return value;
}
console.log(typeof reflect); // "function"
對(duì)其他引用類型的鑒別則較為棘手杉允,因?yàn)閷?duì)于所有非函數(shù)的引用類型邑贴,typeof
返回 object
。為了更方便地鑒別引用類型叔磷,可以使用 JavaScript 的 instanceof
操作符拢驾。
var items = [];
var obj = {};
function reflect(value){
return value;
}
console.log(items instanceof Array); // true;
console.log(obj instanceof Object); // true;
console.log(reflect instanceof Function); // true;
instanceof
操作符可鑒別繼承類型。這意味著所有對(duì)象都是 Oject
的實(shí)例改基,因?yàn)樗幸妙愋投祭^承自 Object
繁疤。
雖然 instanceof 可以鑒別對(duì)象類型(如數(shù)組),但是有一個(gè)列外寥裂。JavaScript 的值可以在同一個(gè)網(wǎng)頁的不用框架之間傳來傳去。由于每個(gè)網(wǎng)頁擁有它自己的全局上下文——Object案疲、Array以及其他內(nèi)建類型的版本封恰。所以當(dāng)你把一個(gè)對(duì)象(如數(shù)組)從一個(gè)框架傳到另外一個(gè)框架時(shí),instanceof就無法識(shí)別它褐啡。
1.8 原始封裝類型
原始封裝類型有 3
種:String诺舔、Number 和 Boolean。
當(dāng)讀取字符串备畦、數(shù)字或布爾值時(shí)低飒,原始封裝類型將被自動(dòng)創(chuàng)建。
var name = "Nicholas";
var firstChar = name.charAt(0); // "N"
這在背后發(fā)生的事情如下:
var name = "Nichola";
var temp = new String(name);
var firstChar = temp.charAt(0);
temp = null;
由于第二行把字符串當(dāng)成對(duì)象使用懂盐,JavaScript引擎創(chuàng)建了一個(gè)字符串的實(shí)體讓 charAt(0)
可以工作褥赊。字符串對(duì)象的存在僅用于該語句并在隨后銷毀(一種被稱為自動(dòng)打包的過程)。為了測(cè)試這一點(diǎn)莉恼,試著給字符串添加一個(gè)屬性看看它是不是對(duì)象拌喉。
var name = "Nicholas";
name.last = "Zakas";
console.log(name.last); // undefined;
下面是在JavaScript引擎中實(shí)際發(fā)生的事情:
var name = "Nicholas";
var temp = new String(name);
temp.last = "Zakas";
temp = null; // temporary object destroyed
var temp = new String(name);
console.log(temp.last);
temp = null;
新屬性 last
實(shí)際上是在一個(gè)立刻就被銷毀的臨時(shí)對(duì)象上而不是字符串上添加速那。之后當(dāng)你試圖訪問該屬性時(shí),另一個(gè)不同的臨時(shí)對(duì)象被創(chuàng)建尿背,而新屬性并不存在端仰。
雖然原始封裝類型會(huì)被自動(dòng)創(chuàng)建,在這些值上進(jìn)行 instanceof
檢查對(duì)應(yīng)類型的返回值卻是 false
田藐。
這是因?yàn)?strong>臨時(shí)對(duì)象僅在值被讀取時(shí)創(chuàng)建荔烧。instanceof
操作符并沒有真的讀取任何東西,也就沒有臨時(shí)對(duì)象的創(chuàng)建汽久。
當(dāng)然你也可以手動(dòng)創(chuàng)建原始封裝類型鹤竭。
var str = new String("me");
str.age = 18;
console.log(typeof str); // object
console.log(str.age); // 18
如你所見,手動(dòng)創(chuàng)建原始封裝類型實(shí)際會(huì)創(chuàng)建出一個(gè) object
回窘。這意味著 typeof
無法鑒別出你實(shí)際保存的數(shù)據(jù)的類型诺擅。
另外,手動(dòng)創(chuàng)建原始封裝類型和使用原始值是有一定區(qū)別的啡直。所以盡量避免使用烁涌。
var found = new Boolean(false);
if(found){
console.log("Found"); // 執(zhí)行到了,盡管對(duì)象的值為 false
}
這是因?yàn)橐粋€(gè)對(duì)象(如 {}
)在條件判斷語句中總被認(rèn)為是 true
;
MDN:Any object whose value is not undefined or null, including a Boolean oject whose value is false, evaluates to true when passed to a conditional statement.
1.9 總結(jié)
第一章的東西都是我們一些比較熟悉的知識(shí)酒觅。但是也有一些需要注意的地方:
- 正確區(qū)分原始類型和引用類型
- 對(duì)于
5
種原始類型都可以用typeof來鑒別撮执,而空類型必須直接跟null
進(jìn)行全等比較。 - 函數(shù)也是對(duì)象舷丹,可用
typeof
鑒別抒钱。其它引用類型,可用instanceof
和一個(gè)構(gòu)造函數(shù)來鑒別颜凯。(當(dāng)然可以用Object.prototype.toString.call()
鑒別谋币,它會(huì)返回[object Array]之類的)。 - 為了讓原始類型看上去更像引用類型症概,JavaScript提供了
3
種封裝類型蕾额。JavaScript會(huì)在背后創(chuàng)建這些對(duì)象使得你能夠像使用普通對(duì)象那樣使用原始值。但這些臨時(shí)對(duì)象在使用它們的語句結(jié)束時(shí)就立刻被銷毀彼城。雖然可手動(dòng)創(chuàng)建诅蝶,但不建議。