摘要:繼承創建子類型的實例時,不能向超類型的構造函數中傳遞參數。借用構造函數偽造對象經典繼承在子類型構造函數內部調用超類型構造函數。組合繼承使用原型鏈實現對原型屬性和方法的繼承,而通過構造函數來實現實例屬性的繼承。
JS繼承 原型鏈 構造函數、原型、實例的關系
每個構造函數都有一個原型對象,原型對象包含一個指向構造函數的指針,而實例包含一個指向構造函數的指針。
原型鏈的構建是通過將一個類型的實例賦值給另一個構造函數的原型實現。
function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function () { return this.property; }; function SubType(){ this.subproperty=false; } //繼承SuperType SubType.prototype=new SuperType(); SuperType.prototype.getSubValue=function () { return this.subproperty; } var instance=new SubType(); console.log(instance.getSubValue()); //false console.log(instance.getSuperValue()); //true
SubType繼承SuperType是通過創建SuperType實例,并將該實例賦給SubType.prototype實現的。本質是重寫原型對象,代之以一個新類型的實例。
instance指向SubType的原型,SubType的原型又指向SuperType的原型。要注意的是instance.constructor現在指向的是SuperType,應為SubType的原型指向了SuperType的原型,而該原型對象的constructor屬性指向的SuperType。
調用instance.getSuperValue()會經歷一下三個步驟:
1)搜索實例
2)搜索SubType.prototype
3)搜索SuperType.prototype,最后一步才會找到該方法。在找不到屬性或方法的情況下,搜索過程總要一環一環地前行到原型鏈末端為止。
1.instanceof
instance instanceof Object
2.isPrototypeOf
Object.prototype.isPrototypeOf(instance)
1.子類型需要覆蓋超類型中的某個方法,或需要添加超類型中不存在的某個方法。給原型添加方法的代碼要放在替換原型的語句后面。
function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function () { return this.property; }; function SubType(){ this.subproperty=false; } //繼承SuperType SubType.prototype=new SuperType(); //添加新方法 SuperType.prototype.getSubValue=function () { return this.subproperty; } //重寫超類型中的方法 SubType.prototype.getSuperValue=function () { return false; } var instance=new SubType(); console.log(instance.getSuperValue());
2.通過原型鏈實現繼承時,不能使用對象字面量創建原型方法。這樣會重寫原型鏈。
function SuperType(){ this.property=true; } SuperType.prototype.getSuperValue=function () { return this.property; }; function SubType(){ this.subproperty=false; } //繼承SuperType SubType.prototype=new SuperType(); SubType.prototype={ getSubValue:function () { return this.subproperty; } }; var instance=new SubType(); console.log(instance.getSuperValue()); //error
以上代碼先把SuperType的實例賦值給原型,緊接著又將原型替換成一個對象字面量。由于現在的原型包含的是一個Object實例,而非SuperType的實例。因此,原來的SuperType和SubType之間的原型鏈被切斷。
原型鏈的問題1.包含引用類型的原型屬性會被所有實例共享。
function SuperType(){ this.nums=[1,2]; } function SubType(){ } //繼承SuperType SubType.prototype=new SuperType(); var instance=new SubType(); instance.nums.push(5); console.log(instance.nums); //[1,2,5] var instance1=new SubType(); instance1.nums.push(0); console.log(instance1.nums); //[1,2,5,0]
2.創建子類型的實例時,不能向超類型的構造函數中傳遞參數。
借用構造函數(偽造對象/經典繼承)在子類型構造函數內部調用超類型構造函數。
function SuperType(){ this.nums=[1,2]; } function SubType() { SuperType.call(this); } SubType.prototype=new SubType(); var instance=new SubType(); instance.nums.push(3); console.log(instance.nums); var instance2=new SubType(); console.log(instance2.nums);
通過使用call()方法(或apply()方法),在新創建的SubType實例的環境下調用了SuperType構造函數。這樣就會在新SubType對象上執行SuperType函數中定義的所有對象初始化代碼。SubType的每個實例都會具有自己的nums屬性的副本。
1.傳遞參數
子類型構造函數向超類型構造函數傳遞參數。
function SuperType(name){ this.name=name; this.school=["whu"]; } function SubType() { SuperType.call(this,"張三"); //實例屬性 this.age=20; } var instance=new SubType(); instance.school.push("hhu"); console.log(instance.name); console.log(instance.age); console.log(instance.school); var instance2=new SubType(); console.log(instance2.school);
2.存在的問題
方法都在構造函數中定義,函數無法復用;
在超類型的原型中定義的方法,對于子類型而言是不可見的,那么所有類型都只能使用構造函數模式。
使用原型鏈實現對原型屬性和方法的繼承,而通過構造函數來實現實例屬性的繼承。
function SuperType(name){ this.name=name; this.city=["武漢","杭州"]; } SuperType.prototype.sayName=function () { console.log(this.name); } function SubType(name,age) { //繼承屬性 SuperType.call(this,name); //實例屬性 this.age=age; } //繼承方法 SubType.prototype=new SuperType(); SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function () { console.log(this.age); } var instance1=new SubType("chen",18); instance1.city.push("北京"); console.log(instance1.city); instance1.sayName(); instance1.sayAge(); var instance2=new SubType("huang",19); console.log(instance2.city); instance2.sayName(); instance2.sayAge();原型式繼承
function object(o){ function F(){} F.prototype=o; return new F(); }
在object函數內部,先創建一個臨時性的構造函數,然后將傳入對象作為這個構造函數的原型,最后返回了這個臨時類型的一個新實例。本質上是object()對傳入其中的對象執行了一次淺復制。
var navy={ name:"海軍", weapon:["航母","驅逐艦"] }; function object(o){ function F() { } F.prototype=o; return new F(); } var navy1=object(navy); navy1.name="俄羅斯"; navy1.weapon.push("巡洋艦"); var navy2=object(navy); navy2.name="美國"; navy2.weapon.push("護衛艦"); console.log(navy.weapon);
ECMAScript5新增Object.create()方法規范了原型式繼承,接收兩個參數:一個是作用于新對象原型的對象,(可選)二是一個新對象定義額外屬性的對象.
var car={ name:"奔馳", weapon:["車輪","發動機"] }; var car1=Object.create(car,{ name:{ value:"寶馬" } }); console.log(car1.name);
原型式的問題依然是包含引用類型的屬性會所有實例被共享
寄生式繼承function object(o){ function F() { } F.prototype=o; return new F(); } function createAnother(original) { var clone=object(original); //通過調用函數創建一個新對象 clone.sayHi=function () { //以某種方式增強這個對象 console.log("hi"); }; return clone; //返回對象 } var person={ name:"coder", ability:["Java","R"] }; var another=createAnother(person); another.sayHi();
寄生式的問題依然是不能做到函數復用
寄生組合式繼承組合繼承最大的問題就是會調用兩次超類型構造函數:一次是創建子類型原型時候,一次是在子類型構造函數內部。
function SuperType(name){ this.name=name; this.city=["南京","蘇州"]; } SuperType.prototype.sayName=function () { console.log(this.name); } function SubType(name,age) { SuperType.call(this,name);//第二次調用SuperType this.age=age; } SubType.prototype=new SuperType(); //第一次調用SuperType SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function () { console.log(this.age); }
在第一次調用SuperType構造函數時,SubType.prototype會得到兩個屬性:name和colors;都是SuperType的實例屬性,只不過現在位于SubType的原型中.當調用SubType構造函數,又會調用一次SuperType的構造函數,這一次又在新對象上創建了實例屬性name和colors.于是這兩個屬性就屏蔽了原型中的兩個同名屬性.
寄生組合式繼承通過借用構造函數來繼承屬性,通過原型鏈的混成形式來繼承方法.其基本思想是:不必為了指定子類型的原型而調用超類型的構造函數,所需要的只是超類型原型的一個副本.本質上,就是使用寄生式繼承來繼承超類型的原型,然后再講結果指定給子類型的原型.
function inheritPrototype(subType,superType){ var prototype=object(superType.prototype); //創建對象 prototype.constructor=subType; //增強對象 subType.prototype=prototype; //指定對象 }
inheritPrototype函數接受兩個參數:子類型構造函數、超類型構造函數
1.創建超類型原型的一個副本
2.為創建的副本添加constructor屬性,彌補因重寫原型而失去的默認的constructor屬性.
3.將新創建的對象(即副本)賦值給子類型的原型.
function SuperType(name){ this.name=name; this.city=["南京","蘇州"]; } SuperType.prototype.sayName=function () { console.log(this.name); } function SubType(name,age) { SuperType.call(this,name);//第二次調用SuperType this.age=age; } inheritPrototype(SubType,SubType); SubType.prototype.sayAge=function () { console.log(this.age); }
上述只調用了一次SuperType構造函數,并因此避免了在SubType.ptototype上創建不必要的、多余的屬性.與此同時,原型鏈還能保持不變.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93627.html
摘要:可以通過構造函數和原型的方式模擬實現類的功能。原型式繼承與類式繼承類式繼承是在子類型構造函數的內部調用超類型的構造函數。寄生式繼承這種繼承方式是把原型式工廠模式結合起來,目的是為了封裝創建的過程。 js繼承的概念 js里常用的如下兩種繼承方式: 原型鏈繼承(對象間的繼承) 類式繼承(構造函數間的繼承) 由于js不像java那樣是真正面向對象的語言,js是基于對象的,它沒有類的概念。...
摘要:首先為了模擬類創建對象的功能搞出了構造函數。也就是名字膚色膚色這里是繼承里的自有屬性生命值這里繼承的共有屬性的方法攻擊力兵種美國大兵攻擊防御死亡膚色 JS面向對象之五 【繼承】 我們已經準備了很多前置知識,包括 原型鏈,對象和對象之間的關系 this,對象和函數之間的關系 new, 用函數批量創建特定的對象的語法糖 JS面向對象的前世今生 我們說,面向對象是一種寫代碼的套路。因為如...
摘要:對象創建的三種方式字面量創建方式系統內置構造函數方式自定義構造函數構造函數原型實例之間的關系實例是由構造函數實例化創建的,每個函數在被創建的時候,都會默認有一個對象。 JS 對象創建的三種方式 //字面量創建方式 var person= { name:jack?。? //系統內置構造函數方式 var person= new Object(); person.name = jack; ...
閱讀 785·2023-04-26 00:30
閱讀 2689·2021-11-23 09:51
閱讀 1045·2021-11-02 14:38
閱讀 2560·2021-09-07 10:23
閱讀 2243·2021-08-21 14:09
閱讀 1362·2019-08-30 10:57
閱讀 1603·2019-08-29 11:20
閱讀 1149·2019-08-26 13:53