摘要:不然原型鏈會(huì)斷開。喵喵喵這樣會(huì)使上一條語句失效,從而使原型鏈斷開。這是在原型鏈里面無法做到的一個(gè)功能。屬性使用借用構(gòu)造函數(shù)模式,而方法則使用原型鏈。
一、對(duì)象的繼承 1.了解原型鏈
在上一篇我們講過關(guān)于原型對(duì)象的概念,當(dāng)然如果不了解的建議去翻看第一篇文章,文末附有連接。我們知道每個(gè)對(duì)象都有各自的原型對(duì)象,那么當(dāng)我們把一個(gè)對(duì)象的實(shí)例當(dāng)做另外一個(gè)對(duì)象的原型對(duì)象。。這樣這個(gè)對(duì)象就擁有了另外一個(gè)引用類型的所有方法與屬性,當(dāng)我們?cè)侔言搶?duì)象的實(shí)例賦予另一個(gè)原型對(duì)象時(shí),這樣又把這些方法繼承下去。如此層層遞進(jìn),對(duì)象與原型間存在鏈接關(guān)系,這樣就構(gòu)成了原型鏈。
function Animal(){ this.type = "Animal"; } Animal.prototype.say = function(){ console.log(this.type); } function Cat(){ this.vioce = "喵喵喵"; } Cat.prototype = new Animal(); Cat.prototype.shout = function(){ console.log(this.vioce); } let cat1 = new Cat(); cat1.say(); //"Animal" //當(dāng)然,我們還可以繼續(xù)繼承下去 function Tom(){ this.name = "Tom"; } Tom.prototype = new Cat(); Tom.prototype.sayName = function(){ console.log(this.name); } let cat2 = new Tom(); cat2.say(); //"Animal" cat2.shout(); //"喵喵喵" cat2.sayName(); //"Tom" cat1.sayName(); //err 報(bào)錯(cuò)表示沒有該函數(shù)
很神奇的,原型鏈就實(shí)現(xiàn)了對(duì)象的繼承。使用原型鏈就可以使一個(gè)新對(duì)象擁有之前對(duì)象的所有方法和屬性。至于cat1.sayName()會(huì)報(bào)錯(cuò),是因?yàn)樵摲椒ㄊ窃谒淖釉蛯?duì)象中定義,所以無法找到該函數(shù)。但是我相信很多人看到這里還是會(huì)一頭霧水,到底鏈在哪里了?誰和誰鏈在一起了?我用一張圖來讓大家更好的理解這個(gè)。
咋眼一看,這張圖信息量不少,但是理解起來卻一點(diǎn)都不難。我們先從Animal看起,Animal中存在一個(gè)prototype指向其原型對(duì)象,這一部分應(yīng)該沒什么問題。但是Animal原型對(duì)象中卻存在[[prototype]]指向了Object,實(shí)際上是指向了Object.prototype。這是因?yàn)樗泻瘮?shù)都是從Object繼承而來的,所有函數(shù)都是Object的實(shí)例。這也正是所有的函數(shù)都可以擁有Object方法的原因,如toString()。所以這也是原型鏈的一部分,我們從創(chuàng)建自定義類型開始就已經(jīng)踏入了原型鏈中。
但是這部分我們暫且不管它,我們繼續(xù)往下面看。我們把Animal的實(shí)例當(dāng)做Cat的原型對(duì)象
Cat.prototype = new Animal();
這樣Cat實(shí)例就擁有了其父類型的所有方法與屬性。因?yàn)榇a中尋找一個(gè)方法會(huì)不斷往上找,先在實(shí)例中尋找,如果沒有就在原型對(duì)象中去尋找,假如原型對(duì)象中沒有,就會(huì)往原型對(duì)象的原型對(duì)象中去找,如此遞進(jìn),最終如果找到則返回,找不到則報(bào)錯(cuò)。當(dāng)我們構(gòu)成原型鏈時(shí),會(huì)有一個(gè)對(duì)象原型當(dāng)做其父類型的實(shí)例,這樣便形成一條原型鏈。當(dāng)然,如果現(xiàn)在有不明白 [[prototype]] (__proto__)與prototype的區(qū)別可以去翻看我們第一篇文章,在這就不重復(fù)了。
這樣一來我們便明白了為何cat1中沒有sayName函數(shù)并了解原型鏈如何實(shí)現(xiàn)繼承了。但是我又提出了一個(gè)問題,假如我們把給子類型原型對(duì)象定義方法的位置調(diào)換一下,那么會(huì)發(fā)生什么事呢?
function Animal(){ this.type = "Animal"; } Animal.prototype.say = function(){ console.log(this.type); } function Cat(){ this.vioce = "喵喵喵"; } Cat.prototype.shout = function(){ console.log(this.vioce); } Cat.prototype = new Animal(); let cat1 = new Cat(); cat1.say(); //"Animal" cat1.shuot(); //err,報(bào)錯(cuò)無此函數(shù)
控制臺(tái)中會(huì)毫不留情的告訴你,沒有該方法Uncaught TypeError: cat1.shuot is not a function。這是因?yàn)楫?dāng)你把父類的實(shí)例賦給子類原型對(duì)象時(shí),會(huì)將其替換。那么你之前所定義的方法就會(huì)失效。所以在這里要注意的一點(diǎn)就是:給原型添加方法時(shí)一定要在替換原型語句之后,而且還有一點(diǎn)要注意就是,在用原型鏈實(shí)現(xiàn)繼承的時(shí)候,千萬不可以用字面量形式定義原型方法。不然原型鏈會(huì)斷開。
function Animal(){ this.type = "Animal"; } Animal.prototype.say = function(){ console.log(this.type); } function Cat(){ this.vioce = "喵喵喵"; } Cat.prototype = new Animal(); Cat.prototype = { //這樣會(huì)使上一條語句失效,從而使原型鏈斷開。 shout:function(){ console.log(this.vioce); } }2.原型鏈的問題
接下來我們談?wù)勗玩湹膯栴}。說起原型鏈的問題我們大概可以聯(lián)想到原型對(duì)象的問題:其屬性與方法會(huì)被所有實(shí)例共享,那么在原型鏈中亦是如此。
function Animal(){ this.type = "Animal"; this.color = ["white","black","yellow"]; } Animal.prototype.say = function(){ console.log(this.type); } function Cat(){ this.vioce = "喵喵喵"; } Cat.prototype = new Animal(); Cat.prototype.shout = function(){ console.log(this.vioce); } let cat1 = new Cat(); let cat2 = new Cat(); cat1.say(); //"Animal" cat1.say(); //"Animal" cat1.color.push("pink"); console.log(cat1.color); //["white", "black", "yellow", "pink"] console.log(cat2.color); //["white", "black", "yellow", "pink"]
當(dāng)然,這也好理解不是。倘若孫子教會(huì)了爺爺某件事,那么爺爺會(huì)把他的本領(lǐng)傳個(gè)他的每個(gè)兒子孫子,沒毛病對(duì)吧。但是我們想要的是,孫子自己學(xué)會(huì)某件事,但不想讓其他人學(xué)會(huì)。這樣意思就是每個(gè)實(shí)例擁有各自的屬性,不與其他實(shí)例共享。那么我們就引入了借用構(gòu)造函數(shù)的概念了。
3.借用構(gòu)造函數(shù)借用構(gòu)造函數(shù),簡(jiǎn)單來說就是在子類構(gòu)造函數(shù)里面調(diào)用父類的構(gòu)造函數(shù)。要怎么調(diào)用?可以使用到apply()和call()這些方法來實(shí)現(xiàn)這個(gè)功能。
function Animal(type = "Animal"){ //設(shè)置一個(gè)參數(shù),如果子類不傳入?yún)?shù)則默認(rèn)為"Animal" this.type = type; this.color = ["white","black","yellow"]; } function Cat(type){ Animal.call(this,type); //繼承Animal同時(shí)傳入type,也可以不傳參 } let cat1 = new Cat(); //沒有傳參,type默認(rèn)為"Animal" let cat2 = new Cat("Cat"); //傳入"Cat",type則為"Cat" cat1.color.push("pink"); console.log(cat1.color); //["white", "black", "yellow", "pink"] console.log(cat2.color); //["white", "black", "yellow"] console.log(cat1.type); //"Animal" console.log(cat2.type); //"Cat"
這樣就實(shí)現(xiàn)了實(shí)例屬性不共享的功能,而且我們?cè)谶@個(gè)里面還可以傳入一個(gè)參數(shù),讓其向父類傳參。這是在原型鏈里面無法做到的一個(gè)功能。至于call()與apply()方法,在這暫且不展開,日后另作文章闡明。暫且只需要知道這是改變函數(shù)作用域的就行。
那么,借用構(gòu)造函數(shù)的問題也就是構(gòu)造函數(shù)的問題,方法都定義在構(gòu)造函數(shù)里面了,復(fù)用性就基本涼涼。所以,我們要組合起來使用。屬性使用借用構(gòu)造函數(shù)模式,而方法則使用原型鏈。
4.組合繼承function Animal(){ this.type = "Animal"; this.color = ["white","black","yellow"]; } Animal.prototype.say = function(){ console.log(this.type); } function Cat(){ Animal.call(this); //繼承屬性 this.vioce = "喵喵喵"; } Cat.prototype = new Animal(); //繼承方法 Cat.prototype.shout = function(){ console.log(this.vioce); } let cat1 = new Cat(); let cat2 = new Cat(); cat1.say(); //"Animal" cat1.say(); //"Animal" cat1.color.push("pink"); console.log(cat1.color); //["white", "black", "yellow", "pink"] console.log(cat2.color); //["white", "black", "yellow"]
這一套方法也變成了最常用的繼承方法了。但是其中也是有個(gè)缺陷,就是每次都會(huì)調(diào)用兩次父類的構(gòu)造函數(shù)。從而使得實(shí)例中與原型對(duì)象中創(chuàng)造相同的屬性,不過原型對(duì)象中的值卻毫無意義。那有沒有更完美的方法?有,就是寄生組合式繼承。在這里我就放代碼給大家。
function obj(o){ function F(){} F.prototype = o; return new F(); } function inheritPrototype(sub,super){ let prototype = obj(super.prototype); //相當(dāng)于拷貝了一個(gè)父類對(duì)象 prototype.constructor = sub; 增強(qiáng)對(duì)象 sub.prototype = prototype; 指定對(duì)象 } function Animal(){ this.type = "Animal"; this.color = ["white","black","yellow"]; } Animal.prototype.say = function(){ console.log(this.type); } function Cat(){ Animal.call(this); //繼承屬性 this.vioce = "喵喵喵"; } inheritPrototype(Cat,Animal); Cat.prototype.shout = function(){ console.log(this.vioce); } let cat1 = new Cat(); let cat2 = new Cat(); cat1.say(); //"Animal" cat1.say(); //"Animal" cat1.color.push("pink"); console.log(cat1.color); //["white", "black", "yellow", "pink"] console.log(cat2.color); //["white", "black", "yellow"]
這樣通過一個(gè)巧妙的方法就可以少調(diào)用一次父類的構(gòu)造函數(shù),而且不會(huì)賦予原型對(duì)象中無意義的屬性。這是被認(rèn)為最理想的繼承方法。但是最多人用的還是上面那個(gè)組合式繼承方法。
總結(jié)到這原型鏈的基本概念與用法都已經(jīng)一一講述,我們需要注意的地方就是prototype與__proto__的關(guān)系,重點(diǎn)是分清其中的區(qū)別,了解父類型跟其子類型的關(guān)系,他們之間的聯(lián)系在哪。大概要弄懂的地方,就是要把那兩文章的兩張圖吃透,那么我們就已經(jīng)把原型鏈吃透大半了。
最后倘若大家還有什么不懂的地方,或者博主有什么遺漏的地方,歡迎大家指出交流。如有興趣可以持續(xù)關(guān)注本博主。
原創(chuàng)文章,轉(zhuǎn)載請(qǐng)注明出處
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/94649.html
摘要:但是該方法有個(gè)缺點(diǎn)就是看不出該對(duì)象的類型,于是乎構(gòu)造函數(shù)模式應(yīng)運(yùn)而生。當(dāng)然,如果細(xì)心的朋友應(yīng)該會(huì)發(fā)現(xiàn)函數(shù)名首字母大寫了,這是約定在構(gòu)造函數(shù)時(shí)將首字母大寫。這時(shí)候,聰明的人應(yīng)該都可以想到,將構(gòu)造函數(shù)模式和原型模式組合起來就可以了。 一.什么是js對(duì)象 1.簡(jiǎn)單理解js對(duì)象 在了解原型鏈之前,我們先要弄清楚什么是JavaScript的對(duì)象,JavaScript對(duì)象又由哪些組成。有人說一個(gè)程...
摘要:函數(shù)式編程,一看這個(gè)詞,簡(jiǎn)直就是學(xué)院派的典范。所以這期周刊,我們就重點(diǎn)引入的函數(shù)式編程,淺入淺出,一窺函數(shù)式編程的思想,可能讓你對(duì)編程語言的理解更加融會(huì)貫通一些。但從根本上來說,函數(shù)式編程就是關(guān)于如使用通用的可復(fù)用函數(shù)進(jìn)行組合編程。 showImg(https://segmentfault.com/img/bVGQuc); 函數(shù)式編程(Functional Programming),一...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個(gè)符合規(guī)范并可配合使用的寫一個(gè)符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個(gè)需求:在系統(tǒng)初始化時(shí)通過http獲取一個(gè)第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個(gè)接口,可通過...
摘要:下圖給出一個(gè)簡(jiǎn)單的列表圖什么是哈希和哈希值為理解挖礦的代碼機(jī)制,首先解決幾個(gè)概念。第一個(gè)就是哈希。哈希值為十六進(jìn)制表示的數(shù),且長(zhǎng)度固定。也正是哈希值的這些特點(diǎn),賦予了其加密信息時(shí)更高的安全性。 第四期 挖礦的相關(guān)算法(2) 卡酷少Wechat:13260325501 看過(1)篇,相信你一定對(duì)挖礦的機(jī)制有了一點(diǎn)了解。那么本篇,我們來一起看一下挖礦中涉及的算法。 在本篇文章中,如果在...
摘要:導(dǎo)語網(wǎng)上有很多自稱能實(shí)現(xiàn)移除注釋的正則表達(dá)式,實(shí)際上存在種種缺陷。為了避免正則的記憶功能,都使用了正則字面量進(jìn)行測(cè)試。下面是去除單行注釋的最終代碼。修改方式將刪除注釋的正則改為。無法正確的移除引號(hào)塊。 導(dǎo)語 網(wǎng)上有很多自稱能實(shí)現(xiàn)移除JS注釋的正則表達(dá)式,實(shí)際上存在種種缺陷。這使人多少有些愕然,也不禁疑惑到:真的可以用正則實(shí)現(xiàn)嗎?而本篇文章以使用正則移除JS注釋為目標(biāo),通過實(shí)踐,由淺及深...
閱讀 2810·2021-11-24 09:39
閱讀 2786·2021-09-23 11:45
閱讀 3412·2019-08-30 12:49
閱讀 3362·2019-08-30 11:18
閱讀 1925·2019-08-29 16:42
閱讀 3351·2019-08-29 16:35
閱讀 1330·2019-08-29 11:21
閱讀 1924·2019-08-26 13:49