淺析JavaScript的對象系統(tǒng)

之前在《JavaScript的數(shù)據(jù)類型》這篇文章里說過,Object對于JavaScript的語言結(jié)構(gòu)來說意義不一般。為什么這么說呢?你肯定在不少地方看到過這句話:JavaScript中一切皆對象。這句話沒什么錯(cuò)。你知道,JS連函數(shù)都有其屬性和方法;當(dāng)你定義一個(gè)基本類型值的字符串,可是你卻可以將這個(gè)作為基本類型值的字符串當(dāng)作對象一樣去調(diào)用substr()方法。你有想過其中的原因嗎?一切的答案都在JavaScript對象系統(tǒng)(聲明一下,JavaScript對象系統(tǒng)這個(gè)詞并不是什么統(tǒng)一定義,只是感覺在這里這么叫比較合適。)中。

請看下面這張圖。

JS對象系統(tǒng).png

這張結(jié)構(gòu)圖其實(shí)不規(guī)范,也并不完善,但是用來整體性地理解JS的Obejct是沒問題的。下面就結(jié)合圖來分析一下JS對象系統(tǒng)的結(jié)構(gòu)。

  • 本地對象(native object)

圖中的“本地對象”這個(gè)叫法并不唯一,也可以叫做“原生對象”、“內(nèi)置對象”、“內(nèi)建對象”,但其實(shí)指的都是 native object。不必糾結(jié)這個(gè),只是翻譯不一導(dǎo)致叫法上的差異 - -,其實(shí)想表達(dá)的意思都是一致的。ECMA-262對于native object的定義為:

獨(dú)立于宿主環(huán)境的 ECMAScript 實(shí)現(xiàn)提供的對象

我們知道,“宿主”一般指瀏覽器,而“獨(dú)立于宿主環(huán)境”,也就是說與瀏覽器環(huán)境不相關(guān),這就是說:native object與瀏覽器不相關(guān),只要這個(gè)瀏覽器按照 ECMAScript 規(guī)范實(shí)現(xiàn)了JS,那就必然實(shí)現(xiàn)了規(guī)范中的所有native object。換句話說就是:本地對象(native object)是語言本身實(shí)現(xiàn)和提供的對象,和語言運(yùn)行在哪個(gè)環(huán)境無關(guān)。也就是說,不管你的JS代碼在哪里跑,你都可以new出 native object 并使用它。照此理解,我覺得其實(shí)把 native object 翻譯成“原生對象”最合意。

  • Object

到目前為止,我們看到的大多數(shù)引用類型值都是 Object 類型的實(shí)例;而且,Object 也是 ECMAScript 中使用最多的一個(gè)類型。雖然 Object 的實(shí)例不具備多少功能,但對于在應(yīng)用程序中存儲(chǔ)和傳輸數(shù)據(jù)而言,它們確實(shí)是非常理想的選擇?!禞avaScript高級程序設(shè)計(jì)(第3版)》

所有其他對象類型的基礎(chǔ)類型。提供了toString()等基礎(chǔ)方法。

  • Array

ECMAScript定義的數(shù)組類型。

  • Date

ECMAScript定義的日期類型。

  • RegExp

ECMAScript定義的正則類型。

  • Function

說起來 ECMAScript 中什么最有意思,我想那莫過于函數(shù)了——而有意思的根源,則在于函數(shù)實(shí)際上是對象。每個(gè)函數(shù)都是 Function 類型的實(shí)例,而且都與其他引用類型一樣具有屬性和方法?!禞3》

沒錯(cuò),F(xiàn)unction 是一種對象類型,function 是一個(gè)對象實(shí)例。見如下代碼:

console.log(Function() instanceof Function);    //true(構(gòu)造函數(shù)Function()是Function類型的實(shí)例)
console.log(Function() instanceof Object);    //true(構(gòu)造函數(shù)Function()當(dāng)然也是Object類型的實(shí)例)

