摘要:眾所周知,是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,如果說(shuō)針對(duì)面向?qū)ο髞?lái)發(fā)問(wèn)的話,我會(huì)想到兩個(gè)問(wèn)題,在中,類與實(shí)例對(duì)象是如何創(chuàng)建的,類與實(shí)例對(duì)象又是如何實(shí)現(xiàn)繼承的。但是在中是指向的,因?yàn)槊恳粋€(gè)構(gòu)造函數(shù)其實(shí)都是這個(gè)對(duì)象構(gòu)造的,中子類的指向父類可以實(shí)現(xiàn)屬性的繼承。
眾所周知,Javascript是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,如果說(shuō)針對(duì)面向?qū)ο髞?lái)發(fā)問(wèn)的話,我會(huì)想到兩個(gè)問(wèn)題,在js中,類與實(shí)例對(duì)象是如何創(chuàng)建的,類與實(shí)例對(duì)象又是如何實(shí)現(xiàn)繼承的。
面向?qū)ο?/b> 如何聲明一個(gè)類ES5中,還沒(méi)有類的概念,而是通過(guò)函數(shù)來(lái)聲明;到了ES6,有了class關(guān)鍵字,則通過(guò)class來(lái)聲明
// 類的聲明 var Animal = function () { this.name = "Animal"; }; // es6中class的聲明 class Animal2 { constructor () { this.name = "Animal2"; }如何創(chuàng)建對(duì)象
1.字面量對(duì)象
2.顯示的構(gòu)造函數(shù)
3.Object.create
// 第一種方式:字面量 var o1 = {name: "o1"}; var o2 = new Object({name: "o2"}); // 第二種方式:構(gòu)造函數(shù) var M = function (name) { this.name = name; }; var o3 = new M("o3"); // 第三種方式:Object.create var p = {name: "p"}; var o4 = Object.create(p);類與繼承
如何實(shí)現(xiàn)繼承?
繼承的本質(zhì)就是原型鏈
/** * 借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承 */ function Parent1 () { this.name = "parent1"; } Parent1.prototype.say = function () { }; function Child1 () { Parent1.call(this); // 或Parent1.apply(this,arguments) this.type = "child1"; } console.log(new Child1(), new Child1().say());
重點(diǎn)是這句:Parent1.call(this); 在子類的構(gòu)造函數(shù)里執(zhí)行父類的構(gòu)造函數(shù),通過(guò)call/apply改變this指向,從而導(dǎo)致父類構(gòu)造函數(shù)執(zhí)行時(shí)的這些屬性都會(huì)掛載到子類實(shí)例上去。
問(wèn)題: 只能繼承父類構(gòu)造函數(shù)中聲明的實(shí)例屬性,并沒(méi)有繼承父類原型的屬性和方法
/** * 借助原型鏈實(shí)現(xiàn)繼承 */ function Parent2 () { this.name = "parent2"; this.play = [1, 2, 3]; } function Child2 () { this.type = "child2"; } Child2.prototype = new Parent2(); var s1 = new Child2(); var s2 = new Child2(); console.log(s1.play, s2.play); s1.play.push(4);
重點(diǎn)就是這句: Child2.prototype = new Parent2(); 就是說(shuō) new 一個(gè)父類的實(shí)例,然后賦給子類的原型 也就是說(shuō) new Child2().__proto__ === Child2.prototype === new Parent2()當(dāng)我們?cè)趎ew Child2()中找不到屬性/方法,順著原型鏈就能找到new Parent2(),這樣就實(shí)現(xiàn)了繼承。
問(wèn)題: 原型鏈中的原型對(duì)象是共用的,子類無(wú)法通過(guò)父類創(chuàng)建私有屬性
比如當(dāng)你new兩個(gè)子類s1、s2的時(shí)候,改s1的屬性,s2的屬性也跟著改變
/** * 組合方式 */ function Parent3 () { this.name = "parent3"; this.play = [1, 2, 3]; } function Child3 () { Parent3.call(this); // 父類構(gòu)造函數(shù)執(zhí)行了 this.type = "child3"; } Child3.prototype = new Parent3(); // 父類構(gòu)造函數(shù)執(zhí)行了 var s3 = new Child3(); var s4 = new Child3(); s3.play.push(4); console.log(s3.play, s4.play);
組合式就是原型鏈+構(gòu)造函數(shù)繼承,解決了前兩種方法的問(wèn)題,但也有不足:子類實(shí)例化時(shí),父類構(gòu)造函數(shù)執(zhí)行了兩次,所以有了下面的組合繼承的優(yōu)化1
組合繼承的優(yōu)化1/** * 組合繼承的優(yōu)化1 * @type {String} */ function Parent4 () { this.name = "parent4"; this.play = [1, 2, 3]; } function Child4 () { Parent4.call(this); this.type = "child4"; } Child4.prototype = Parent4.prototype; var s5 = new Child4(); var s6 = new Child4(); console.log(s5, s6); console.log(s5 instanceof Child4, s5 instanceof Parent4); console.log(s5.constructor);
其實(shí)就是把原型鏈繼承的那句 Child4.prototype = new Parent4(); 改為 Child4.prototype = Parent4.prototype; 這樣雖然父類構(gòu)造函數(shù)只執(zhí)行了一次了,但又有了新的問(wèn)題: 無(wú)法判斷s5是Child4的實(shí)例還是Parent4的實(shí)例 因?yàn)镃hild4.prototype.constructor指向了Parent4的實(shí)例;如果直接加一句 Child4.prototype.constructor = Child4 也不行,這樣Parent4.prototype.constructor也指向Child4,就無(wú)法區(qū)分父類實(shí)例了。
若要判斷a是A的實(shí)例 用constructor組合繼承的優(yōu)化2(推薦)
a.__proto__.constructor === A
用instanceof則不準(zhǔn)確, instanceof 判斷 實(shí)例對(duì)象的__proto__ 是不是和 構(gòu)造函數(shù)的prototype 是同一個(gè)引用。若A 繼承 B, B 繼承 C 在該原型鏈上的對(duì)象 用instanceof判斷都返回ture
/** * 組合繼承的優(yōu)化2 */ function Parent5 () { this.name = "parent5"; this.play = [1, 2, 3]; } function Child5 () { Parent5.call(this); this.type = "child5"; } //注意此處,用到了Object.creat(obj)方法,該方法會(huì)對(duì)傳入的obj對(duì)象進(jìn)行淺拷貝 //這個(gè)方法作為一個(gè)橋梁,達(dá)到父類和子類的一個(gè)隔離 Child5.prototype = Object.create(Parent5.prototype); //修改構(gòu)造函數(shù)指向 Child5.prototype.constructor = Child5
構(gòu)造函數(shù)屬性繼承和建立子類和父類原型的鏈接
ES6實(shí)現(xiàn)繼承引入了class、extends、super關(guān)鍵字,在子類構(gòu)造函數(shù)里調(diào)用super()方法來(lái)調(diào)用父類的構(gòu)造函數(shù)。
在子類的構(gòu)造函數(shù)中,只有調(diào)用super之后,才可以使用this關(guān)鍵字,否則會(huì)報(bào)錯(cuò)。這是因?yàn)樽宇悓?shí)例的構(gòu)建,是基于對(duì)父類實(shí)例加工,只有super方法才能返回父類實(shí)例。
class Child6 extends Parent6 { constructor(x, y, color) { super(x, y); // 調(diào)用父類的constructor(x, y) this.color = color; } toString() { return this.color + " " + super.toString(); // super代表父類原型,調(diào)用父類的toString() } }class實(shí)現(xiàn)原理
Class充當(dāng)了ES5中構(gòu)造函數(shù)在繼承實(shí)現(xiàn)過(guò)程中的作用
有prototype屬性,有__proto__屬性,這個(gè)屬性在ES6中的指向有一些主動(dòng)的修改。
同時(shí)存在兩條繼承鏈:一條實(shí)現(xiàn)屬性繼承,一條實(shí)現(xiàn)方法繼承。
class A extends B {} A.__proto__ === B; //繼承屬性 A.prototype.__proto__ === B.prototype; //繼承方法
ES6的子類的__proto__是父類,子類的原型的__proto__是父類的原型。
但是在ES5中 A.__proto__是指向Function.prototype的,因?yàn)槊恳粋€(gè)構(gòu)造函數(shù)其實(shí)都是Function這個(gè)對(duì)象構(gòu)造的,ES6中子類的__proto__指向父類可以實(shí)現(xiàn)屬性的繼承。
只有函數(shù)有prototype屬性,只有對(duì)象有__proto__屬性 ;但函數(shù)也有__proto__屬性,因?yàn)楹瘮?shù)也是一個(gè)對(duì)象,函數(shù)的__proto__等于 Function.prototype。extends實(shí)現(xiàn)原理
//原型連接 Man.prototype = Object.create(Person.prototype); // B繼承A的靜態(tài)屬性 Object.setPrototypeOf(Man, Person); //綁定this Person.call(this);
前兩句實(shí)現(xiàn)了原型鏈上的繼承,最后一句實(shí)現(xiàn)構(gòu)造函數(shù)上的繼承。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93997.html
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:很多情況下,通常一個(gè)人類,即創(chuàng)建了一個(gè)具體的對(duì)象。對(duì)象就是數(shù)據(jù),對(duì)象本身不包含方法。類是相似對(duì)象的描述,稱為類的定義,是該類對(duì)象的藍(lán)圖或原型。在中,對(duì)象通過(guò)對(duì)類的實(shí)體化形成的對(duì)象。一類的對(duì)象抽取出來(lái)。注意中,對(duì)象一定是通過(guò)類的實(shí)例化來(lái)的。 showImg(https://segmentfault.com/img/bVTJ3H?w=900&h=385); 馬上就要到七夕了,離年底老媽老爸...
摘要:是完全的面向?qū)ο笳Z(yǔ)言,它們通過(guò)類的形式組織函數(shù)和變量,使之不能脫離對(duì)象存在。而在基于原型的面向?qū)ο蠓绞街校瑢?duì)象則是依靠構(gòu)造器利用原型構(gòu)造出來(lái)的。 JavaScript 函數(shù)式腳本語(yǔ)言特性以及其看似隨意的編寫(xiě)風(fēng)格,導(dǎo)致長(zhǎng)期以來(lái)人們對(duì)這一門(mén)語(yǔ)言的誤解,即認(rèn)為 JavaScript 不是一門(mén)面向?qū)ο蟮恼Z(yǔ)言,或者只是部分具備一些面向?qū)ο蟮奶卣?。本文將回歸面向?qū)ο蟊疽?,從?duì)語(yǔ)言感悟的角度闡述為什...
摘要:之前,本質(zhì)上不能算是一門(mén)面向?qū)ο蟮木幊陶Z(yǔ)言,因?yàn)樗鼘?duì)于封裝繼承多態(tài)這些面向?qū)ο笳Z(yǔ)言的特點(diǎn)并沒(méi)有在語(yǔ)言層面上提供原生的支持。所以在中出現(xiàn)了等關(guān)鍵字,解決了面向?qū)ο笾谐霈F(xiàn)了問(wèn)題。 ES6之前,javascript本質(zhì)上不能算是一門(mén)面向?qū)ο蟮木幊陶Z(yǔ)言,因?yàn)樗鼘?duì)于封裝、繼承、多態(tài)這些面向?qū)ο笳Z(yǔ)言的特點(diǎn)并沒(méi)有在語(yǔ)言層面上提供原生的支持。但是,它引入了原型(prototype)的概念,可以讓我們以...
摘要:除了以上介紹的幾種對(duì)象創(chuàng)建方式,此外還有寄生構(gòu)造函數(shù)模式穩(wěn)妥構(gòu)造函數(shù)模式。 showImg(https://segmentfault.com/img/remote/1460000018196128); 面向?qū)ο?是以 對(duì)象 為中心的編程思想,它的思維方式是構(gòu)造。 面向?qū)ο?編程的三大特點(diǎn):封裝、繼承、多態(tài): 封裝:屬性方法的抽象 繼承:一個(gè)類繼承(復(fù)制)另一個(gè)類的屬性/方法 多態(tài):方...
閱讀 2365·2021-11-16 11:52
閱讀 2338·2021-11-11 16:55
閱讀 765·2021-09-02 15:41
閱讀 2997·2019-08-30 15:54
閱讀 3156·2019-08-30 15:54
閱讀 2265·2019-08-29 15:39
閱讀 1520·2019-08-29 15:18
閱讀 981·2019-08-29 13:00