前言
與大部分面向?qū)ο笳Z言不同,ES6之前中并沒有引入類(class)的概念,JavaScript并非通過類而是直接通過構(gòu)造函數(shù)來創(chuàng)建實(shí)例。在介紹原型和原型鏈之前,我們有必要先復(fù)習(xí)一下構(gòu)造函數(shù)的知識(shí)。

一、構(gòu)造函數(shù)
構(gòu)造函數(shù)模式的目的就是為了創(chuàng)建一個(gè)自定義類,并且創(chuàng)建這個(gè)類的實(shí)例。構(gòu)造函數(shù)模式中擁有了類和實(shí)例的概念,并且實(shí)例和實(shí)例之間是相互獨(dú)立的,即實(shí)例識(shí)別。
構(gòu)造函數(shù)就是一個(gè)普通的函數(shù),創(chuàng)建方式和普通函數(shù)沒有區(qū)別,不同的是構(gòu)造函數(shù)習(xí)慣上首字母大寫。另外就是調(diào)用方式的不同,普通函數(shù)是直接調(diào)用,而構(gòu)造函數(shù)需要使用new關(guān)鍵字來調(diào)用。
functionPerson(name, age, gender){this.name = namethis.age = agethis.gender = genderthis.sayName =function(){? ? ? ? ? ? alert(this.name);? ? ? ? }? ? }varper =newPerson("孫悟空",18,"男");functionDog(name, age, gender){this.name = namethis.age = agethis.gender = gender? ? }vardog =newDog("旺財(cái)",4,"雄")console.log(per);//當(dāng)我們直接在頁面中打印一個(gè)對(duì)象時(shí),事件上是輸出的對(duì)象的toString()方法的返回值console.log(dog);
每創(chuàng)建一個(gè)Person構(gòu)造函數(shù),在Person構(gòu)造函數(shù)中,為每一個(gè)對(duì)象都添加了一個(gè)sayName方法,也就是說構(gòu)造函數(shù)每執(zhí)行一次就會(huì)創(chuàng)建一個(gè)新的sayName方法。這樣就導(dǎo)致了構(gòu)造函數(shù)執(zhí)行一次就會(huì)創(chuàng)建一個(gè)新的方法,執(zhí)行10000次就會(huì)創(chuàng)建10000個(gè)新的方法,而10000個(gè)方法都是一摸一樣的,為什么不把這個(gè)方法單獨(dú)放到一個(gè)地方,并讓所有的實(shí)例都可以訪問到呢?這就需要原型(prototype)
二、原型
在JavaScript中,每當(dāng)定義一個(gè)函數(shù)數(shù)據(jù)類型(普通函數(shù)、類)時(shí)候,都會(huì)天生自帶一個(gè)prototype屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象,并且這個(gè)屬性是一個(gè)對(duì)象數(shù)據(jù)類型的值。
讓我們用一張圖表示構(gòu)造函數(shù)和實(shí)例原型之間的關(guān)系:
原型對(duì)象就相當(dāng)于一個(gè)公共的區(qū)域,所有同一個(gè)類的實(shí)例都可以訪問到這個(gè)原型對(duì)象,我們可以將對(duì)象中共有的內(nèi)容,統(tǒng)一設(shè)置到原型對(duì)象中。
三、原型鏈
1.__proto__和constructor
每一個(gè)對(duì)象數(shù)據(jù)類型(普通的對(duì)象、實(shí)例、prototype......)也天生自帶一個(gè)屬性__proto__,屬性值是當(dāng)前實(shí)例所屬類的原型(prototype)。原型對(duì)象中有一個(gè)屬性constructor, 它指向函數(shù)對(duì)象。
functionPerson(){}varperson =newPerson()console.log(person.__proto__ === Person.prototype)//trueconsole.log(Person.prototype.constructor===Person)//true//順便學(xué)習(xí)一個(gè)ES5的方法,可以獲得對(duì)象的原型console.log(Object.getPrototypeOf(person) === Person.prototype)// true
2.何為原型鏈
在JavaScript中萬物都是對(duì)象,對(duì)象和對(duì)象之間也有關(guān)系,并不是孤立存在的。對(duì)象之間的繼承關(guān)系,在JavaScript中是通過prototype對(duì)象指向父類對(duì)象,直到指向Object對(duì)象為止,這樣就形成了一個(gè)原型指向的鏈條,專業(yè)術(shù)語稱之為原型鏈。
舉例說明:person → Person → Object? ,普通人繼承人類,人類繼承對(duì)象類
當(dāng)我們?cè)L問對(duì)象的一個(gè)屬性或方法時(shí),它會(huì)先在對(duì)象自身中尋找,如果有則直接使用,如果沒有則會(huì)去原型對(duì)象中尋找,如果找到則直接使用。如果沒有則去原型的原型中尋找,直到找到Object對(duì)象的原型,Object對(duì)象的原型沒有原型,如果在Object原型中依然沒有找到,則返回undefined。
我們可以使用對(duì)象的hasOwnProperty()來檢查對(duì)象自身中是否含有該屬性;使用in檢查對(duì)象中是否含有某個(gè)屬性時(shí),如果對(duì)象中沒有但是原型中有,也會(huì)返回true
functionPerson(){}? ? Person.prototype.a =123;? ? Person.prototype.sayHello =function(){? ? ? alert("hello");? ? };varperson =newPerson()console.log(person.a)//123console.log(person.hasOwnProperty('a'));//falseconsole.log('a'inperson)//true
person實(shí)例中沒有a這個(gè)屬性,從 person 對(duì)象中找不到 a 屬性就會(huì)從 person 的原型也就是person.__proto__,也就是 Person.prototype中查找,很幸運(yùn)地得到a的值為123。那假如person.__proto__中也沒有該屬性,又該如何查找?
當(dāng)讀取實(shí)例的屬性時(shí),如果找不到,就會(huì)查找與對(duì)象關(guān)聯(lián)的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層Object為止。Object是JS中所有對(duì)象數(shù)據(jù)類型的基類(最頂層的類)在Object.prototype上沒有__proto__這個(gè)屬性。
console.log(Object.prototype.__proto__ ===null)// true