事實(shí)上,任何一個(gè)函數(shù)都是函數(shù)對象的實(shí)例,而函數(shù)類型本身就是一種對象類型,所以:函數(shù)也是對象。

  • Error 等各種錯(cuò)誤類對象

Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError 等錯(cuò)誤類型的對象。

  • String Number Boolean

為了方便操作對應(yīng)的3種基本類型值而定義的3種對象類型,使得可以像使用對象一般操作字符串、數(shù)值、布爾值。請理解一下下面的代碼:

var str = new String();
str.detail = '我是字符串,但實(shí)際上是一個(gè)對象,否則我怎么可以設(shè)置屬性?';
console.log(str);    //String {detail: "我是字符串,但實(shí)際上是一個(gè)對象,否則我怎么可以設(shè)置屬性?", length: 0, [[PrimitiveValue]]: ""}
console.log(str.detail);    //'我是字符串,但實(shí)際上是一個(gè)對象,否則我怎么可以設(shè)置屬性?'

前面說到過,你聲明并初始化一個(gè)字符串后,通常會(huì)理所應(yīng)當(dāng)?shù)貙⑵湟暈橐粋€(gè)對象直接使用:

var str = 'Hello World';
str = str.substr(0,5);    //調(diào)用substr()方法截取字符串
console.log(str);    //'Hello'

代碼沒有任何問題,我們也經(jīng)常這么寫。但稍微思考一下你就會(huì)發(fā)現(xiàn),str本身作為一個(gè)字符串,它只是一個(gè)基本數(shù)據(jù)類型的值,憑什么可以像一個(gè)對象一樣直接調(diào)用方法?講道理,str僅僅是一個(gè)值,哪里來的什么substr()方法?原因就在對象系統(tǒng)中的那個(gè)基本包裝類型對象。

實(shí)際上,每當(dāng)讀取一個(gè)基本類型值的時(shí)候,后臺(tái)就會(huì)創(chuàng)建一個(gè)對應(yīng)的基本包裝類型的對象,從而讓我們能夠調(diào)用一些方法來操作這些數(shù)據(jù) ——《J3》

書上一語道明。注意一下這句話中的“讀取”和“對應(yīng)”兩個(gè)詞?!白x取”是指進(jìn)入讀取模式,即從內(nèi)存中去讀取這個(gè)字符串值的時(shí)候;“對應(yīng)”指的是基本類型值是哪種類型,后臺(tái)自動(dòng)給你創(chuàng)建的基本包裝類型就是哪種對應(yīng)的類型,比如這里的基本類型值是String類型,那后臺(tái)創(chuàng)建的基本包裝類型也就是String包裝類型。

因?yàn)檫@一機(jī)制,上面代碼的實(shí)際執(zhí)行情況其實(shí)是這樣的:

var str = 'Hello World';
//str = str.substr(0,5);    //(str進(jìn)入讀取模式,準(zhǔn)備創(chuàng)建基本包裝類型對象)

var _str = new String('Hello World');
str = _str.substr(0, 5);
_str = null;    //使用完后臺(tái)會(huì)立即將該包裝對象銷毀

console.log(str);    //'Hello'

這里分析的是String包裝類型,還有Number包裝類型和Boolean包裝類型也是完全一樣的原理。

  • 內(nèi)置對象(單體內(nèi)置對象)(built-in object)

又來一個(gè)內(nèi)置對象?別混淆,這個(gè)是 built-in object ,上面說過的那個(gè)是 native object 。要怪就怪漢語博大精深,這兩個(gè)詞你來給我翻一下 - - ?!禞3》這本書上又將 built-in object 定義為“單體內(nèi)置對象”,我也搞不懂這個(gè)“單體”又是什么意思。。

ECMA-262對內(nèi)置對象的定義是:“由ECMAScript實(shí)現(xiàn)提供的、不依賴于宿主環(huán)境的對象,這些對象在ECMAScript程序執(zhí)行之前就已經(jīng)存在了。”意思就是說,開發(fā)人員不必顯式地實(shí)例化內(nèi)置對象,因?yàn)樗鼈円呀?jīng)實(shí)例化了?!禞3》

