在JavaScript這門語言中,原型是一個非常非常重要的概念,因為這門語言很特殊,不像其他面向對象語言一樣是基于類來實作繼承的,而是基于對象來實作繼承的,而其中基于原型來實作繼承是JavaScript中常用的一種方式。接下來,我們就來一起探讨一下原型的概念。
JavaScript中,每個對象都有constructor屬性和__proto__屬性,其中__proto__屬性指向建立該對象的構造函數的原型對象(__proto__屬性在IE中不可通路)。
每個對象事實上都是由一個構造函數建立的一個執行個體,而這個構造函數在建立時會同時存在一個prototype屬性,而這個prototype屬性指向一個對象,這個對象就是原型對象,這個對象中包含由這個構造函數所建立的所有執行個體共享的屬性和方法。同樣,這個原型對象跟普通對象一樣,也存在constructor屬性和__proto__屬性。其中constructor屬性指向該構造函數,__proto__屬性指向建立該原型對象的構造函數的原型對象(感覺有點繞,不要緊,你隻要知道__proto__屬性都是指向原型對象就好了,再不懂繼續往下看),若該原型對象并不是某個構造函數的一個執行個體,那麼該__proto__屬性将預設指向Object()的原型對象,而Object()的原型對象的__proto__又是指向哪裡呢?答案是null,不指向任何地方,因為Object是最頂級的對象。
舉個例子:
var zhangsan = { //以字面量表達式的方式建立一個對象
name: "張三",
sex: "男",
age: 18
};
console.log(zhangsan.constructor); //function Object() { [native code] }
console.log(zhangsan.prototype); //undefined 對象執行個體不存在prototype屬性,隻有構造函數才有
console.log(Object.prototype); //Object {}
console.log(zhangsan instanceof Object); //true
console.log(zhangsan.__proto__); //Object {}
console.log(zhangsan.__proto__.__proto__); //null
console.log(zhangsan.__proto__ == Object.prototype); //true
下面我将以圖形的方式更加清晰的展現原型對象的實質:
圖中的 [[prototype]] 就是__proto__,隻存在于對象執行個體中
前面我們已經明白了原型的概念,那麼原型鍊又是什麼呢?
我們知道,原型事實上是一個對象,而對象又可以由構造函數建立,那麼如果有一個構造函數** b 的原型是由另一個構造函數 a 建立的一個執行個體的話,接下來由這個構造函數 b 建立的對象執行個體将會繼承構造函數 a 的所有屬性及方法,而這些屬性和方法都将存在于執行個體所指向的原型當中。那麼同樣的,如果又有一個新的構造函數 c 的原型是上面這個構造函數 b 建立的執行個體的話,就又将以上兩個構造函數的屬性和方法都繼承了下來,然後以此類推……這樣的話就形成了一條以原型對象構成的“鍊條”,而這個“鍊條”就稱作原型鍊**。
講這麼多還不如來個例子生動些:
function Animal(name,sex,age){ //建立Animal構造函數,并傳三個參數
this.name = name;
this.sex = sex;
this.age = age;
}
Animal.prototype = { //重寫Animal的原型對象
constructor: Animal, //注意重寫原型對象後需要重新定義constructor指向
greet: function(){
console.log("Hello!My name is "+ this.name +".");
}
};
function Person(name,sex,age,chr){
Animal.call(this,name,sex,age); //調用Animal構造函數,并将其this作用域改變(借用構造函數)
this.character = chr;
}
Person.prototype = new Animal(); //将Animal建立的對象的引用指派給Person的prototype屬性,進而實作繼承
Person.prototype.constructor = Person; //由于Person的原型對象被重寫,是以要将其constructor重新指向Person
var zhangsan = new Person("張三","男",18,"shy");
console.log(zhangsan.character);
zhangsan.greet();
以上代碼實際上用的不是原型式繼承,而是組合式繼承方式(原型鍊和借用構造函數技術組合,具體詳見《Javascript進階程式設計》),因為在Person構造函數用調用了Animal構造函數,将Animal構造函數中的屬性繼承了下來,而原型對象是通過原型鍊的方式繼承下來的。
結束語:原型和原型鍊是JavaScript中非常重要的一個知識點,同樣也是難點之一,我也本是初學者,是以以上隻能算是自己在學習當中的一些總結。文中的例子都是自己通過實際測試出來的,是以可以保證其正确性。若是文中有些地方總結的不夠到位或者不對的地方,還請多多指正。
本文為原創文章,轉載請注明出處,謝謝!