理解Javascript_04_數據模型
本文主要描述Javascript的數據模型,即對Javascript所支持的數據類型的一個全局概纜。文章比較的理論化,非常深入,因此不易理解,但務必對數據模型有一個映象,因為他是理解Javascript對象模型與Javascript執行模型的基礎。
基本的數據類型
原始類型(簡單數據類型、基本數據類型)
Undefined類型: 表示聲明了變量但未對其初始化時賦予該變量的值。undefined為Undefined類型下的唯一的一個值。
Null類型:用于表示尚未存在的對象。Null類型下也只有一個專用值null。
Boolean類型:有兩個值true和false,主要用于條件判斷,控制執行流程。
Number類型:代表數字(即包括32的整數,也包括64位的浮點數)
String類型:用于代表字符串。
注:關于undefined與null的關系,可以參見《理解Javascript_02_理解undefined和null》一文。
對象:一個無序屬性的集合,這些屬性的值為簡單數據類型、對象或者函數。注:這里對象并不特指全局對象Object.
函數:函數是對象的一種,實現上內部屬性[[Class]]值為"Function",表明它是函數類型,除了對象的內部屬性方法外,還有 [[Construct]]、[[Call]]、[[Scope]]等內部屬性。函數作為函數調用與構造器(使用new關鍵字創建實例對象)的處理機制不一樣(Function對象除外),內部方法[[Construct]]用于實現作為構造器的邏輯,方法[[Call]]實現作為函數調用的邏輯。同上,這里的函數并不特指全局對象Function。
注:關于函數與對象的關系可以引申出很多問題,現在可以不去深究函數實現內部的細節,這將在以后的文章中探討。
注:"基本的數據類型"與"基本數據類型"的概念不一樣,"基本的數據類型"指的是最常用的數據類型,"基本數據類型"指的是原始類型(關于原始類型與引用類型的問題,具體可以參見《理解Javascript_01_理解內存分配》一文)。
內置數據類型(內置對象)
Function: 函數類型的用戶接口。
Object: 對象類型的用戶接口。
Boolean, Number, String: 分別為這三種簡單數值類型的對象包裝器,對象包裝在概念上有點類似C#/Java中的Box/Unbox。
Date, Array, RegExp: 可以把它們看作是幾種內置的擴展數據類型。
首先,Function, Object, Boolean, Number, String, Date, Array, RegExp等都是JavaScript語言的內置對象,它們都可以看作是函數的派生類型,例如Number instanceof Function為true,Number instanceof Object為true。在這個意義上,可以將它們跟用戶定義的函數等同看待。
其次,它們各自可以代表一種數據類型,由JS引擎用native code或內置的JS代碼實現,是暴露給開發者對這些內置數據類型進行操作的接口。在這個意義上,它們都是一種抽象的概念,后面隱藏了具體的實現機制。在每一個提到Number, Function等單詞的地方,應該迅速的在思維中將它們實例化為上面的兩種情況之一。
數據類型實現模型描述
注:圖片來源于http://www.cnblogs.com/riccc
Build-in *** data structure: 指JS內部用于實現***類型的數據結構,由宿主環境(瀏覽器)提供,這些結構我們基本上無法直接操作。
Build-in *** object: 指JS內置的Number, String, Boolean等這些對象,這是JS將內部實現的數據類型暴露給開發者使用的接口。
Build-in *** constructor: 指JS內置的一些構造器,用來構造相應類型的對象實例。它們被包裝成函數對象暴露出來,例如我們可以使用下面的方法訪問到這些函數對象:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 //access the build-in number constructor var number = new Number(123); var numConstructor1 = number.constructor; //or var numConstructor2 = new Object(123).constructor; //both numConstructor1 and numConstructor2 are the build-in Number constructor numConstructor1 == numConstructor2 //result: true //access the build-in object constructor var objConstructor1 = {}.constructor; //or var objConstructor2 = new Object().constructor; //both objConstructor1 and objConstructor2 are the build-in Object constructor objConstructor1==objConstructor2 //result: true
關于"接口"的解釋:簡單的說,接口就是可以調用的方法。如:
//String就是一個接口,它定義了String的行為.它可以由外部調用 var str = new String('笨蛋的座右銘'); //我們自已定義一個接口 function say(msg){ alert(msg); } //調用定義的接口 say("hello world");
注:完全理解接口的概念需要有一定的強類型語言編程經驗(java/c#),因為本文已經夠復雜了,就不再將問題復雜化了。所以對于接口的答案并不是很嚴謹,但已經夠用了,望高人見諒。
關于簡單數據類型的對象化
這是一個細微的地方,下面描述對于Boolean, String和Number這三種簡單數值類型都適用,以Number為例說明。JS規范要求: 使用var num1=123;這樣的代碼,直接返回基本數據類型,就是說返回的對象不是派生自Number和Object類型,用num1 instanceof Object測試為false;使用new關鍵字創建則返回Number類型,例如var num2=new Number(123); num2 instanceof Number為true。
將Number當作函數調用,返回結果會轉換成簡單數值類型。下面是測試代碼:
//Passed in FF2.0, IE7, Opera9.25, Safari3.0.4 var num1 = new Number(123); //num1 derived from Number & Object num1 instanceof Number //result: true num1 instanceof Object //result: true //convert the num1 from Number type to primitive type, so it's no longer an instance of Number or Object num1 = Number(num1); num1 instanceof Number //result: false num1 instanceof Object //result: false var num2 = 123; //num2 is a primitive type num2 instanceof Number //result: false num2 instanceof Object //result: false
結論:雖然我們得到了一個簡單數值類型,但它看起來仍然是一個JS Object對象,具有Object以及相應類型的所有屬性和方法,使用上基本沒有差別,唯一不同之處是instanceof的測試結果。由此也就產生了一個概念"Literal Syntax"
Literal Syntax
在簡單數據類型的對象化一節中,我們也看到了簡單類型和其包裝類型可以相互轉換,并且兩者之間的行為相同。但兩者相比較,明顯簡單類型的定義更加輕量,因此我們可以用簡單類型定義替換相應的包裝類型定義。如:
Number: var i = 100; //替代var i = new Number(100); Boolean: var b = true; //替代var b = new Boolean(true); String: var str = 'this is a string.'; //替代var str = new String('this is a string');
其實這種類似于var i = 100;var b=true;var str='this is a string'這種定義方式就叫做Literal Syntax。難道就只有簡單數據類型才有這種Literal Syntax的表示方法嗎!不是的,復合數據類型同樣有。
//對象定義的字面量表示法 var obj = {name:'笨蛋的座右銘',age:25} /* //對象的非字面量表示法 var obj = new Object(); obj.name = '笨蛋的座右銘'; obj.age = 25; */ //數組定義的字面量表示法 var arr = ['笨蛋的座右銘',25]; /* //數組的非字面量表示法 var arr = new Array(); arr[0]='笨蛋的座右銘']; arr[1]=25; */ //正則表達式字面量表式法 var reg = /\d+/; /* //正則表達式非字面量表式法 var reg = new RegExp("\d+"); */
那函數呢!其實函數的定義已經是Literal Syntax的表示形式了。在實際工作中,我們建議盡量采用Literal Syntax的形式定義變量,因為這樣更簡單,更高效。