這是ECMA-262對于 built-in object 的定義。對比 native object 的定義:“獨(dú)立于宿主環(huán)境的 ECMAScript 實(shí)現(xiàn)提供的對象”,可以看出,built-in object 是更加特殊的 native object,built-in object 屬于 native object。

ECMA-262定義了兩種 built-in object:Global對象、Math對象。

Global對象

事實(shí)上,js中并不存在所謂的全局變量以及完全獨(dú)立的函數(shù)。沒錯(cuò),你定義的所有全局變量其實(shí)都是Global對象的屬性,你在全局作用域下定義的所有函數(shù)其實(shí)都是Global對象的方法。

ECMAScript中的 Global 對象在某種意義上是作為一個(gè)終極的“兜底兒對象” 來定義的。換句話說,不屬于任何其他對象的屬性和方法,終都是它的屬性和方法。事實(shí)上,沒有全局變量或全局函數(shù);所有在全局作用域中定義的屬性和函數(shù),都是 Global 對象的屬性。——《J3》

parseInt()、eval()、encodeURIComponent()這些函數(shù)實(shí)際上都是Global對象的方法,在使用這些方法的時(shí)候無需通過對象去調(diào)用,直接就可以使用。引文說過:“這些對象在ECMAScript程序執(zhí)行之前就已經(jīng)存在了?!边@也算 build-in object 的一個(gè)特別之處,《J3》將 build-in object 定義為“單體內(nèi)置對象”,這個(gè)“單體”是否就是指無需手動(dòng)實(shí)例化這一點(diǎn)?

Global對象的所有屬性如下表:

Global對象的屬性

好吧,特殊值undefined居然是Global對象的一個(gè)屬性值 - -。乍看之下可能會(huì)有點(diǎn)詫異,不過仔細(xì)想想,我們之所以說JavaScript中一切皆對象是有原因的,你看,就連undefined都是對象的屬性值。從表中還可以看到,所有原生引用類型的構(gòu)造函數(shù)也都是Global對象的屬性。這怎么理解?其實(shí)也很好理解,比如說當(dāng)你想要?jiǎng)?chuàng)建一個(gè)日期類型的變量時(shí),直接上構(gòu)造函數(shù)var date = new Date();就可以了,可是你想過沒有,Date()構(gòu)造函數(shù)本身是一個(gè)函數(shù),而前面我們說過,JS并不存在完全獨(dú)立的函數(shù),任何函數(shù)其實(shí)都是掛在某個(gè)對象下的方法,哪怕它是一個(gè)構(gòu)造函數(shù)。而這些原生構(gòu)造函數(shù),其“掛載”的對象正是Global對象。

介紹了這么多關(guān)于Global對象的內(nèi)容,是不是還是不清楚它是個(gè)什么玩意兒?這是因?yàn)槲覀冊跒g覽器中并不能直接訪問這個(gè)對象。好在瀏覽器為我們實(shí)現(xiàn)了一個(gè)包含了Global對象的window對象。

ECMAScript 雖然沒有指出如何直接訪問 Global 對象,但 Web 瀏覽器都是將這個(gè)全局對象作為 window 對象的一部分加以實(shí)現(xiàn)的。因此,在全局作用域中聲明的所有變量和函數(shù),就都成為了 window 對象的屬性?!禞3》

window對象包含了Global對象的所有內(nèi)容,同時(shí)擴(kuò)展了大量自身需要的屬性和方法(比如常用的alert())。因此,在瀏覽器中我們可以通過window對象隨意訪問上述的屬性和方法。請看下面代碼并加以理解:

var date = new window.Date();    //window是對Global對象的擴(kuò)展實(shí)現(xiàn),構(gòu)造函數(shù)Date()確實(shí)是掛在window上面的
console.log(date);    //Thu Jul 21 2016 16:52:08 GMT+0800 (中國標(biāo)準(zhǔn)時(shí)間)

