在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
下面我將以圖形的方式更加清晰的展現原型對象的實質:
前面我們已經明白了原型的概念,那么原型鏈又是什么呢?
我們知道,原型事實上是一個對象,而對象又可以由構造函數創建,那么如果有一個構造函數 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中非常重要的一個知識點,同樣也是難點之一,我也本是初學者,所以以上只能算是自己在學習當中的一些總結。文中的例子都是自己通過實際測試出來的,所以可以保證其正確性。若是文中有些地方總結的不夠到位或者不對的地方,還請多多指正。