天天看點

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

荒廢了好幾天,在宿舍悶了幾天了,一直想着回家放松,什麼也沒搞,論文就讓老師催吧。不過,閑的沒事幹的感覺真是不好,還是看看書,寫寫部落格吧,今天和大家說說函數的原型。

第一次看到這個的時候,沒太了解這個概念,其實也就是一個概念呗,沒啥神秘的。書上說每個函數都有一個prototype屬性(原型屬性),這個屬性是一個指針,指向一個對象(原型對象),這個對象包含這個函數建立的執行個體的共享屬性和方法。也就是說原型對象中的屬性和方法是所有執行個體共享的,打住,那我們就先建立一個函數看看,原型是什麼東東

看來,是這的有這麼一個屬性,可以看出是一個對象,但是預設的是一個空的對象,既然是一個對象,那我們就可以給它添加屬性和方法喽,試試看

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

View Code

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

我們成功的添加了屬性和方法,驕傲吧,對象中的屬性一會我們在解釋,現在看看我們修改之後的原型對象,有啥用呢?剛才我們說過,原型對象中的屬性和方法是這個函數new出來執行個體所共享的,那我們就new執行個體出來試試

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

看上面的圖,我們new出來兩個執行個體,這兩個執行個體中并沒有name對象和getName()方法,但是我們卻使用了該屬性和方法,就是因為函數的原型對象中存在這個屬性和方法,并且是每個執行個體都可以使用的,這就是原型的神秘之處。也就是說以後我們想在每一個執行個體中添加屬性和方法,那我們就把這個共有的屬性或方法直接添加到原型對象上就可以了。

現在我們知道了,函數有一個prototype屬性,這個屬性是一個指針,指向一個原型對象,這個原型對象中的屬性和方法是這個函數的執行個體所共有的。現在我們看看這個原型對象中的屬性,看這幅圖

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

我們給一個原型屬性添加屬性之後,這個對象就不是空得了,看第一個圖,上面列印出來的Object是空的,其實這個對象不是空的,隻是有些屬性沒有被枚舉出來,看下圖

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

嗯,這樣就對了,無論啥時候,隻要建立一個新函數,就為給這個函數建立一個prototype屬性,在預設的情況下,所有的原型對象會自動添加一個constructor屬性,從名字就可以看出來應該指向構造函數,看圖

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

指向了構造函數,也就是test.prototype.constructor === test。

屬性中還有一個__prototype__屬性,我們先放一下,我們還是看new出來那個執行個體的圖,這次我把屬性展開大家看看

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

我們分别列印了test的原型對象和一個執行個體對象,我們可以看到原型對象和執行個體對象中都有一個__proto__對象,但是指向不同,原型對象中的__proto__指向Object,而執行個體中的__proto__指向内部明叫test的對象,展開這個對象可以看到就是原型對象。就是說每一個執行個體中有一個__proto__屬性指向原型對象。是不是有點暈呢,畫個圖看看先

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

是不是清楚點了呢,每一個執行個體的内部都包含一個内部屬性__proto__,指向了構造函數的原型,就是這個屬性連接配接了執行個體和原型對象之間的關系,并且我們知道執行個體中不包含name屬性和getName方法,但是我們卻使用了getName(),就是通過這個__proto__屬性查找的。

大家應該發現了test的原型對象中也有一個__proto__屬性,這個屬性指向誰呢,我們來分析一下。我們知道了__proto__屬性存在一個執行個體中并指向的是一個原型對象,現在就是說test的原型對象時某一個對象的執行個體喽,因為它有一個__proto__屬性嘛。那test的原型對象是哪個對象的執行個體呢?我們知道javascript所有的對象都是基于Object這個對象的,都是Object的執行個體,我們大膽的猜測就是Oject的執行個體,我們展開這個屬性看看就知道我們猜的對不對了,看圖

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

 嗯,我們看到了__proto__屬性指向的對象中存在一些方法,這些方法就是我們前面介紹的Object對象的方法,好牛,我們猜對了。我們剛才說了一下,執行個體可以共享原型的方法和屬性,也就是test的原型可以使用Object原型中方法,而test的執行個體可以使用test的原型中的方法,也就是說test的執行個體可以使用Object原型中的方法,嗯,就是這樣,我們試一下,看代碼,使用一個簡單的函數試一下就知道了

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

