建構函式
JavaScript 通過建構函式生成新物件,因此建構函式可以視為物件的模板。例項物件的屬性和方法,可以定義在建構函式內部。
function Person (name, age) {
this.name = name
this.age = age
}
var person1 = new Person('kari', '18')
person1.name // 'kari'
person1.age // '18'
複製程式碼
上面程式碼中,Person
函式是一個建構函式,函式內部定義了 name
屬性和 age
屬性,所有例項物件(上例是 person1
)都會生成這兩個屬性,即這兩個屬性會定義在例項物件上面。
通過建構函式為例項物件定義屬性,雖然很方便,但是有一個缺點。同一個建構函式的多個例項之間,無法共享屬性,從而造成對系統資源的浪費。
function Person(name, age) {
this.name = name
this.age = age
this.say = function () {
console.log('hello')
};
}
var person1 = new Person('老大', '20')
var person2 = new Person('老二', '18')
person1.say === person2.say
// false
複製程式碼
上面程式碼中,person1
和 person2
是同一個建構函式的兩個例項,它們都具有 say
方法。由於 say
方法是生成在每個例項物件上面,所以兩個例項就生成了兩次。也就是說,每新建一個例項,就會新建一個 say
方法。這既沒有必要,又浪費系統資源,因為所有 say
方法都是同樣的行為,完全應該共享。
這個問題的解決方法,就是 JavaScript 的原型物件(prototype
)。
JS原型
JavaScript 繼承機制的設計思想就是,原型物件的所有屬性和方法,都能被例項物件共享。也就是說,如果屬性和方法定義在原型上,那麼所有例項物件就能共享,不僅節省了記憶體,還體現了例項物件之間的聯絡。
function Person() {
}
var person1 = new Person()
var person2 = new Person()
person1.name = 'kari'
person1.name // kari
person2.name // undefined
複製程式碼
function Person() {
}
var person1 = new Person()
var person2 = new Person()
person1.__proto__.name = 'kari'
person1.name // kari
person2.name // kari
複製程式碼
__proto__ 和 prototype
__proto__
是每個物件都有的一個屬性,而 prototype
是函式才會有的屬性。
__proto__
指向的是當前物件的原型物件,而 prototype
指向的是以當前函式作為建構函式構造出來的物件的原型物件。
通過下面這幅圖會更好的理解上面的兩句話:
當構造了Person
函式後,函式便會有一個屬性(prototype
),指向原型物件。
當 Person
函式被例項成 kari
後,kari
同樣也會有一個屬性(__proto__
)指向原型物件。
JS原型鏈
每個例項物件( object
)都有一個私有屬性(稱之為 __proto__
)指向它的原型物件(prototype
)。該原型物件也有一個自己的原型物件( __proto__
) ,層層向上直到一個物件的原型物件為 null
。
根據定義,null
沒有原型,並作為這個原型鏈中的最後一個環節。