很長一段時間對JS的原型與原型鏈理解的很模糊,而想學好JS,原型與原型鏈倒是繞不開的話題,因此只好進行對其進行一個梳理。javascript
理解原型和原型鏈,需理解三個重要的屬性: prototype、__proto__、constructorjava
prototype函數
JavaScript函數中,每個函數都有一個prototype屬性,叫原型對象。spa
代碼示例:prototype
function Person(){
}
Person.prototype.name = 'zhangsan'
let person = new Person()
console.log(person.name) // zhangsan
上面的代碼中,建立了一個構造函數Person,並在它的原型上添加一個屬性name爲zhangsan, 而後再建立了實例對象person,那麼這個實例對象上也有name屬性,打印輸出「zhangsan」3d
能夠看出:code
Person這個函數的prototype屬性指向了一個對象,即:Person.prototype 也是一個對象,這個對象正是調用該構造函數而建立的實例的原型,即person的原型。對象
方便理解,咱們進行拆解:blog
1.建立了構造函數Person 2.使用new關鍵字進行調用 3.調用獲得了實例person 4.實例和原型的關係:person的原型就是Person.prototype
那什麼是原型呢?能夠這樣理解:每個JavaScript對象(null除外)在建立的時候就會與之關聯另一個對象,這個對象就是咱們所說的原型,而每個對象都會從原型"繼承"屬性。繼承
咱們用一張圖表示構造函數和原型之間的關係:
構造函數和實例原型之間的關係咱們已經梳理清楚了,那怎麼表示實例與原型,也就是person和Person.prototype之間的關係呢?
__proto__
其實每個JavaScript對象(除了null)都有一個屬性,叫__proto__,這個屬性會指向該對象的原型。
代碼示例:
function Person(){ } let person = new Person() console.log(person.__proto__ === Person.prototype) // true
構造函數Person的實例對象person,有個屬性叫__proto__,這個屬性指向原型,即Person.prototype。
補全上面的關係圖:
Q:原型是否有屬性指向構造函數或者實例對象呢?
constructor
代碼示例:
function Person() { } let person = new Person() console.log(Person === Person.prototype.constructor); // true
繼續完善關係圖:
原型的constructor指向構造函數,但沒有屬性指向實例,由於可能有多個實例。
其實 person 中並無constructor 屬性,當不能讀取到constructor屬性時,會從 person 的原型也就是 Person.prototype中讀取
console.log(person.constructor === Person) // true
console.log(person.constructor === Person.prototype.constructor) // true
實例與原型
當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性,若是還查不到,就去找原型的原型,一直找到最頂層爲止。
代碼示例:
function Person() { } Person.prototype.name = 'zhangsan'; let person = new Person(); person.name = 'lisi'; console.log(person.name) // lisi delete person.name; console.log(person.name) // zhangsan
new建立了一個實例對象person,有個屬性name值爲「lisi」,當咱們把刪除了這個name屬性後,依然可以打印出「zhangsan」,實際狀況是從 person 實例對象中找不到 name 屬性,就會從 person 的原型也就是 person.__proto__
,也就是 Person.prototype
中查找,幸運的是咱們找到了 name 屬性,結果爲 zhangsan
原型的原型
代碼示例:
Object.prototype.name = 'wangwu' function Person(){ } let person = new Person() console.log(person.name) // wangwu
Person 的實例對象person,自己沒有name屬性值,全部會去原型上找,Person.prototype上也沒有,就去原型的原型上找,即:Object.prototype,有一個name爲「wangwu」的屬性,全部輸出「wangwu」
其實原型對象就是經過Object構造函數生成的,結合以前咱們所說的,實例的__proto__指向構造函數的 prototype 因此咱們再豐富一下咱們的關係圖:
原型鏈
那Object.prototype 的原型呢?Object是根節點的對象,再往上查找就是null,咱們能夠打印:
console.log(Object.prototype.__proto__ === null) // true
咱們將null也加入關係圖,就比較完整了:
上面的person實例對象到null的這條線即爲原型鏈。
咱們還能夠把大Function加上
console.log(Person.constructor === Function) // true
console.log(obj.__proto__ === Object.prototype) // true
console.log(obj.__proto__.constructor === Object) /// true
console.log(obj.__proto__.constructor.__proto__ === Function.prototype) // true
console.log(obj.__proto__.constructor.__proto__.constructor === Function) // true
console.log(Person.__proto__ === Function.prototype) // true console.log(Object.__proto__ === Function.prototype) // true
從網上找了張圖,如今再去理解原型與原型鏈,就好理解了吧