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