之前也有和大家講過有關(guān)JS的對象創(chuàng)建和對象繼承,本篇文章主要為大家做個匯總和梳理。
JS中其實(shí)就是原型鏈繼承和構(gòu)造函數(shù)繼承的“毛病”,還有就是“工廠、構(gòu)造、原型”設(shè)計(jì)模式與JS繼承。
JS高級程序設(shè)計(jì)4:class繼承的重點(diǎn),不只是簡簡單單的語法而已。
對象創(chuàng)建
不難發(fā)現(xiàn),每一篇都離不開工廠、構(gòu)造、原型這3種設(shè)計(jì)模式中的至少其一!
那JS為什么非要用到這種3種設(shè)計(jì)模式了呢??
我們先從對象創(chuàng)建講起:
let car={ price:100, color:"white", run:()=>{console.log("run fast")} }
當(dāng)有兩個或多個這樣的對象需要聲明時(shí),無法一直這樣復(fù)制下去的:
let car1={ price:100, color:"white", run:()=>{console.log("run fast")} } let car2={ price:200, color:"balck", run:()=>{console.log("run slow")} } let car3={ price:300, color:"red", run:()=>{console.log("broken")} }
這樣寫:
其實(shí)上面寫的不僅麻煩,而且代碼量也比較大;
也不必方便修改,比如當(dāng)car對象要增刪改一個屬性,需要多處進(jìn)行增刪改;
工廠函數(shù)
肯定是要封裝啦,第一個反應(yīng),可以借助函數(shù)來幫助我們批量創(chuàng)建對象~
于是乎:
function makeCar(price,color,performance){ let obj={} obj.price=price obj.color=color obj.run=()=>{console.log(performance)} return obj } let car1=makeCar("100","white","run fast") let car2=makeCar("200","black","run slow") let car3=makeCar("300","red","broken")
上面的是工廠設(shè)計(jì)模式在JS創(chuàng)建對象時(shí)應(yīng)用的由來~
如果有其他需求,比如,需要創(chuàng)建car4、car5、car6對象,它們要在原有基礎(chǔ)上再新增一個brand屬性,會怎么寫?
第一反應(yīng),直接修改makeCar
function makeCar(price,color,performance,brand){ let obj={} obj.price=price obj.color=color obj.run=()=>{console.log(performance)} obj.brand=brand return obj } let car4=makeCar("400","white","run fast","benz") let car5=makeCar("500","black","run slow","audi") let car6=makeCar("600","red","broken","tsl")
這代碼是錯誤的,會影響原有的car1、car2、car3對象;
那再重新寫一個makeCarChild工廠函數(shù)行不行?
function makeCarChild(price,color,performance,brand){ let obj={} obj.price=price obj.color=color obj.run=()=>{console.log(performance)} obj.brand=brand return obj } let car4=makeCarChild("400","white","run fast","benz") let car5=makeCarChild("500","black","run slow","audi") let car6=makeCarChild("600","red","broken","tsl")
為了方便,全量復(fù)制之前的屬性,建立N個相像的工廠,這種方法不行。
構(gòu)造函數(shù)
于是乎,在工廠設(shè)計(jì)模式上,發(fā)展出了:構(gòu)造函數(shù)設(shè)計(jì)模式,來解決以上復(fù)用(也就是繼承)的問題。
function MakeCar(price,color,performance){ this.price=price this.color=color this.run=()=>{console.log(performance)} } function MakeCarChild(brand,...args){ MakeCar.call(this,...args) this.brand=brand } let car4=new MakeCarChild("benz","400","white","run fast") let car5=new MakeCarChild("audi","500","black","run slow") let car6=new MakeCarChild("tsl","600","red","broken")
構(gòu)造函數(shù)區(qū)別于工廠函數(shù):
函數(shù)名首字母通常大寫;
創(chuàng)建對象的時(shí)候要用到new關(guān)鍵字(new的過程這里不再贅述了,之前文章有);
函數(shù)沒有return,而是通過this綁定來實(shí)現(xiàn)尋找屬性的;
到此為止,工廠函數(shù)的復(fù)用也解決了。
構(gòu)造+原型
新的問題在于,我們不能通過查找原型鏈從MakeCarChild找到MakeCar
car4.__proto__===MakeCarChild.prototype//true MakeCarChild.prototype.__proto__===MakeCar.prototype//false MakeCarChild.__proto__===MakeCar.prototype//false
無論在原型鏈上怎么找,都無法從MakeCarChild找到MakeCar
這樣也就明白了,子類不能繼承父類原型上的屬性
這里提個思考問題:為什么“要從原型鏈查找到”很重要?為什么“子類要繼承父類原型上的屬性”?就靠this綁定來找不行嗎?
于是乎,構(gòu)造函數(shù)設(shè)計(jì)模式+原型設(shè)計(jì)模式的【組合繼承】應(yīng)運(yùn)而生
function MakeCar(price,color,performance){ this.price=price this.color=color this.run=()=>{console.log(performance)} } function MakeCarChild(brand,...args){ MakeCar.call(this,...args) this.brand=brand } MakeCarChild.prototype=new MakeCar()//原型繼承父類的構(gòu)造器 MakeCarChild.prototype.constructor=MakeCarChild//重置constructor let car4=new MakeCarChild("benz","400","white","run fast")
現(xiàn)在再找原型,就找的到啦:
car4.__proto__===MakeCarChild.prototype//true MakeCarChild.prototype.__proto__===MakeCar.prototype//true
其實(shí),能到這里,就已經(jīng)很很優(yōu)秀了,該有的都有了,寫法也不算是很復(fù)雜。
工廠+構(gòu)造+原型
但,總有人在追求極致。
上述的組合繼承,父類構(gòu)造函數(shù)被調(diào)用了兩次,一次是call的過程,一次是原型繼承new的過程,如果每次實(shí)例化,都重復(fù)調(diào)用,肯定是不可取的,怎樣避免?
工廠+構(gòu)造+原型=寄生組合繼承應(yīng)運(yùn)而生
核心是,通過工廠函數(shù)新建一個中間商F(),復(fù)制了一份父類的原型對象,再賦給子類的原型;
function object(o){//工廠函數(shù) function F(){} F.prototype=o; return new F();//new一個空的函數(shù),所占內(nèi)存很小 } function inherit(child,parent){//原型繼承 var prototype=object(parent.prototype) prototype.constructor=child child.prototype=prototype } function MakeCar(price,color,performance){ this.price=price this.color=color this.run=()=>{console.log(performance)} } function MakeCarChild(brand,...args){//構(gòu)造函數(shù) MakeCar.call(this,...args) this.brand=brand } inherit(MakeCarChild,MakeCar) let car4=new MakeCarChild("benz","400","white","run fast")
car4.__proto__===MakeCarChild.prototype//true MakeCarChild.prototype.__proto__===MakeCar.prototype//true
ES6 class
再到后來,ES6的class作為寄生組合繼承的語法糖:
class MakeCar{ constructor(price,color,performance){ this.price=price this.color=color this.performance=performance } run(){ console.log(console.log(this.performance)) } } class MakeCarChild extends MakeCar{ constructor(brand,...args){ super(brand,...args); this.brand=brand; } } let car4=new MakeCarChild("benz","400","white","run fast")
car4.__proto__===MakeCarChild.prototype//true MakeCarChild.prototype.__proto__===MakeCar.prototype//true
有興趣的工友,可以看下ES6解析成ES5的代碼:原型與原型鏈-ES6 Class的底層實(shí)現(xiàn)原理#22
對象與函數(shù)
最后本瓜想再談?wù)勱P(guān)于JS對象和函數(shù)的關(guān)系:
即使是這樣聲明一個對象,let obj={},它一樣是由構(gòu)造函數(shù)Object構(gòu)造而來的:
let obj={} obj.__proto__===Object.prototype//true
在JS中,萬物皆對象,對象都是有函數(shù)構(gòu)造而來,函數(shù)本身也是對象。
對應(yīng)代碼中的意思:
所有的構(gòu)造函數(shù)的隱式原型都等于Function的顯示原型,函數(shù)都是由Function構(gòu)造而來,Object構(gòu)造函數(shù)也不例外;
所有構(gòu)造函數(shù)的顯示原型的隱式原型,都等于Object的顯示原型,F(xiàn)unction也不例外;
//1. Object.__proto__===Function.prototype//true //2. Function.prototype.__proto__===Object.prototype//true
對于這個設(shè)計(jì)我只可以給一個大大的無語了。
先這樣說吧:Function就是上帝,上帝創(chuàng)造了萬物;Object就是萬物。萬物由上帝創(chuàng)造(對象由函數(shù)構(gòu)造而來),上帝本身也屬于一種物質(zhì)(函數(shù)本身卻也是對象);
對于本篇來說,繼承,其實(shí)都是父子構(gòu)造函數(shù)在繼承,然后再由構(gòu)造函數(shù)實(shí)例化對象,以此來實(shí)現(xiàn)對象的繼承。
到底是誰在繼承?函數(shù)?對象?都是吧~~
小結(jié)
本篇由創(chuàng)建對象說起,講了工廠函數(shù),它可以做一層最基本的封裝;
再到,對工廠的拓展,演進(jìn)為構(gòu)造函數(shù);
再基于原型特點(diǎn),構(gòu)造+原型,得出組合繼承;
再追求極致,講到寄生組合;
再講到簡化書寫的Es6 class;
以及最后對對象與函數(shù)的思考。
本篇文章到此結(jié)束了,歡迎大家關(guān)注后續(xù)更多精彩內(nèi)容。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/130356.html
摘要:最近的一次更新的變量有效,并且會作用于全部的引用的處理方式和相同,變量值輸出時(shí)根據(jù)之前最近的一次定義計(jì)算,每次引用最近的定義有效嵌套三種預(yù)編譯器的選擇器嵌套在使用上來說沒有任何區(qū)別,甚至連引用父級選擇器的標(biāo)記也相同。 面試匯總一:2018大廠高級前端面試題匯總 高級面試:【半月刊】前端高頻面試題及答案匯總 css內(nèi)容 響應(yīng)式布局 當(dāng)前主流的三種預(yù)編譯器比較 CSS預(yù)處理器用一種專門的...
摘要:中創(chuàng)建對象的方式有很多,尤其是基于原型的方式創(chuàng)建對象,是理解基于原型繼承的基礎(chǔ)。該函數(shù)中的屬性指向該源性對象當(dāng)通過該函數(shù)的構(gòu)造函數(shù)創(chuàng)建一個具體對象時(shí),在這個對象中,就會有一個屬性指向原型。 js中創(chuàng)建對象的方式有很多,尤其是基于原型的方式創(chuàng)建對象,是理解基于原型繼承的基礎(chǔ)。因此在這里匯總一下,并對各種方法的利弊進(jìn)行總結(jié)和對比,不至于以后對這些概念有模糊。 簡單方式創(chuàng)建 var o = ...
摘要:對象脫離常規(guī)流,偏移定位是以窗口為參考絕對定位的元素,在,,,屬性未設(shè)置時(shí),會緊隨在其前面的兄弟元素之后,但在位置上不影響常規(guī)流中的任何元素。例如設(shè)置百分比時(shí),子元素繼承是父元素乘以百分百之后的具體數(shù)值,所以可能會出現(xiàn)重疊現(xiàn)象。元素寬高 width,min-width,max-width等元素寬度設(shè)置百分比,以包含塊的寬度為標(biāo)準(zhǔn)進(jìn)行計(jì)算; height,min-height,max-hei...
摘要:只要沒有被覆蓋的話對象原型的屬性就能在所有的實(shí)例中找到,若整個原型鏈未找到則返回如何實(shí)現(xiàn)繼承構(gòu)造繼承原型繼承實(shí)例繼承拷貝繼承原型機(jī)制或和方法去實(shí)現(xiàn)較簡單,建議使用構(gòu)造函數(shù)與原型混合方式。 HTML相關(guān)問題 1.XHTML和HTML有什么區(qū)別 HTML是一種基本的WEB網(wǎng)頁設(shè)計(jì)語言,XHTML是一個基于XML的標(biāo)記語言最主要的不同:XHTML 元素必須被正確地嵌套。XHTML 元素必須被...
摘要:只要沒有被覆蓋的話對象原型的屬性就能在所有的實(shí)例中找到,若整個原型鏈未找到則返回如何實(shí)現(xiàn)繼承構(gòu)造繼承原型繼承實(shí)例繼承拷貝繼承原型機(jī)制或和方法去實(shí)現(xiàn)較簡單,建議使用構(gòu)造函數(shù)與原型混合方式。 HTML相關(guān)問題 1.XHTML和HTML有什么區(qū)別 HTML是一種基本的WEB網(wǎng)頁設(shè)計(jì)語言,XHTML是一個基于XML的標(biāo)記語言最主要的不同:XHTML 元素必須被正確地嵌套。XHTML 元素必須被...
閱讀 506·2023-03-27 18:33
閱讀 708·2023-03-26 17:27
閱讀 607·2023-03-26 17:14
閱讀 576·2023-03-17 21:13
閱讀 502·2023-03-17 08:28
閱讀 1754·2023-02-27 22:32
閱讀 1261·2023-02-27 22:27
閱讀 2066·2023-01-20 08:28