原型與原型鏈詳解
前言
與大部分面嚮物件語言不同,ES6之前中並沒有引入類(class)的概念,JavaScript並非通過類而是直接通過建構函式來建立例項。在介紹原型和原型鏈之前,我們有必要先複習一下建構函式的知識。
一、建構函式
建構函式模式的目的就是為了建立一個自定義類,並且建立這個類的例項。建構函式模式中擁有了類和例項的概念,並且例項和例項之間是相互獨立的,即例項識別。
建構函式就是一個普通的函式,建立方式和普通函式沒有區別,不同的是建構函式習慣上首字母大寫。另外就是呼叫方式的不同,普通函式是直接呼叫,而建構函式需要使用new關鍵字來呼叫。
function Person(name, age, gender) {imagethis.name = name this.age = age this.gender = gender this.sayName = function () { alert(this.name); } } var per = new Person("孫悟空", 18, "男"); function Dog(name, age, gender) { this.name = name this.age = age this.gender = gender }var dog = new Dog("旺財", 4, "雄") console.log(per);//當我們直接在頁面中列印一個物件時,事件上是輸出的物件的toString()方法的返回值 console.log(dog);
每建立一個Person建構函式,在Person建構函式中,為每一個物件都添加了一個sayName方法,也就是說建構函式每執行一次就會建立一個新的sayName方法。這樣就導致了建構函式執行一次就會建立一個新的方法,執行10000次就會建立10000個新的方法,而10000個方法都是一摸一樣的,為什麼不把這個方法單獨放到一個地方,並讓所有的例項都可以訪問到呢?這就需要原型(prototype
二、原型
在JavaScript中,每當定義一個函式資料型別(普通函式、類)時候,都會天生自帶一個prototype
屬性,這個屬性指向函式的原型物件,並且這個屬性是一個物件資料型別的值。
讓我們用一張圖表示建構函式和例項原型之間的關係:
image原型物件就相當於一個公共的區域,所有同一個類的例項都可以訪問到這個原型物件,我們可以將物件中共有的內容,統一設定到原型物件中。
三、原型鏈
1.__proto__
和constructor
每一個物件資料型別(普通的物件、例項、prototype
......)也天生自帶一個屬性__proto__
,屬性值是當前例項所屬類的原型(prototype
)。原型物件中有一個屬性constructor
, 它指向函式物件。
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) // trueimage
2.何為原型鏈
在JavaScript中萬物都是物件,物件和物件之間也有關係,並不是孤立存在的。物件之間的繼承關係,在JavaScript中是通過prototype物件指向父類物件,直到指向Object物件為止,這樣就形成了一個原型指向的鏈條,專業術語稱之為原型鏈。
舉例說明:person → Person → Object ,普通人繼承人類,人類繼承物件類
當我們訪問物件的一個屬性或方法時,它會先在物件自身中尋找,如果有則直接使用,如果沒有則會去原型物件中尋找,如果找到則直接使用。如果沒有則去原型的原型中尋找,直到找到Object物件的原型,Object物件的原型沒有原型,如果在Object原型中依然沒有找到,則返回undefined。
我們可以使用物件的hasOwnProperty()
來檢查物件自身中是否含有該屬性;使用in
檢查物件中是否含有某個屬性時,如果物件中沒有但是原型中有,也會返回true
function Person() {} Person.prototype.a = 123; Person.prototype.sayHello = function () { alert("hello"); }; var person = new Person() console.log(person.a)//123 console.log(person.hasOwnProperty('a'));//false console.log('a'in person)//true
person例項中沒有a這個屬性,從 person 物件中找不到 a 屬性就會從 person 的原型也就是 person.__proto__
,也就是 Person.prototype中查詢,很幸運地得到a的值為123。那假如 person.__proto__
中也沒有該屬性,又該如何查詢?
當讀取例項的屬性時,如果找不到,就會查詢與物件關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層Object為止。Object是JS中所有物件資料型別的基類(最頂層的類)在Object.prototype上沒有__proto__
這個屬性。
console.log(Object.prototype.__proto__ === null) // true