JS對(duì)象創(chuàng)建與繼承的匯總梳理
引言
在 6 月更文中零零散散講了 JS 的對(duì)象創(chuàng)建和對(duì)象繼承,有工友對(duì)此還是表示疑惑,要注意:這是兩個(gè)不同但又相關(guān)的東西,千萬(wàn)別搞混了!
這些文章是:
驀然回首,“工廠、構(gòu)造、原型”設(shè)計(jì)模式,正在燈火闌珊處
JS精粹,原型鏈繼承和構(gòu)造函數(shù)繼承的 “毛病”
“工廠、構(gòu)造、原型” 設(shè)計(jì)模式與 JS 繼承
JS 高級(jí)程序設(shè)計(jì) 4:class 繼承的重點(diǎn)
JS class 并不只是簡(jiǎn)單的語(yǔ)法糖!
本篇作為匯總篇,來(lái)一探究竟??!沖沖沖
對(duì)象創(chuàng)建
不難發(fā)現(xiàn),每一篇都離不開(kāi)工廠、構(gòu)造、原型這 3 種設(shè)計(jì)模式中的至少其一!
讓人不禁想問(wèn):JS 為什么非要用到這種 3 種設(shè)計(jì)模式了呢??
正本溯源,先從對(duì)象創(chuàng)建講起:
我們本來(lái)習(xí)慣這樣聲明對(duì)象(不用任何設(shè)計(jì)模式)
let car= { price:100, color:"white", run:()=>{console.log("run fast")} }
當(dāng)有兩個(gè)或多個(gè)這樣的對(duì)象需要聲明時(shí),是不可能一直復(fù)制寫(xiě)下去的:
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")} }
這樣寫(xiě):
- 寫(xiě)起來(lái)麻煩,重復(fù)的代碼量大;
- 不利于修改,比如當(dāng) car 對(duì)象要增刪改一個(gè)屬性,需要多處進(jìn)行增刪改;
工廠函數(shù)
肯定是要封裝啦,第一個(gè)反應(yīng),可以 借助函數(shù) 來(lái)幫助我們批量創(chuàng)建對(duì)象~
于是乎:
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)建對(duì)象時(shí)應(yīng)用的由來(lái)~
到這里,對(duì)于【對(duì)象創(chuàng)建】來(lái)說(shuō),應(yīng)該夠用了吧?是,在不考慮擴(kuò)展的情況下,基本夠用了。
但這個(gè)時(shí)候來(lái)個(gè)新需求,需要?jiǎng)?chuàng)建 car4、car5、car6 對(duì)象,它們要在原有基礎(chǔ)上再新增一個(gè) brand
屬性,會(huì)怎么寫(xiě)?
第一反應(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")
這樣寫(xiě),不行,會(huì)影響原有的 car1、car2、car3 對(duì)象;
那再重新寫(xiě)一個(gè) 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è)相像的工廠,顯得太蠢了。。。
構(gòu)造函數(shù)
于是乎,在工廠設(shè)計(jì)模式上,發(fā)展出了:構(gòu)造函數(shù)設(shè)計(jì)模式,來(lái)解決以上復(fù)用(也就是繼承)的問(wè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")
構(gòu)造函數(shù)區(qū)別于工廠函數(shù):
- 函數(shù)名首字母通常大寫(xiě);
- 創(chuàng)建對(duì)象的時(shí)候要用到 new 關(guān)鍵字(new 的過(guò)程這里不再贅述了,之前文章有);
- 函數(shù)沒(méi)有 return,而是通過(guò) this 綁定來(lái)實(shí)現(xiàn)尋找屬性的;
到此為止,工廠函數(shù)的復(fù)用也解決了。
構(gòu)造+原型
新的問(wèn)題在于,我們不能通過(guò)查找原型鏈從 MakeCarChild 找到 MakeCar
car4.__proto__===MakeCarChild.prototype // true MakeCarChild.prototype.__proto__ === MakeCar.prototype // false MakeCarChild.__proto__ === MakeCar.prototype // false
無(wú)論在原型鏈上怎么找,都無(wú)法從 MakeCarChild
找到 MakeCar
這就意味著:子類不能繼承父類原型上的屬性
這里提個(gè)思考問(wèn)題:為什么“要從原型鏈查找到”很重要?為什么“子類要繼承父類原型上的屬性”?就靠 this 綁定來(lái)找不行嗎?
于是乎,構(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)秀了,該有的都有了,寫(xiě)法也不算是很復(fù)雜。
工廠+構(gòu)造+原型
但,總有人在追求極致。
上述的組合繼承,父類構(gòu)造函數(shù)被調(diào)用了兩次,一次是 call 的過(guò)程,一次是原型繼承 new 的過(guò)程,如果每次實(shí)例化,都重復(fù)調(diào)用,肯定是不可取的,怎樣避免?
工廠 + 構(gòu)造 + 原型 = 寄生組合繼承 應(yīng)運(yùn)而生
核心是,通過(guò)工廠函數(shù)新建一個(gè)中間商 F( ),復(fù)制了一份父類的原型對(duì)象,再賦給子類的原型;
function object(o) { // 工廠函數(shù) function F() {} F.prototype = o; return new F(); // new 一個(gè)空的函數(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
再到后來(lái),ES6 的 class 作為寄生組合繼承的語(yǔ)法糖:
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
對(duì)象與函數(shù)
最后本瓜想再談?wù)勱P(guān)于 JS 對(duì)象和函數(shù)的關(guān)系:
即使是這樣聲明一個(gè)對(duì)象,let obj = {}
,它一樣是由構(gòu)造函數(shù) Object
構(gòu)造而來(lái)的:
let obj = {} obj.__proto__ === Object.prototype // true
在 JS 中,萬(wàn)物皆對(duì)象,對(duì)象都是有函數(shù)構(gòu)造而來(lái),函數(shù)本身也是對(duì)象。
對(duì)應(yīng)代碼中的意思:
- 所有的構(gòu)造函數(shù)的隱式原型都等于 Function 的顯示原型,函數(shù)都是由 Function 構(gòu)造而來(lái),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
這個(gè)設(shè)計(jì)真的就一個(gè)大無(wú)語(yǔ),大糾結(jié),大麻煩。。。
只能先按之前提過(guò)的歪理解記著先:Function 就是上帝,上帝創(chuàng)造了萬(wàn)物;Object 就是萬(wàn)物。萬(wàn)物由上帝創(chuàng)造(對(duì)象由函數(shù)構(gòu)造而來(lái)),上帝本身也屬于一種物質(zhì)(函數(shù)本身卻也是對(duì)象);
對(duì)于本篇來(lái)說(shuō),繼承,其實(shí)都是父子構(gòu)造函數(shù)在繼承,然后再由構(gòu)造函數(shù)實(shí)例化對(duì)象,以此來(lái)實(shí)現(xiàn)對(duì)象的繼承。
到底是誰(shuí)在繼承?函數(shù)?對(duì)象?都是吧~~
小結(jié)
本篇由創(chuàng)建對(duì)象說(shuō)起,講了工廠函數(shù),它可以做一層最基本的封裝;
再到,對(duì)工廠的拓展,演進(jìn)為構(gòu)造函數(shù);
再基于原型特點(diǎn),構(gòu)造+原型,得出組合繼承;
再追求極致,講到寄生組合;
再講到簡(jiǎn)化書(shū)寫(xiě)的 Es6 class ;
以及最后對(duì)對(duì)象與函數(shù)的思考。
更多關(guān)于JS對(duì)象創(chuàng)建繼承的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- JS創(chuàng)建對(duì)象常用設(shè)計(jì)模式工廠構(gòu)造函數(shù)及原型
- java鏈?zhǔn)絼?chuàng)建json對(duì)象的實(shí)現(xiàn)
- JS創(chuàng)建對(duì)象的四種方式
- JavaScript函數(shù)this指向問(wèn)題詳解
- 詳解JavaScript原型對(duì)象的this指向問(wèn)題
- JavaScript 中this指向問(wèn)題案例詳解
- JavaScript函數(shù)中this指向問(wèn)題詳解
- JavaScript中的this指向問(wèn)題詳解
- JavaScript創(chuàng)建對(duì)象的幾種方式及關(guān)于this指向問(wèn)題
相關(guān)文章
arrify 轉(zhuǎn)數(shù)組實(shí)現(xiàn)示例源碼解析
這篇文章主要為大家介紹了arrify 轉(zhuǎn)數(shù)組實(shí)現(xiàn)示例源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12關(guān)于Javascript閉包與應(yīng)用的詳解
這篇文章主要介紹了關(guān)于Javascript閉包與應(yīng)用的詳解,文中有非常詳細(xì)的代碼示例.對(duì)正在學(xué)習(xí)js的伙伴們有很好的幫助,需要的朋友可以參考下2021-04-04Three.js引用和環(huán)境搭建過(guò)程詳解
這篇文章主要為大家介紹了Three.js引用和環(huán)境搭建過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05微信小程序 WXDropDownMenu組件詳解及實(shí)例代碼
這篇文章主要介紹了微信小程序 WXDropDownMenu組件詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-10-10原型和原型鏈 prototype和proto的區(qū)別詳情
原型是function對(duì)象下的屬性,它定義了構(gòu)造函數(shù)的共同祖先,也就是一個(gè)父子級(jí)的關(guān)系,子對(duì)象會(huì)繼承父對(duì)象的方法和屬性,每個(gè)實(shí)例對(duì)象下都有__proto__屬性,通過(guò)屬性__proto__指向構(gòu)造函數(shù)的原型對(duì)象,當(dāng)?shù)竭_(dá)末端時(shí),返回null,這樣一層一層向頂端查找,就形成了原型鏈2021-10-10JavaScript單例模式能不能去實(shí)例只留單原理解析
這篇文章主要為大家介紹了JavaScript單例模式能不能去實(shí)例只留單原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12