之前在《JavaScript的數(shù)據(jù)類型》這篇文章里說過,Object對于JavaScript的語言結(jié)構(gòu)來說意義不一般括饶。為什么這么說呢?你肯定在不少地方看到過這句話:JavaScript中一切皆對象入录。這句話沒什么錯。你知道佳镜,JS連函數(shù)都有其屬性和方法僚稿;當你定義一個基本類型值的字符串,可是你卻可以將這個作為基本類型值的字符串當作對象一樣去調(diào)用substr()
方法蟀伸。你有想過其中的原因嗎蚀同?一切的答案都在JavaScript對象系統(tǒng)(聲明一下缅刽,JavaScript對象系統(tǒng)這個詞并不是什么統(tǒng)一定義,只是感覺在這里這么叫比較合適蠢络。)中衰猛。
請看下面這張圖。
這張結(jié)構(gòu)圖其實不規(guī)范刹孔,也并不完善啡省,但是用來整體性地理解JS的Obejct
是沒問題的。下面就結(jié)合圖來分析一下JS對象系統(tǒng)的結(jié)構(gòu)髓霞。
- 本地對象(native object)
圖中的“本地對象”這個叫法并不唯一卦睹,也可以叫做“原生對象”、“內(nèi)置對象”方库、“內(nèi)建對象”结序,但其實指的都是 native object。不必糾結(jié)這個纵潦,只是翻譯不一導致叫法上的差異 - -徐鹤,其實想表達的意思都是一致的。ECMA-262對于native object的定義為:
獨立于宿主環(huán)境的 ECMAScript 實現(xiàn)提供的對象
我們知道邀层,“宿主”一般指瀏覽器凳干,而“獨立于宿主環(huán)境”,也就是說與瀏覽器環(huán)境不相關被济,這就是說:native object與瀏覽器不相關救赐,只要這個瀏覽器按照 ECMAScript 規(guī)范實現(xiàn)了JS,那就必然實現(xiàn)了規(guī)范中的所有native object只磷。換句話說就是:本地對象(native object)是語言本身實現(xiàn)和提供的對象经磅,和語言運行在哪個環(huán)境無關。也就是說钮追,不管你的JS代碼在哪里跑预厌,你都可以new
出 native object 并使用它。照此理解元媚,我覺得其實把 native object 翻譯成“原生對象”最合意轧叽。
Object
到目前為止,我們看到的大多數(shù)引用類型值都是 Object 類型的實例刊棕;而且炭晒,Object 也是 ECMAScript 中使用最多的一個類型。雖然 Object 的實例不具備多少功能甥角,但對于在應用程序中存儲和傳輸數(shù)據(jù)而言网严,它們確實是非常理想的選擇∴臀蓿——《JavaScript高級程序設計(第3版)》
所有其他對象類型的基礎類型震束。提供了toString()
等基礎方法怜庸。
Array
ECMAScript定義的數(shù)組類型。
Date
ECMAScript定義的日期類型垢村。
RegExp
ECMAScript定義的正則類型割疾。
Function
說起來 ECMAScript 中什么最有意思,我想那莫過于函數(shù)了——而有意思的根源嘉栓,則在于函數(shù)實際上是對象杈曲。每個函數(shù)都是 Function 類型的實例,而且都與其他引用類型一樣具有屬性和方法胸懈〉F耍——《J3》
沒錯,F(xiàn)unction 是一種對象類型趣钱,function 是一個對象實例涌献。見如下代碼:
console.log(Function() instanceof Function); //true(構(gòu)造函數(shù)Function()是Function類型的實例)
console.log(Function() instanceof Object); //true(構(gòu)造函數(shù)Function()當然也是Object類型的實例)
事實上,任何一個函數(shù)都是函數(shù)對象的實例首有,而函數(shù)類型本身就是一種對象類型燕垃,所以:函數(shù)也是對象。
- Error 等各種錯誤類對象
Error井联、EvalError卜壕、RangeError、ReferenceError烙常、SyntaxError轴捎、TypeError、URIError 等錯誤類型的對象蚕脏。
-
String
Number
Boolean
為了方便操作對應的3種基本類型值而定義的3種對象類型侦副,使得可以像使用對象一般操作字符串、數(shù)值驼鞭、布爾值秦驯。請理解一下下面的代碼:
var str = new String();
str.detail = '我是字符串,但實際上是一個對象挣棕,否則我怎么可以設置屬性译隘?';
console.log(str); //String {detail: "我是字符串,但實際上是一個對象洛心,否則我怎么可以設置屬性固耘?", length: 0, [[PrimitiveValue]]: ""}
console.log(str.detail); //'我是字符串,但實際上是一個對象皂甘,否則我怎么可以設置屬性玻驻?'
前面說到過,你聲明并初始化一個字符串后偿枕,通常會理所應當?shù)貙⑵湟暈橐粋€對象直接使用:
var str = 'Hello World';
str = str.substr(0,5); //調(diào)用substr()方法截取字符串
console.log(str); //'Hello'
代碼沒有任何問題璧瞬,我們也經(jīng)常這么寫。但稍微思考一下你就會發(fā)現(xiàn)渐夸,str
本身作為一個字符串嗤锉,它只是一個基本數(shù)據(jù)類型的值,憑什么可以像一個對象一樣直接調(diào)用方法墓塌?講道理瘟忱,str
僅僅是一個值,哪里來的什么substr()
方法苫幢?原因就在對象系統(tǒng)中的那個基本包裝類型對象访诱。
實際上,每當讀取一個基本類型值的時候韩肝,后臺就會創(chuàng)建一個對應的基本包裝類型的對象触菜,從而讓我們能夠調(diào)用一些方法來操作這些數(shù)據(jù) ——《J3》
書上一語道明。注意一下這句話中的“讀取”和“對應”兩個詞哀峻∥邢啵“讀取”是指進入讀取模式,即從內(nèi)存中去讀取這個字符串值的時候剩蟀;“對應”指的是基本類型值是哪種類型催蝗,后臺自動給你創(chuàng)建的基本包裝類型就是哪種對應的類型,比如這里的基本類型值是String
類型育特,那后臺創(chuàng)建的基本包裝類型也就是String
包裝類型丙号。
因為這一機制,上面代碼的實際執(zhí)行情況其實是這樣的:
var str = 'Hello World';
//str = str.substr(0,5); //(str進入讀取模式缰冤,準備創(chuàng)建基本包裝類型對象)
var _str = new String('Hello World');
str = _str.substr(0, 5);
_str = null; //使用完后臺會立即將該包裝對象銷毀
console.log(str); //'Hello'
這里分析的是String
包裝類型槽袄,還有Number
包裝類型和Boolean
包裝類型也是完全一樣的原理。
- 內(nèi)置對象(單體內(nèi)置對象)(built-in object)
又來一個內(nèi)置對象锋谐?別混淆遍尺,這個是 built-in object ,上面說過的那個是 native object 涮拗。要怪就怪漢語博大精深乾戏,這兩個詞你來給我翻一下 - - ∪龋《J3》這本書上又將 built-in object 定義為“單體內(nèi)置對象”鼓择,我也搞不懂這個“單體”又是什么意思。就漾。
ECMA-262對內(nèi)置對象的定義是:“由ECMAScript實現(xiàn)提供的呐能、不依賴于宿主環(huán)境的對象,這些對象在ECMAScript程序執(zhí)行之前就已經(jīng)存在了“诔觯”意思就是說朗徊,開發(fā)人員不必顯式地實例化內(nèi)置對象,因為它們已經(jīng)實例化了偎漫∫遥——《J3》
這是ECMA-262對于 built-in object 的定義。對比 native object 的定義:“獨立于宿主環(huán)境的 ECMAScript 實現(xiàn)提供的對象”象踊,可以看出温亲,built-in object 是更加特殊的 native object,built-in object 屬于 native object杯矩。
ECMA-262定義了兩種 built-in object:Global對象栈虚、Math對象。
Global對象
事實上史隆,js中并不存在所謂的全局變量以及完全獨立的函數(shù)魂务。沒錯,你定義的所有全局變量其實都是Global對象的屬性逆害,你在全局作用域下定義的所有函數(shù)其實都是Global對象的方法头镊。
ECMAScript中的 Global 對象在某種意義上是作為一個終極的“兜底兒對象” 來定義的。換句話說魄幕,不屬于任何其他對象的屬性和方法相艇,終都是它的屬性和方法。事實上纯陨,沒有全局變量或全局函數(shù)坛芽;所有在全局作用域中定義的屬性和函數(shù),都是 Global 對象的屬性翼抠×——《J3》
parseInt()
、eval()
阴颖、encodeURIComponent()
這些函數(shù)實際上都是Global對象的方法活喊,在使用這些方法的時候無需通過對象去調(diào)用,直接就可以使用量愧。引文說過:“這些對象在ECMAScript程序執(zhí)行之前就已經(jīng)存在了钾菊。”這也算 build-in object 的一個特別之處偎肃,《J3》將 build-in object 定義為“單體內(nèi)置對象”煞烫,這個“單體”是否就是指無需手動實例化這一點?
Global對象的所有屬性如下表:
好吧累颂,特殊值undefined
居然是Global對象的一個屬性值 - -滞详。乍看之下可能會有點詫異,不過仔細想想,我們之所以說JavaScript中一切皆對象是有原因的料饥,你看蒲犬,就連undefined
都是對象的屬性值。從表中還可以看到稀火,所有原生引用類型的構(gòu)造函數(shù)也都是Global對象的屬性暖哨。這怎么理解赌朋?其實也很好理解凰狞,比如說當你想要創(chuàng)建一個日期類型的變量時,直接上構(gòu)造函數(shù)var date = new Date();
就可以了沛慢,可是你想過沒有赡若,Date()
構(gòu)造函數(shù)本身是一個函數(shù),而前面我們說過团甲,JS并不存在完全獨立的函數(shù)逾冬,任何函數(shù)其實都是掛在某個對象下的方法,哪怕它是一個構(gòu)造函數(shù)躺苦。而這些原生構(gòu)造函數(shù)身腻,其“掛載”的對象正是Global對象。
介紹了這么多關于Global對象的內(nèi)容匹厘,是不是還是不清楚它是個什么玩意兒嘀趟?這是因為我們在瀏覽器中并不能直接訪問這個對象。好在瀏覽器為我們實現(xiàn)了一個包含了Global對象的window
對象愈诚。
ECMAScript 雖然沒有指出如何直接訪問 Global 對象她按,但 Web 瀏覽器都是將這個全局對象作為 window 對象的一部分加以實現(xiàn)的。因此炕柔,在全局作用域中聲明的所有變量和函數(shù)酌泰,就都成為了 window 對象的屬性∝袄郏——《J3》
window
對象包含了Global對象的所有內(nèi)容陵刹,同時擴展了大量自身需要的屬性和方法(比如常用的alert()
)。因此欢嘿,在瀏覽器中我們可以通過window
對象隨意訪問上述的屬性和方法衰琐。請看下面代碼并加以理解:
var date = new window.Date(); //window是對Global對象的擴展實現(xiàn),構(gòu)造函數(shù)Date()確實是掛在window上面的
console.log(date); //Thu Jul 21 2016 16:52:08 GMT+0800 (中國標準時間)
你可以自己再做下測試际插,控制臺打印一下window
碘耳,可以發(fā)現(xiàn)它確確實實包含了上述的所有屬性和構(gòu)造函數(shù)。
Math對象
Math對象是一個保存著必要的數(shù)學操作的 build-in object框弛。里面保存著常量e辛辨、圓周率π等的值作為屬性,同時提供了取整Math.floor()
、取隨機數(shù)Math.random()
斗搞、求平方根等等數(shù)學方法指攒。和Global對象一樣,這些屬性和方法都可以在任何時候任何地方直接訪問和調(diào)用僻焚,只不過其形式統(tǒng)一為:Math.***
允悦。
宿主對象(host object)
宿主對象是指宿主環(huán)境所實現(xiàn)和提供的對象。所有非本地對象(native object)都是宿主對象虑啤。我之所以在結(jié)構(gòu)圖中把自定義對象掛到了宿主對象下正是基于此隙弛,不過這并非定死的,你若將自定義對象拎出來和本地對象狞山、宿主對象并列放也并非不可全闷。
所謂宿主,就是指JS代碼所在的運行環(huán)境萍启。對于瀏覽器環(huán)境而言总珠,我們顯示一個頁面需要HTML,所以瀏覽器實現(xiàn)了DOM對象 —— window.document
勘纯;我們還需要瀏覽器本身給我們提供一些必要的東西局服,比如URL地址相關的location
、設備屏幕相關的screen
等驳遵,所以瀏覽器又為我們提供了BOM對象 —— window
淫奔。這些對象,就是host object超埋。
等等搏讶,怎么window
對象又出場了?上面不是說過了霍殴,window
對象是瀏覽器對Global對象的擴展實現(xiàn)媒惕,是Global對象的超集,那這玩意兒究竟該掛到build-in object上来庭,還是宿主對象下的BOM上妒蔚?答案當然是后者≡鲁冢可以明確的是肴盏,window
對象本就是瀏覽器所實現(xiàn)的,那它當然屬于瀏覽器對象模型(BOM)了帽衙!只不過菜皂,window
把ECMAScript規(guī)定的Global對象也給一并實現(xiàn)了而已。還有厉萝,DOM對象即是window.document
恍飘,而window.document
就是DOM的根節(jié)點榨崩,從這點來講,我們可以理解為BOM包含了DOM 章母。
如此母蛛,我們終于找到最終對象了 - -,它正是 BOM —— window
乳怎。你幾乎可以在這個對象中找到一切彩郊。全局變量、自定義對象蚪缀、JSON
對象(ECMA262-5引入為規(guī)范)秫逝、Math
對象、原生構(gòu)造函數(shù)椿胯、Global的東西筷登。剃根。哩盲。
萬物合一,世界清靜了狈醉。
最后廉油,可以把最開始給的圖修正更新一下了:
(注:ECMA262-5已將JSON
對象納入native object)