你可以自己再做下測試,控制臺(tái)打印一下window,可以發(fā)現(xiàn)它確確實(shí)實(shí)包含了上述的所有屬性和構(gòu)造函數(shù)。

Math對象

Math對象是一個(gè)保存著必要的數(shù)學(xué)操作的 build-in object。里面保存著常量e、圓周率π等的值作為屬性,同時(shí)提供了取整Math.floor()、取隨機(jī)數(shù)Math.random()、求平方根等等數(shù)學(xué)方法。和Global對象一樣,這些屬性和方法都可以在任何時(shí)候任何地方直接訪問和調(diào)用,只不過其形式統(tǒng)一為:Math.***。

宿主對象(host object)


宿主對象是指宿主環(huán)境所實(shí)現(xiàn)和提供的對象。所有非本地對象(native object)都是宿主對象。我之所以在結(jié)構(gòu)圖中把自定義對象掛到了宿主對象下正是基于此,不過這并非定死的,你若將自定義對象拎出來和本地對象、宿主對象并列放也并非不可。

所謂宿主,就是指JS代碼所在的運(yùn)行環(huán)境。對于瀏覽器環(huán)境而言,我們顯示一個(gè)頁面需要HTML,所以瀏覽器實(shí)現(xiàn)了DOM對象 —— window.document;我們還需要瀏覽器本身給我們提供一些必要的東西,比如URL地址相關(guān)的location、設(shè)備屏幕相關(guān)的screen等,所以瀏覽器又為我們提供了BOM對象 —— window。這些對象,就是host object。

等等,怎么window對象又出場了?上面不是說過了,window對象是瀏覽器對Global對象的擴(kuò)展實(shí)現(xiàn),是Global對象的超集,那這玩意兒究竟該掛到build-in object上,還是宿主對象下的BOM上?答案當(dāng)然是后者??梢悦鞔_的是,window對象本就是瀏覽器所實(shí)現(xiàn)的,那它當(dāng)然屬于瀏覽器對象模型(BOM)了!只不過,window把ECMAScript規(guī)定的Global對象也給一并實(shí)現(xiàn)了而已。還有,DOM對象即是window.document,而window.document就是DOM的根節(jié)點(diǎn),從這點(diǎn)來講,我們可以理解為BOM包含了DOM 。

如此,我們終于找到最終對象了 - -,它正是 BOM —— window。你幾乎可以在這個(gè)對象中找到一切。全局變量、自定義對象、JSON對象(ECMA262-5引入為規(guī)范)、Math對象、原生構(gòu)造函數(shù)、Global的東西。。。

萬物合一,世界清靜了。

最后,可以把最開始給的圖修正更新一下了:

QQ截圖20160722114051.png

(注:ECMA262-5已將JSON對象納入native object)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第5章 引用類型(返回首頁) 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學(xué)一百閱讀 3,667評論 0 4
  • ECMAScript關(guān)鍵字 delete do else finally function in instance...
    doudou2閱讀 756評論 0 0
  • 本章內(nèi)容 理解對象屬性 理解并創(chuàng)建對象 理解繼承 面向?qū)ο笳Z言有一個(gè)標(biāo)志,那就是它們都有類的概念,而通過類可以創(chuàng)建...
    悶油瓶小張閱讀 957評論 0 1
  • 中山有女嬌無雙,清眸皓齒垂明珰。 我出生于南京,父親是做官的,卻是不幸早亡,家財(cái)被吞沒,我和妹妹流落街頭。 我只好...
    問心淺笑閱讀 516評論 12 11
  • 裴晨旭,現(xiàn)年33歲,2008年畢業(yè)于鄭州大學(xué)升達(dá)經(jīng)貿(mào)學(xué)院財(cái)務(wù)管理專業(yè),同年到我院財(cái)務(wù)科參加工作,第二年調(diào)至...
    平_fe98閱讀 1,401評論 12 8

友情鏈接更多精彩內(nèi)容