構造函數
function Animal() {
}
var cat = new Animal();
cat.name = 'Tom';
console.log(cat.name) // Tom
prototype
每個函數都有一個 prototype 屬性
函數的 prototype 屬性指向了一個對象,這個對象正是調用該構造函數而創建的實例的原型,也就是這個例子中的 cat
和 dog
的原型。
function Animal() {
}
// 雖然寫在註釋裏,但是需要注意:prototype是函數纔會有的屬性
Animal.prototype.name = 'Tom';
var cat = new Animal();
var dog = new Animal();
console.log(cat.name) // Tom
console.log(dog.name) // Tom
原型
每一個JavaScript對象(null除外)在創建的時候就會與之關聯另一個對象,這個對象就是我們所說的原型,每一個對象
都會從原型"繼承"屬性。
proto
如何表示實例與實例原型之間的關係呢,這裏需要提到 proto
每一個JavaScript對象(除了 null )都具有的一個屬性,叫__proto__,這個屬性會指向該對象的原型
- 數組、正則、Math、Date、類數組等
- 實例是對象數據類型的值
- 函數的原型prototype屬性的值也是對象類型
- 函數也是對象數據類型
每一個對象數據類型的值,都自帶__proto__屬性,這個屬性用來指向所屬類的原型prototype
function Animal() {
}
var cat = new Animal();
console.log(cat.__proto__ === Animal.prototype); // true
實例對象和構造函數都可以指向原型
constructor
每個原型都有一個 constructor
屬性指向關聯的構造函數
function Animal() {
}
console.log(Animal === Animal.prototype.constructor); // true
則可以得出
function Animal() {
}
var cat = new Animal();
console.log(cat.__proto__ == Animal.prototype) // true
console.log(Animal.prototype.constructor == Animal) // true
// 順便學習一個ES5的方法,可以獲得對象的原型
console.log(Object.getPrototypeOf(cat) === Animal.prototype) // true
實例和原型
當讀取實例的屬性時,如果找不到,就會查找與對象關聯的原型中的屬性,如果還查不到,就去找原型的原型,一直找到最頂層爲止。
function Animal() {
}
Animal.prototype.name = 'Tom';
var cat = new Animal();
cat.name = 'Jerry';
console.log(cat.name) // Jerry
delete cat.name;
console.log(cat.name) // Tom
我們給實例添加了name屬性,放我們打印cat.name
時結果自然是Jerry。當我們刪除了cat的name屬性時,讀取cat.name時,從對象中無法找到name屬性,就會從cat的原型,也就是cat.__proto__
,同時也是Animal.prototype
中查找,找到結果爲Tom。
反問:假若沒有找到該怎麼辦呢?原型的原型又是誰呢?
原型的原型
原型對象其實就是通過Object
構造函數生成的,實例的__proto__
指向構造函數的prototype
原型鏈
提問:Object.prototype
的原型呢?
是null,可以打印一下看看:
console.log(Object.prototype.__proto__ === null) // true
null 表示“沒有對象”,即該處不應該有值。
Object.prototype.__proto__
的值爲null跟Object.prototype
沒有原型,其實是一個意思。
查找屬性的時候查到Object.prototype就停止查找了。
圖中黃色的線就是相互關聯的原型組成的鏈狀結構
——原型鏈。
由於函數也是對象,會在內存中單獨開闢一個空間,因此,當實例對象多的時候,就會出現內存空間被很多重複的函數佔用。
如:有100個 Person 的實例對象,那麼在內存空間裏就會有100個eat方法佔用的空間,這些方法分別指向不同的實例對象。但是他們的執行結果卻都是一樣的。這就造成了內存空間的浪費!
爲解決這個問題,我們需要一個共享的空間,在裏面存放一些公共的方法,屬性,這就是原型。
JavaScript原型就是爲了實現對象之間的聯繫,解決構造函數無法數據共享而引入的一個屬性。原型鏈就是一個實現對象間聯繫即繼承的主要方法。