說到這個繼承,了解object-oriented的朋友都知道,大多數oo語言都有兩種,一種是介面繼承(只繼承方法簽章);一種是實作繼承(繼承實際的方法)本文主要和大家介紹js物件導向之繼承的相關知識,以及分享了讀者弄清楚這個知識點的學習心得,對此有需要的朋友參考下吧。希望能幫助大家。
奈何js中沒有簽名,因而只有實作繼承,而且靠的是原型鏈實現的。下面正式的說一說js中繼承那點事兒
原型鏈:實作繼承的主要方法,利用原型讓一個引用型別繼承另一個引用型別的屬性和方法。
回顧:建構函數,原型,實例三者的關係
每一個建構子都有一個原型物件(Person.prototype);原型物件都包含指向建構子的指標(constructor );每個實例都包含指向原型物件的指標(看不見的_proto_指標)
原型鍊是怎麼來的呢?
某個建構子的原型物件是另一個建構子的實例;這個建構子的原型物件就會有個(看不見的_proto_指標)指向另一個建構子的原型物件;
那麼另一個原型物件又是其他的建構子實例又會怎麼樣,就這樣層層遞進,形成原型鏈;來具體看一下吧
//第一个构造函数;有一个属性和一个原型方法 function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function(){ return this.property } //第二个构造函数;目前有一个属性 function SubType(){ this.subproperty=false } //继承了SuperType;SubType原型成了SuperType的实例;实际就是重写SubType的原型对象;给SuperType原型对象继承了 SubType.prototype=new SuperType() //现在这个构造函数有两个属性(一个本身的subproperty,一个继承的存在原型对象的property);两个方法(一个原型对象的getSubValue,一个原型对象的原型对象的getSuperValue) SubType.prototype.getSubValue=function(){ return this.subproperty } var instance=new SubType() //创建第二个构造函数的实例 console.log(instance.getSuperValue()) //true 先查找instance这个实例有没有此方法;显然没有,再查找SubType原型对象有没有此方法;也没有,再查找SubType原型对象的原型对象;显然是存在的
注意:instance的constructor現在指向的是SuperType這個建構子;因為原來的SubType.prototype被重寫了,其內部的constructor也就隨著SubType.prototype的原型物件的constructor指向建構函式SuperType;至於原型搜尋機制是怎麼樣運作的,請仔細看上面的程式碼,相信你是可以的
1.1完整的原型
在原型那節已經提了些,還是再說一下。完整的原型包括Object。
所有函數的預設原型都是Object的實例;每個預設原型都有個_proto_指標指向Object.prototype;因此自訂類型都繼承如toString,valueOf的方法
而Object.prototype的_proto_指標指向null來結束原型鏈。以Person建構函式為例,看看完整的原型鏈圖
1.2原型與實例的關係判斷
第一種使用instanceof運算子:測試實例和原型鏈中出現的建構函數,結果為true
第二種使用isPrototypeOf()方法: 只要是原型鏈中出現過的原型,都可以說是該原型鏈所衍生的實例的原型
console.log(instance instanceof Object) //都为true console.log(instance instanceof SuperType) console.log(instance instanceof SubType) console.log(Object.prototype.isPrototypeOf(instance)) //都为true console.log(SuperType.prototype.isPrototypeOf(instance)) console.log(SubType.prototype.isPrototypeOf(instance))
1.3謹慎定義方法
注意:給原型物件添加方法,一定放在替換原型的後面,因為放在替換原型之前是找不到了,原型會被重寫的;
注意:在透過原型鏈繼承時,不能使用物件字面量來創建原型方法,因為也會重寫原型鏈;
function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function(){ return this.property } function SubType(){ this.subproperty=false } //继承SuperType SubType.prototype=new SuperType() //使用字面量添加新方法,导致上一行无效 因为现在的原型替换了Object实例而非SuperType的实例,关系中断 SubType.prototype={ getSubValue:function(){ return this.subproperty; }, somOtherMethod:function(){ return false } }; var instance=new SubType() console.log(instance.getSuperValue()) //error
1.4原型鏈的問題
1、包含引用型別值的原型:當實例是另一個函數的原型時,引用型別值就會變成原型上的屬性,就會被另一個函數的實例所共用。
function SuperType(){ this.colors=["yellow","red","olive"] } function SubType(){ } SubType.prototype=new SuperType() //color实际上就是原型上的了 var instance1=new SubType() instance1.colors.push("purple") var instance2=new SubType() console.log(instance1.colors==instance2.colors) //true
2、建立子類型實例時,不能傳遞參數給超類型的建構子參數(沒有辦法在不影響所有物件實例的情況下,給超類型的建構子傳遞參數)
為了解決原型中包含引用型別值所帶來的問題,利用建構子來解決
在子型別構造函數的內部呼叫超類型建構函數(函數是特定環境中執行程式碼的對象,可以透過apply或call呼叫)
function SuperType(){ this.color=["yellow","red","olive"] } function SubType(){ //继承了SuperType SuperType.call(this) } var instance1=new SubType() instance1.color.push("purple") var instance2=new SubType() console.log(instance1.color) //["yellow","red","olive","purple"] console.log(instance2.color) //["yellow","red","olive"] //传递参数 function SuperType(name){ this.name=name } function SubType(){ SuperType.call(this,"double") this.age=12 } var instance1=new SubType() console.log(instance1.name) //double console.log(instance1.age) //12
問題:僅僅借鏡建構函數,那麼避免不了建構函式的問題,方法都在建構函式定義了,函式無法重複使用
function SuperType(name){ this.name=name; this.color=["yellow","red","olive"]; } SuperType.prototype.sayName=function(){ console.log(this.name); } function SubType(name,age){ //继承属性,创建属性副本 SuperType.call(this,name); this.age=age; } //继承属性和方法,只是原型中属性被后来的函数调用生成的属性副本遮盖 SubType.prototype=new SuperType(); alert(SubType.prototype.constructor) //指向的是SuperType SubType.prototype.constructor=SubType; //将constructor回归到SubType构造函数身上 SubType.prototype.sayAge=function(){ console.log(this.age) } var instance1=new SubType("double",23) instance1.color.push("pink") console.log(instance1.color) //["yellow","red","olive","pink"] instance1.sayName() //double instance1.sayAge() //23 var instance2=new SubType("single",34) console.log(instance2.color) //["yellow","red","olive"] instance2.sayName() //single instance2.sayAge() //34
還有其他的繼承,花點時間寫一下
1、原型式繼承
克羅克福德寫的;借助原型可以基於已有的對象建立新對象,同時不必建立自訂類型
function object(o){ //本质上object()函数对其中对象的浅复制 function F(){} //创建一个新的构造函数 F.prototype=o //构造函数原型为传入的对象 return new F() //返回构造函数的实例 } var person={ name:"double", friends:["tom","jack","mike"] } var person1=object(person) //事实上为原型共享 person1.name="grey" person1.friends.push("single") console.log(person1.friends) //["tom", "jack", "mike", "single"] var person2=object(person) person2.name="red" console.log(person2.friends) //["tom", "jack", "mike", "single"]
ES5為了規範原型式的繼承,有個Object.create()來方便,IE9以上可以;只是想一個物件和另一個物件保持類似的情況,完全可以這種方法
var person={ name:"double", friends:["tom","jack","mike"] } var person1=Object.create(person) person1.name="single" person1.friends.push("singles") var person2=Object.create(person) console.log(person1.friends==person2.friends) //true //Object.create()接受两个参数,一个为作为新对象原型的对象,一个为新对象定义额外属性对象 var person={ name:"double", friends:["tom","jack","mike"] } var person1=Object.create(person,{ name:{ value:"single" //每个属性都是通过自己描述符定义的 } })
2、寄生式繼承
思路和原型式繼承一脈相承,建立一個用於封裝繼承過程的函數,內部透過方式增強對象,返回對象;主要考慮對象時使用
function object(o){ function F(){} F.prototype=o return new F() } function createPerson(original){ var clone=object(original) //继承原型 clone.sayName=function(){ alert("name") } return clone } var person={ name:"double", friends:["single","tom","jack"] } var person1=createPerson(person) person1.sayName() //name 引用类型值还是共享的
3、寄生組合繼承
組合繼承是繼承中常用到的,但是會呼叫兩次超類型建構函數;寄生組合繼承就是為了解決這個問題的
function object(o){ function F(){} F.prototype=o return new F() } function inheritPrototype(subType,superType){ var prototype=object(superType) //创建对象 (superType实例) prototype.constructor=subType //增强对象 subType.prototype=prototype //指定对象 (原型赋予实例) } function SuperType(name,sex){ this.name=name this.sex=sex this.colors=["red"] } SuperType.prototype.sayName=function(){ alert(this.name) } function SubType(name,sex,age){ SuperType.call(this,name,sex) this.age=age } inheritPrototype(SubType,SuperType) //目前subType.prototype什么都没有 SubType.prototype.sayAge=function(){ //为subType.prototype添加个方法 alert(this.age) } var person1=new SubType("double","man",34) console.log(person1.name) //SuperType 这是个Bug console.log(person1.sex) //man console.log(person1.colors) //["red"] person1.sayAge() //34
到此,差不多結束啦,感謝你對腳本之家的支持,希望我們整理的內容能幫助你。
相關推薦:
詳解JavaScript基於物件導向之繼承_javascript技巧
#以上是js物件導向之繼承知識詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!