我們使用了Object原型中的方法,我們再一次猜對了。現在我們把上面的圖補充完整

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

現在就完整了,這就是這個例子的完整原型圖形。我們以前說過,所有的應用類型都是繼承Object,所有函數的預設原型都是Object的執行個體,是以預設原型都包含一個預設指針指向Object.prototype。這就是我們所說的原型鍊,繼承就是通過原型鍊實作的,這就是所有的自定義類型都會繼承toString()和valueOf()等預設方法的根本原因。Object是所有引用類型的父類,可以這麼了解。

使用方法a.isprototypeOf(b),判斷對象a是否是執行個體b__proto__指向的原型對象,如果是傳回true,否則傳回false。看個例子

這個方法是檢測一個屬性是否存在執行個體中,存在原型中會傳回false,看例子

對象執行個體可以通路儲存在原型中的值,但是不能重寫原型中的值。每次要讀取某一個屬性時,都會執行一次搜尋:首先在對象本身開始查找,如果查找到了就傳回這個屬性,如果沒有找到,則繼續搜尋__proto__指向的原型對象,如果還沒有找到,則繼續搜尋原型對象中__proto__指向的原型對象,這樣一直疊代下去,這就是原型鍊的作用。看例子

我們已經知道怎麼給一個原型對象添加屬性和方法,但是大家都會想到一個簡單地方法來一起添加,就像這樣

我們思考一下,上面的代碼test.prorotype現在已經指向了一個新的對象,已經不是原來那個預設的原型對象了,原來的預設原型對象我們之前隻是添加屬性并沒有重寫它,所有他的内部屬性還是存在了,現在我們重寫了這個對象,即prototype指向了一個新的對象了,那他原來的屬性constructor就沒有了,如果我們以後會使用這個屬性,那我們應該人為的設定,例如

看個例子先

我們一般的時候肯定是先設定原型在建立執行個體,這在任何情況下都是沒有問題的。我們建立執行個體是在給原型添加屬性之後,即使這樣我們也可以在執行個體中使用這個屬性,這就是原型的動态性。這是由于在原型中查找值的過程是一次搜尋,所有你修改的屬性可以立即在執行個體中得到展現。但是重寫一個函數的原型就不是這樣了,看例子

出現了錯誤,也就是我先建立了一個執行個體,在重寫函數的原型對象這是不行的。原因是這樣的,由于執行個體中的__proto__指針隻是指向原型,而不是構造函數,上面的這段代碼中,我們建立執行個體的時候,這個執行個體中的__proto__指向的是函數的預設原型對象,當我們重寫了這個函數的原型對象時,雖然函數的prototype屬性指向了新的對象,但是執行個體中的已經建立好了,它__proto__并沒有改變,這個屬性還是指向的是預設的原型對象,所有它的内部沒有這個方法。但是如果在重寫原型之後建立一個執行個體的話,這個新的執行個體的__proto__指向的就是新的原型對象了,像這樣

原生對象和我們自定義對象一樣,都存在原型,原生對象的方法都存在原型中,這樣我們建立一個新的對象執行個體時,這些對象就會擁有這些方法,當然我們可擴充原型對象,這樣我們以後new出來的原型對象的執行個體就會共享這個方法或屬性了。

我們擴充一個Array類型,增加一個函數contains(),判斷數組中是否包含一個值

這就成了。

PS:給大家截一個圖大家看看,不懂的可以在下面讨論下

【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結
【javascript基礎】4、原型與原型鍊前言原型是什麼了解原型對象重寫原型對象原型動态性原生對象的原型小結

就先寫到這吧,大家有不懂的可以在下面讨論吧,我不懂的話我再去問大神,大夥要是覺得寫得亂的話推薦去看看《javascript進階程式設計》,那上面寫得比較好。小夥伴們都要回家了吧,提前祝大家春節快樂,馬上有錢,立馬變土豪。

繼續閱讀