摘要:繼承創(chuàng)建子類型的實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。借用構(gòu)造函數(shù)偽造對(duì)象經(jīng)典繼承在子類型構(gòu)造函數(shù)內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。組合繼承使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過構(gòu)造函數(shù)來實(shí)現(xiàn)實(shí)例屬性的繼承。
JS繼承 原型鏈 構(gòu)造函數(shù)、原型、實(shí)例的關(guān)系
每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象包含一個(gè)指向構(gòu)造函數(shù)的指針,而實(shí)例包含一個(gè)指向構(gòu)造函數(shù)的指針。
原型鏈的構(gòu)建是通過將一個(gè)類型的實(shí)例賦值給另一個(gè)構(gòu)造函數(shù)的原型實(shí)現(xiàn)。
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是通過創(chuàng)建SuperType實(shí)例,并將該實(shí)例賦給SubType.prototype實(shí)現(xiàn)的。本質(zhì)是重寫原型對(duì)象,代之以一個(gè)新類型的實(shí)例。
instance指向SubType的原型,SubType的原型又指向SuperType的原型。要注意的是instance.constructor現(xiàn)在指向的是SuperType,應(yīng)為SubType的原型指向了SuperType的原型,而該原型對(duì)象的constructor屬性指向的SuperType。
調(diào)用instance.getSuperValue()會(huì)經(jīng)歷一下三個(gè)步驟:
1)搜索實(shí)例
2)搜索SubType.prototype
3)搜索SuperType.prototype,最后一步才會(huì)找到該方法。在找不到屬性或方法的情況下,搜索過程總要一環(huán)一環(huán)地前行到原型鏈末端為止。
1.instanceof
instance instanceof Object
2.isPrototypeOf
Object.prototype.isPrototypeOf(instance)
1.子類型需要覆蓋超類型中的某個(gè)方法,或需要添加超類型中不存在的某個(gè)方法。給原型添加方法的代碼要放在替換原型的語句后面。
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.通過原型鏈實(shí)現(xiàn)繼承時(shí),不能使用對(duì)象字面量創(chuàng)建原型方法。這樣會(huì)重寫原型鏈。
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的實(shí)例賦值給原型,緊接著又將原型替換成一個(gè)對(duì)象字面量。由于現(xiàn)在的原型包含的是一個(gè)Object實(shí)例,而非SuperType的實(shí)例。因此,原來的SuperType和SubType之間的原型鏈被切斷。
原型鏈的問題1.包含引用類型的原型屬性會(huì)被所有實(shí)例共享。
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.創(chuàng)建子類型的實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)。
借用構(gòu)造函數(shù)(偽造對(duì)象/經(jīng)典繼承)在子類型構(gòu)造函數(shù)內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。
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()方法),在新創(chuàng)建的SubType實(shí)例的環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)。這樣就會(huì)在新SubType對(duì)象上執(zhí)行SuperType函數(shù)中定義的所有對(duì)象初始化代碼。SubType的每個(gè)實(shí)例都會(huì)具有自己的nums屬性的副本。
1.傳遞參數(shù)
子類型構(gòu)造函數(shù)向超類型構(gòu)造函數(shù)傳遞參數(shù)。
function SuperType(name){ this.name=name; this.school=["whu"]; } function SubType() { SuperType.call(this,"張三"); //實(shí)例屬性 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.存在的問題
方法都在構(gòu)造函數(shù)中定義,函數(shù)無法復(fù)用;
在超類型的原型中定義的方法,對(duì)于子類型而言是不可見的,那么所有類型都只能使用構(gòu)造函數(shù)模式。
使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過構(gòu)造函數(shù)來實(shí)現(xiàn)實(shí)例屬性的繼承。
function SuperType(name){ this.name=name; this.city=["武漢","杭州"]; } SuperType.prototype.sayName=function () { console.log(this.name); } function SubType(name,age) { //繼承屬性 SuperType.call(this,name); //實(shí)例屬性 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函數(shù)內(nèi)部,先創(chuàng)建一個(gè)臨時(shí)性的構(gòu)造函數(shù),然后將傳入對(duì)象作為這個(gè)構(gòu)造函數(shù)的原型,最后返回了這個(gè)臨時(shí)類型的一個(gè)新實(shí)例。本質(zhì)上是object()對(duì)傳入其中的對(duì)象執(zhí)行了一次淺復(fù)制。
var navy={ name:"海軍", weapon:["航母","驅(qū)逐艦"] }; 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("護(hù)衛(wèi)艦"); console.log(navy.weapon);
ECMAScript5新增Object.create()方法規(guī)范了原型式繼承,接收兩個(gè)參數(shù):一個(gè)是作用于新對(duì)象原型的對(duì)象,(可選)二是一個(gè)新對(duì)象定義額外屬性的對(duì)象.
var car={ name:"奔馳", weapon:["車輪","發(fā)動(dòng)機(jī)"] }; var car1=Object.create(car,{ name:{ value:"寶馬" } }); console.log(car1.name);
原型式的問題依然是包含引用類型的屬性會(huì)所有實(shí)例被共享
寄生式繼承function object(o){ function F() { } F.prototype=o; return new F(); } function createAnother(original) { var clone=object(original); //通過調(diào)用函數(shù)創(chuàng)建一個(gè)新對(duì)象 clone.sayHi=function () { //以某種方式增強(qiáng)這個(gè)對(duì)象 console.log("hi"); }; return clone; //返回對(duì)象 } var person={ name:"coder", ability:["Java","R"] }; var another=createAnother(person); another.sayHi();
寄生式的問題依然是不能做到函數(shù)復(fù)用
寄生組合式繼承組合繼承最大的問題就是會(huì)調(diào)用兩次超類型構(gòu)造函數(shù):一次是創(chuàng)建子類型原型時(shí)候,一次是在子類型構(gòu)造函數(shù)內(nèi)部。
function SuperType(name){ this.name=name; this.city=["南京","蘇州"]; } SuperType.prototype.sayName=function () { console.log(this.name); } function SubType(name,age) { SuperType.call(this,name);//第二次調(diào)用SuperType this.age=age; } SubType.prototype=new SuperType(); //第一次調(diào)用SuperType SubType.prototype.constructor=SubType; SubType.prototype.sayAge=function () { console.log(this.age); }
在第一次調(diào)用SuperType構(gòu)造函數(shù)時(shí),SubType.prototype會(huì)得到兩個(gè)屬性:name和colors;都是SuperType的實(shí)例屬性,只不過現(xiàn)在位于SubType的原型中.當(dāng)調(diào)用SubType構(gòu)造函數(shù),又會(huì)調(diào)用一次SuperType的構(gòu)造函數(shù),這一次又在新對(duì)象上創(chuàng)建了實(shí)例屬性name和colors.于是這兩個(gè)屬性就屏蔽了原型中的兩個(gè)同名屬性.
寄生組合式繼承通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法.其基本思想是:不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),所需要的只是超類型原型的一個(gè)副本.本質(zhì)上,就是使用寄生式繼承來繼承超類型的原型,然后再講結(jié)果指定給子類型的原型.
function inheritPrototype(subType,superType){ var prototype=object(superType.prototype); //創(chuàng)建對(duì)象 prototype.constructor=subType; //增強(qiáng)對(duì)象 subType.prototype=prototype; //指定對(duì)象 }
inheritPrototype函數(shù)接受兩個(gè)參數(shù):子類型構(gòu)造函數(shù)、超類型構(gòu)造函數(shù)
1.創(chuàng)建超類型原型的一個(gè)副本
2.為創(chuàng)建的副本添加constructor屬性,彌補(bǔ)因重寫原型而失去的默認(rèn)的constructor屬性.
3.將新創(chuàng)建的對(duì)象(即副本)賦值給子類型的原型.
function SuperType(name){ this.name=name; this.city=["南京","蘇州"]; } SuperType.prototype.sayName=function () { console.log(this.name); } function SubType(name,age) { SuperType.call(this,name);//第二次調(diào)用SuperType this.age=age; } inheritPrototype(SubType,SubType); SubType.prototype.sayAge=function () { console.log(this.age); }
上述只調(diào)用了一次SuperType構(gòu)造函數(shù),并因此避免了在SubType.ptototype上創(chuàng)建不必要的、多余的屬性.與此同時(shí),原型鏈還能保持不變.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93627.html
摘要:中的繼承上學(xué)過或者之類語言的同學(xué)應(yīng)該會(huì)對(duì)的繼承感到很困惑不要問我怎么知道的的繼承主要是基于原型的對(duì)的原型感興趣的同學(xué)可以了解一下我之前寫的中的原型對(duì)象相信很多同學(xué)也跟我一樣剛開始接觸的面向?qū)ο缶幊痰臅r(shí)候都抱著一種排斥的心態(tài)為什么這么 JS中的繼承(上) 學(xué)過java或者c#之類語言的同學(xué),應(yīng)該會(huì)對(duì)js的繼承感到很困惑--不要問我怎么知道的,js的繼承主要是基于原型(prototype)...
摘要:可以通過構(gòu)造函數(shù)和原型的方式模擬實(shí)現(xiàn)類的功能。原型式繼承與類式繼承類式繼承是在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型的構(gòu)造函數(shù)。寄生式繼承這種繼承方式是把原型式工廠模式結(jié)合起來,目的是為了封裝創(chuàng)建的過程。 js繼承的概念 js里常用的如下兩種繼承方式: 原型鏈繼承(對(duì)象間的繼承) 類式繼承(構(gòu)造函數(shù)間的繼承) 由于js不像java那樣是真正面向?qū)ο蟮恼Z言,js是基于對(duì)象的,它沒有類的概念。...
摘要:首先為了模擬類創(chuàng)建對(duì)象的功能搞出了構(gòu)造函數(shù)。也就是名字膚色膚色這里是繼承里的自有屬性生命值這里繼承的共有屬性的方法攻擊力兵種美國大兵攻擊防御死亡膚色 JS面向?qū)ο笾?【繼承】 我們已經(jīng)準(zhǔn)備了很多前置知識(shí),包括 原型鏈,對(duì)象和對(duì)象之間的關(guān)系 this,對(duì)象和函數(shù)之間的關(guān)系 new, 用函數(shù)批量創(chuàng)建特定的對(duì)象的語法糖 JS面向?qū)ο蟮那笆澜裆?我們說,面向?qū)ο笫且环N寫代碼的套路。因?yàn)槿?..
摘要:對(duì)象創(chuàng)建的三種方式字面量創(chuàng)建方式系統(tǒng)內(nèi)置構(gòu)造函數(shù)方式自定義構(gòu)造函數(shù)構(gòu)造函數(shù)原型實(shí)例之間的關(guān)系實(shí)例是由構(gòu)造函數(shù)實(shí)例化創(chuàng)建的,每個(gè)函數(shù)在被創(chuàng)建的時(shí)候,都會(huì)默認(rèn)有一個(gè)對(duì)象。 JS 對(duì)象創(chuàng)建的三種方式 //字面量創(chuàng)建方式 var person= { name:jack?。? //系統(tǒng)內(nèi)置構(gòu)造函數(shù)方式 var person= new Object(); person.name = jack; ...
摘要:構(gòu)造函數(shù)所以,就有了畸形的繼承方式原型鏈繼承三原型鏈繼承改變構(gòu)造函數(shù)的原型對(duì)象繼承了屬性以上例子中,暴露出原型鏈繼承的兩個(gè)問題包含引用類型數(shù)據(jù)的原型屬性,會(huì)被所有實(shí)例共享,基本數(shù)據(jù)類型則不會(huì)。 前言 眾所周知,JavaScript 中,沒有 JAVA 等主流語言類的概念,更沒有父子類繼承的概念,而是通過原型對(duì)象和原型鏈的方式實(shí)現(xiàn)繼承。 于是,我們這一篇講一講 JS 中的繼承(委托)。 ...
摘要:舉例說明組合繼承組合繼承利用原型鏈借用構(gòu)造函數(shù)的模式解決了原型鏈繼承和類式繼承的問題。示例組合式繼承是比較常用的一種繼承方法,其背后的思路是使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。 對(duì)js原型和繼承的理解一直處于不懂-懂-不懂-懂-不懂。。。的無限循環(huán)之中,本來打算只是簡(jiǎn)單總結(jié)下js繼承方式,可看了些網(wǎng)上的資料后,發(fā)現(xiàn)又不懂繼承了。。。這篇文章只...
閱讀 818·2023-04-26 00:30
閱讀 2730·2021-11-23 09:51
閱讀 1073·2021-11-02 14:38
閱讀 2678·2021-09-07 10:23
閱讀 2280·2021-08-21 14:09
閱讀 1447·2019-08-30 10:57
閱讀 1631·2019-08-29 11:20
閱讀 1177·2019-08-26 13:53