前言
與大部分面向?qū)ο笳Z言不同隔心,ES6之前中并沒有引入類(class)的概念,JavaScript并非通過類而是直接通過構(gòu)造函數(shù)來創(chuàng)建實(shí)例透揣。在介紹原型和原型鏈之前济炎,我們有必要先復(fù)習(xí)一下構(gòu)造函數(shù)的知識。
一辐真、構(gòu)造函數(shù)
構(gòu)造函數(shù)模式的目的就是為了創(chuàng)建一個自定義類须尚,并且創(chuàng)建這個類的實(shí)例崖堤。構(gòu)造函數(shù)模式中擁有了類和實(shí)例的概念,并且實(shí)例和實(shí)例之間是相互獨(dú)立的耐床,即實(shí)例識別密幔。
構(gòu)造函數(shù)就是一個普通的函數(shù),創(chuàng)建方式和普通函數(shù)沒有區(qū)別撩轰,不同的是構(gòu)造函數(shù)習(xí)慣上首字母大寫胯甩。另外就是調(diào)用方式的不同,普通函數(shù)是直接調(diào)用堪嫂,而構(gòu)造函數(shù)需要使用new關(guān)鍵字來調(diào)用偎箫。
function Person(name, age, gender) {
this.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);//當(dāng)我們直接在頁面中打印一個對象時,事件上是輸出的對象的toString()方法的返回值
console.log(dog);
每創(chuàng)建一個Person構(gòu)造函數(shù)皆串,在Person構(gòu)造函數(shù)中淹办,為每一個對象都添加了一個sayName方法,也就是說構(gòu)造函數(shù)每執(zhí)行一次就會創(chuàng)建一個新的sayName方法恶复。這樣就導(dǎo)致了構(gòu)造函數(shù)執(zhí)行一次就會創(chuàng)建一個新的方法怜森,執(zhí)行10000次就會創(chuàng)建10000個新的方法,而10000個方法都是一摸一樣的谤牡,為什么不把這個方法單獨(dú)放到一個地方副硅,并讓所有的實(shí)例都可以訪問到呢?這就需要原型(prototype
)
二、原型
在JavaScript中翅萤,每當(dāng)定義一個函數(shù)數(shù)據(jù)類型(普通函數(shù)恐疲、類)時候,都會天生自帶一個prototype
屬性断序,這個屬性指向函數(shù)的原型對象流纹,并且這個屬性是一個對象數(shù)據(jù)類型的值。
讓我們用一張圖表示構(gòu)造函數(shù)和實(shí)例原型之間的關(guān)系:
原型對象就相當(dāng)于一個公共的區(qū)域违诗,所有同一個類的實(shí)例都可以訪問到這個原型對象漱凝,我們可以將對象中共有的內(nèi)容,統(tǒng)一設(shè)置到原型對象中诸迟。
三茸炒、原型鏈
1.__proto__
和constructor
每一個對象數(shù)據(jù)類型(普通的對象、實(shí)例阵苇、prototype
......)也天生自帶一個屬性__proto__
壁公,屬性值是當(dāng)前實(shí)例所屬類的原型(prototype
)。原型對象中有一個屬性constructor
, 它指向函數(shù)對象绅项。
function Person() {}
var person = new Person()
console.log(person.__proto__ === Person.prototype)//true
console.log(Person.prototype.constructor===Person)//true
//順便學(xué)習(xí)一個ES5的方法,可以獲得對象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
2.何為原型鏈
在JavaScript中萬物都是對象紊册,對象和對象之間也有關(guān)系,并不是孤立存在的快耿。對象之間的繼承關(guān)系囊陡,在JavaScript中是通過prototype對象指向父類對象芳绩,直到指向Object對象為止,這樣就形成了一個原型指向的鏈條撞反,專業(yè)術(shù)語稱之為原型鏈妥色。
舉例說明:person → Person → Object ,普通人繼承人類遏片,人類繼承對象類
當(dāng)我們訪問對象的一個屬性或方法時嘹害,它會先在對象自身中尋找,如果有則直接使用吮便,如果沒有則會去原型對象中尋找笔呀,如果找到則直接使用。如果沒有則去原型的原型中尋找,直到找到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實(shí)例中沒有a這個屬性,從 person 對象中找不到 a 屬性就會從 person 的原型也就是 person.__proto__
粗卜,也就是 Person.prototype中查找屋确,很幸運(yùn)地得到a的值為123。那假如 person.__proto__
中也沒有該屬性续扔,又該如何查找攻臀?
當(dāng)讀取實(shí)例的屬性時,如果找不到纱昧,就會查找與對象關(guān)聯(lián)的原型中的屬性刨啸,如果還查不到,就去找原型的原型识脆,一直找到最頂層Object為止设联。Object是JS中所有對象數(shù)據(jù)類型的基類(最頂層的類)在Object.prototype上沒有__proto__
這個屬性。
console.log(Object.prototype.__proto__ === null) // true
如果覺得文章對你有些許幫助灼捂,歡迎在我的GitHub博客點(diǎn)贊和關(guān)注离例,感激不盡!