原型到原型鏈

2022-09-16 03:27:10 字數 3964 閱讀 1898

我們先使用建構函式建立乙個物件:

function person() 

var person = new person();

person.name = 'kevin';

console.log(person.name) // kevin

在這個例子中,person 就是乙個建構函式,我們使用 new 建立了乙個例項物件 person。

很簡單吧,接下來進入正題:

每個函式都有乙個 prototype 屬性,就是我們經常在各種例子中看到的那個 prototype ,比如:

function person() 

// 雖然寫在注釋裡,但是你要注意:

// prototype是函式才會有的屬性

person.prototype.name = 'kevin';

var person1 = new person();

var person2 = new person();

console.log(person1.name) // kevin

console.log(person2.name) // kevin

那這個函式的 prototype 屬性到底指向的是什麼呢?是這個函式的原型嗎?

其實,函式的 prototype 屬性指向了乙個物件,這個物件正是呼叫該建構函式而建立的例項的原型,也就是這個例子中的 person1 和 person2 的原型。

那什麼是原型呢?你可以這樣理解:每乙個j**ascript物件(null除外)在建立的時候就會與之關聯另乙個物件,這個物件就是我們所說的原型,每乙個物件都會從原型"繼承"屬性。

讓我們用一張圖表示建構函式和例項原型之間的關係:

在這張圖中我們用 object.prototype 表示例項原型。

那麼我們該怎麼表示例項與例項原型,也就是 person 和 person.prototype 之間的關係呢,這時候我們就要講到第二個屬性:

這是每乙個j**ascript物件(除了 null )都具有的乙個屬性,叫__proto__,這個屬性會指向該物件的原型。

為了證明這一點,我們可以在火狐或者谷歌中輸入:

function person() 

var person = new person();

console.log(person.__proto__ === person.prototype); // true

於是我們更新下關係圖:

既然例項物件和建構函式都可以指向原型,那麼原型是否有屬性指向建構函式或者例項呢?

指向例項倒是沒有,因為乙個建構函式可以生成多個例項,但是原型指向建構函式倒是有的,這就要講到第三個屬性:constructor,每個原型都有乙個 constructor 屬性指向關聯的建構函式。

為了驗證這一點,我們可以嘗試:

function person() 

console.log(person === person.prototype.constructor); // true

所以再更新下關係圖:

綜上我們已經得出:

function person() 

var person = new person();

console.log(person.__proto__ == person.prototype) // true

console.log(person.prototype.constructor == person) // true

// 順便學習乙個es5的方法,可以獲得物件的原型

console.log(object.getprototypeof(person) === person.prototype) // true

了解了建構函式、例項原型、和例項之間的關係,接下來我們講講例項和原型的關係:

當讀取例項的屬性時,如果找不到,就會查詢與物件關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層為止。

舉個例子:

function person() 

person.prototype.name = 'kevin';

var person = new person();

person.name = 'daisy';

console.log(person.name) // daisy

delete person.name;

console.log(person.name) // kevin

在這個例子中,我們給例項物件 person 新增了 name 屬性,當我們列印 person.name 的時候,結果自然為 daisy。

但是當我們刪除了 person 的 name 屬性時,讀取 person.name,從 person 物件中找不到 name 屬性就會從 person 的原型也就是 person.__proto__ ,也就是 person.prototype中查詢,幸運的是我們找到了 name 屬性,結果為 kevin。

但是萬一還沒有找到呢?原型的原型又是什麼呢?

在前面,我們已經講了原型也是乙個物件,既然是物件,我們就可以用最原始的方式建立它,那就是:

var obj = new object();

obj.name = 'kevin'

console.log(obj.name) // kevin

所以原型物件是通過 object 建構函式生成的,結合之前所講,例項的 __proto__ 指向建構函式的 prototype ,所以我們再更新下關係圖:

那 object.prototype 的原型呢?

null,我們可以列印:

console.log(object.prototype.__proto__ === null) // true

然而 null 究竟代表了什麼呢?

引用阮一峰老師的 《undefined與null的區別》 就是:

null 表示「沒有物件」,即該處不應該有值。

所以 object.prototype.__proto__ 的值為 null 跟 object.prototype 沒有原型,其實表達了乙個意思。

所以查詢屬性的時候查到 object.prototype 就可以停止查詢了。

最後一張關係圖也可以更新為:

順便還要說一下,圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。

首先是 constructor 屬性,我們看個例子:

function person() 

var person = new person();

console.log(person.constructor === person); // true

當獲取 person.constructor 時,其實 person 中並沒有 constructor 屬性,當不能讀取到constructor 屬性時,會從 person 的原型也就是 person.prototype 中讀取,正好原型中有該屬性,所以:

person.constructor === person.prototype.constructor

其次是 __proto__ ,絕大部分瀏覽器都支援這個非標準的方法訪問原型,然而它並不存在於 person.prototype 中,實際上,它是來自於 object.prototype ,與其說是乙個屬性,不如說是乙個 getter/setter,當使用 obj.__proto__ 時,可以理解成返回了 object.getprototypeof(obj)。

最後是關於繼承,前面我們講到「每乙個物件都會從原型『繼承』屬性」,實際上,繼承是乙個十分具有迷惑性的說法,引用《你不知道的j**ascript》中的話,就是:

繼承意味著複製操作,然而 j**ascript 預設並不會複製物件的屬性,相反,j**ascript 只是在兩個物件之間建立乙個關聯,這樣,乙個物件就可以通過委託訪問另乙個物件的屬性和函式,所以與其叫繼承,委託的說法反而更準確些。

原型 原型鏈

var animal function var dog function animal.price 2000 dog.prototype animal var tidy new dog console.log dog.price 為什麼輸出 undefined console.log tidy.pr...

原型,原型鏈

原型object.hasownproperty proname object 乙個物件的例項 propname 乙個屬性名稱的字串值 返回乙個布林值原型鏈中的this 原型物件 原型物件的作用 一般情況下,會把方法宣告在原型物件裡。目的是實現繼承。當物件訪問自身屬性或方法時,先從自身找有沒有,如果有...

原型 原型鏈

在最近的原型和原型鏈的學習中,感覺壓力有點大,學習難度也比較大,但又很基礎很重要,我們在學習中得要下很大的功夫才行。無論什麼時候,只要建立了乙個新的函式,就會根據一組特定的規則為這個函式建立乙個prototype的屬性,prototype這個屬性指向函式的原型物件,然後所有的的原型物件都會有乙個co...