本篇文章爲做者對原型鏈的一些本身的理解和總結,若是有哪裏不對或者不夠詳細歡迎補充。javascript
在說到JavaScript的原型鏈時,會遇到如下幾個關鍵字:java
原型、prototype、__proto__、constructor、實例、原型鏈。git
首先咱們知道,在JavaScript中,大多數的量、方法、函數均可以看做對象。github
而原型在理解上來講就是當咱們建立上述的JavaScript對象(null除外)時,必定會有一個對象與這個建立的對象相關聯,這個對象就是咱們所說的原型,每個對象都會從原型中「繼承」屬性。函數
一. Prototypethis
在JavaScript中,每一個函數都有一個prototype屬性,這個屬性指向函數的原型對象。spa
例如:prototype
1 function Student(age){ 2 this.age=age; 3 } 4 Student.prototype.name = 'Alice' 5 const student1=new Student(); 6 const student2=new Student(); 7 console.log(student1.name); //Alice 8 console.log(student2.name); //Alice 9 Student.prototype.name = 'Pop' 10 console.log(student2.name); //Pop
函數的prototype指向了一個對象,而這個對象正是調用構造函數時建立的對象的原型,也就是student1和student2的原型。設計
上面的代碼中咱們也能夠看到,隨着原型的name的改變,這些實例的name也發生了改變。指針
而在這裏,咱們經過一個圖讓你們瞭解原型。
二. __proto__
這是每一個對象(除null外)都會有的屬性,叫作__proto__,這個屬性會指向該對象的原型。
1 function Student(age){ 2 this.age=age; 3 } 4 const student = new Student(); 5 console.log(student.__proto__===Student.prototype); // true
而這之間的關係能夠用下圖表示
三. Constructor
每一個原型都有一個constructor屬性,指向該關聯的構造函數。
1 function Student(age){ 2 this.age=age; 3 } 4 console.log(Student===Student.prototype.constructor) // true
而這之間的關係能夠用下圖表示
這裏咱們再看一個例子:
1 function Student(age){ 2 this.age=age; 3 } 4 console.log(Student===Student.prototype.constructor) // true 5 const student = new Student(); 6 console.log(student.constructor===Student); //true
爲何上面的第二個的返回會是true?
由於在獲得student.constructor時,student是沒有這個屬性的,所以它會從它的原型中,也就是student.prototype中尋找constructor,也就變成了第一個的判斷,所以纔會與構造函數相同,返回true。
四. 實例與原型的關係
當讀取實例的屬性時,若是找不到,就會查找與對象關聯的原型中的屬性,若是還查不到,就去找原型的原型,一直找到最頂層爲止,這是JavaScript中的重要的一部分。
1 function Student(age){ 2 this.age=age; 3 } 4 Student.prototype.name = 'Alice' 5 const student = new Student(); 6 student.name='pop' 7 console.log(student.name) // pop 查找到的是實例的屬性 8 delete student.name 9 console.log(student.name) // Alice 在實例中查找不到該屬性,向上查詢,在原型中查找到該屬性
在上面這個代碼中,我爲student實例添加了一個name屬性,爲Student原型添加了name屬性,當查找student的name屬性時,先在student本事查找這個屬性,查找到了便返回,值就是第一個輸出的值,而後由於我刪除了student的name屬性。當再次尋找name屬性時,student自己並無這個屬性,所以從他的原型中尋找這個屬性,因而在Student.prototype中找到了name屬性,返回。
那麼若是Student.prototype也沒有這個屬性,他會繼續找到哪裏呢?
答案是找到原型的原型。
咱們知道,JavaScript萬物皆爲對象,那麼原型也不例外,他也是一個對象,既然是對象,那麼就能夠用Object的方式去建立他。
1 const obj = new Object(); 2 obj.name = 'Alice' 3 console.log(obj.name) // Alice
這裏咱們能夠經過代碼來看任意構造函數的原型和對象原型之間的關係之間的關係
function Student(age) { this.age = age; } console.log(Student.prototype.__proto__===Object.prototype); // true
沒錯,原型就是這麼被建立出來的(或者說你能夠這麼理解),這樣咱們在更新咱們的原型的關係圖。
五. 原型鏈
簡單的回顧一下構造函數、原型和實例的關係:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。那麼假如咱們讓原型對象等於另外一個類型的實例,結果會怎樣?顯然,此時的原型對象將包含一個指向另外一個原型的指針,相應地,另外一個原型中也包含着一個指向另外一個構造函數的指針。假如另外一個原型又是另外一個類型的實例,那麼上述關係依然成立。如此層層遞進,就構成了實例與原型的鏈條。這就是所謂的原型鏈的基本概念。——摘自《javascript高級程序設計》
其實看起來就像是套娃同樣對不對,沒錯,就是上面的第四點的不斷重複。
那麼object.prototype的原型呢?
1 console.log(Object.prototype.__proto__) // null
null 表示「沒有對象」,即該處不該該有值。
因此到這裏時,就已經查找中止了。
那麼最後咱們的關係圖就變成了這樣
圖中的student(實例)->Student.prototype(原型)->Object.prototype->null 這條相互關聯的原型的鏈就是原型鏈。
文章中的代碼可查看個人GitHub:https://github.com/FuloliyaLansfroya/allProject