繼承是面向對象的特性(封裝、抽象、繼承、多態)之一,JavaScript做爲面嚮對象語言天然擁有繼承的特性。若是想要真正理解JavaScript的繼承機制,那麼應該從JavaScript對象的原型提及。javascript
1 prototypejava
每個對象都有一個原型屬性,固然,不一樣的瀏覽器對這個屬性包裝不同。好比咱們使用 Firefox 或者 Google瀏覽器就能經過 __proto__ 獲取屬性指向的實例引用(原型對象)。IE瀏覽器不能經過以上方法獲取,並不能說明這個對象不存在!咱們知道JavaScript中函數也是一個對象,一個Function對象,既然是對象他也有原型對象的引用,他的屬性名稱就是prototype,換句話:咱們可以經過函數調用prototype的方式獲取原型引用。函數又能夠稱做爲類,那麼類的實例所擁有的原型引用與函數自己所擁有的原型引用是徹底相同的,這就保證了原型共享,爲繼承打好了基礎。瀏覽器
總結:函數
1)每一個函數都有一個prototype(原型)屬性,這個屬性是一個對象,它的用途是包含全部實例共享的屬性和方法。邏輯上能夠這麼理解:使用原型的好處可讓全部對象實例共享它所包含的屬性和方法,並且函數自己與該函數全部的實例共享一個原型。測試
2)函數調用原型採用 Class.prototype,實例調用原型屬性 obj.__proto__,判斷(Class.prototype ==obj.__proto__)this
3)原型是一個對象,一個實例化的對象,這個對象的實例化過程比較特殊,特殊之處在於他不是經過類自己去實例化的,可是他又是該類的實例。好比一個類 function Person(name,age){ this.name=name; ....}; var p = new Person('learn',23);若是p的原型是經過類自己去實例化的話,那麼原型必定會有一個name屬性,然而實際上原型是一個 Person{ };卻不是一個Person的實例,又不是經過Person的構造方法new出來的。能夠用 instanceof 測試這個原型不屬於Person實例。spa
function Person(name ,age){ this.name = name; this.age = age; this.info = function(){ return this.name + this.age; }; } var p = new Person('learn',23); console.info(Person.prototype); // Person{} console.info(p.__proto__); // Person{} console.info(Person.prototype == p.__proto__ );//true console.info(p.__proto__ instanceof Person);//false
2 constructorprototype
與原型同樣,每個函數都有一個constructor屬性,每個函數的原型也有一個constructor屬性,既然原型中有constructor屬性,天然每個對象實例就能共享這個constructor屬性。那麼這裏要注意了:函數其實有兩個constructor屬性,一個是自身的屬性,能夠經過 Class.constructor 獲取(js屬性調用就近原則),一個是原型中的constructor屬性,能夠經過 Class.prototype.constructor 或者 obj.__proto__.construcotr 獲取。這兩個是有本質差別的,前者是一個匿名函數的引用,後者是該類自己的引用。並且前者是類專有私有的屬性,是不被實例共享的。實際開發中,咱們不會去觸碰前者,咱們每每是在繼承的時候對後者進行改變。code
總結:對象
1)每一個函數都有一個類私有的constructor屬性和一個原型當中共享的constructor屬性
2)只有類自己才能調用這個私有屬性:Class.constructor ;原型constructor屬性調用:obj.consturctor 或 obj.__proto__.construcotr 或 Class.prototype.constructor
3)類私有的constructor屬性指向一個匿名函數的引用,注意是函數引用,不是函數的實例引用;原型的constructor屬性則是指向類自己的引用,這裏也是函數引用,不是實例引用。
4)原型的constructor屬性是一個類的重要標誌,他必定要指向類自己,由於面向對象規定構造函數指向自己。然而類的私有constructor屬性是由系統決定,咱們不最好不要觸碰。
console.info(Person.constructor);//Function() console.info(Person.prototype.constructor);// Person(name, age) console.info(p.constructor);// Person(name, age) console.info(p.__proto__.constructor);// Person(name, age)
3 extend
JavaScript是經過原型進行繼承的,原型的做用就是共享,因此經過原型能夠很好的達到繼承機制。採用原型繼承時要注意必不可少的兩點:子類的原型必須指向父類的實例,子類原型的constructor屬性必須指向子類自己。經過原型指向父類實例,從而全部子類實例共享父類的屬性與方法,達到繼承效果。經過子類原型的constructor屬性指向子類自己,達到面向對象中要求的構造函數指向之間。
總結:
1)子類的原型指向父類的實例,SubClass.prototype = new SuperClass( );
2)子類的原型構造函數指向子類自己,SubClass.prototype.constructor = SubClass ;
3 ) 因爲js使用原型繼承,致使構造函數也被繼承,然而面向對象來講這是錯誤的,因此纔有第二步從新指向構造函數。
4 ) 我的理解 Person 與 Person.prototype 的差別是:前者是指構造函數或類自己,後者實際上是一個全部實例都共享的實例對象。
5)若是單純只是調用某一個函數那麼可使用 call 函數進行處理,而無需繼承。 Class.method.call(Self , param) ;
function extends( SubClass , SuperClass){ /*第一步 : 構建橋樑類Bridge,他的做用就是徹底替代父類自己,包括構造方法*/ var Bridge = function( ){ } ; Bridge.prototype = new SuperClass( ); // Bridge.prototype.constructor = SuperClass ;這一步原型鏈默認完成 /*第二步 : 使用子類的原型鏈繼承橋樑父類*/ SubClass.prototype = new Bridge( ); SubClass.prototype.constructor = SubClass; /*第三步 : 擴展子類屬性,把父類的引用做爲子類的共享屬性,爲子類中所調用 */ SubClass.prototype.superClass = SuperClass.prototype; // 這裏必須是prototype,而不能是函數自己 /*第四步 : 爲保證程序正常運行機制,作個小判斷*/ if( SuperClass.prototype.constructor == Object.prototype.constructor ){ SuperClass.prototype.constructor = SuperClass; } } 6)javascript是單繼承的,但若是想在一個類中擁有多個類的方法,那麼就要使用聚合(摻元類,把其餘類的方法爲本身所用)。 具體以下: function mixin(ReceivingClass,GivingClass){ for(var method in GivingClass.prototype ){ if(ReceivingClass.prototype[method] == undefined){ /* 這裏特別注意使用prototype而不使用緣由就是 靜態屬性 與 原型屬性的差別 */ ReceivingClass.prototype[method]= GivingClass.prototype[method]; } } }