摘要:繼承了如上,我們通過方法借調(diào)了超類的構(gòu)造函數(shù),實(shí)際上是在新創(chuàng)建的實(shí)力環(huán)境下調(diào)用了構(gòu)造函數(shù)。組合繼承組合繼承的基本思想將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長(zhǎng)的一種繼承模式。繼承方法在上面這個(gè)例子中,構(gòu)造函數(shù)定義了兩個(gè)屬性和。
在ECMAScript中只支持實(shí)現(xiàn)繼承,而且實(shí)現(xiàn)繼承主要是依靠原型鏈來實(shí)現(xiàn)的。
繼承基本思想:利用原型讓一個(gè)引用類型繼承另一個(gè)引用類型的屬性和方法。
構(gòu)造函數(shù),原型對(duì)象和實(shí)例對(duì)象的關(guān)系:每個(gè)構(gòu)造函數(shù)都有一個(gè)原型對(duì)象,原型對(duì)象包含一個(gè)指向構(gòu)造函數(shù)的指針,實(shí)例對(duì)象包含一個(gè)指向原型對(duì)象的內(nèi)部指針。
假如我們讓原型對(duì)象等于另外一個(gè)構(gòu)造函數(shù)的實(shí)例,那么此時(shí)的原型對(duì)象將包含一個(gè)指向另外一個(gè)原型對(duì)象的指針,且該被指向的原型對(duì)象包含一個(gè)指向另一個(gè)構(gòu)造函數(shù)的指針。假如另一個(gè)原型對(duì)象又是另一個(gè)類型的實(shí)例,那么上述關(guān)系依然成立,如此層層遞進(jìn),就構(gòu)成了實(shí)例與原型的鏈條,這就是原型鏈的基本概念。用代碼描述如下:
function Super() { this.property = true } Super.prototype.getSuperValue = function() { return this.property } function Sub() { this.subproperty = false //繼承Super Sub.prototype = new Super() Sub.prototype.getSubValue = function() { return this.subproperty } var instance = new Sub() console.log(instance.getSuperValue()) //true
如上代碼,很好的描述了利用原型鏈進(jìn)行繼承。Super和Sub兩個(gè)構(gòu)造函數(shù)。每個(gè)類型分別有一個(gè)屬性和一個(gè)方法。Sub繼承了Spuer,而繼承時(shí)通過創(chuàng)建了Super的實(shí)例,并將該實(shí)例賦給Sub.prototype實(shí)現(xiàn)。這實(shí)現(xiàn)的本質(zhì)是重寫了原型對(duì)象。在確立繼承關(guān)系后我們又給Sub.prototype上面加了一個(gè)新方法。注意現(xiàn)在instance.constructor現(xiàn)在指向的值Super,因?yàn)镾ub.prototype中的constructor被重寫了。
我們知道,所有引用類型默認(rèn)都繼承Object, 而這個(gè)繼承也是通過原型鏈實(shí)現(xiàn)的。所有函數(shù)的默認(rèn)原型對(duì)象都是Object的實(shí)例。因此默認(rèn)原型都會(huì)包含一個(gè)指向內(nèi)部的指針,指向Object.prototype。
可以通過兩種方式來確定原型和實(shí)例之間的關(guān)系。第一種是通過instanceof操作符,只要用這個(gè)操作符來測(cè)試實(shí)例與原型鏈中出現(xiàn)過的構(gòu)造函數(shù),結(jié)果就會(huì)返回true。
instance instanceof Object //true instance instanceof Super // true instance instanceof Sub //true
由于原型鏈的關(guān)系,我們可以說instance是Object, Super, Sub中任何一個(gè)類型的實(shí)例。
第二種方式是使用isPrototypeOf()方法。同樣,只要是原型鏈中出現(xiàn)過得原型,都可以說是該原型鏈所派生的實(shí)例的原型。
Object.prototype.isPrototypeOf(instance) //true Super.prototype.isPrototypeOf(instance) //true Sub.prototype.isPrototypeOf(instance) //true
子類型有時(shí)需要重寫超類中的某個(gè)方法,或者需要添加超類中不存在的某個(gè)方法。給原型添加方法的代碼一定要放在替換原型的語(yǔ)句之后.
function Super() { this.property = true } Super.prototype.getSuperValue = function() { return this.property } function Sub() { this.subproperty = false } //繼承Super Sub.prototype = new Super() //添加新方法 Sub.prototype.getSubValue = function() { return this.subproperty } //重寫超類中的方法 Sub.prototype.getSuperValue = function() { return false } var instance = new Sub() console.log(instance.getSuperValue()) //false
還有一點(diǎn)需要注意的是,通過原型鏈繼承的時(shí)候,不能使用對(duì)象字面量的形式創(chuàng)建原型方法。這是因?yàn)闀?huì)重寫原型鏈。
function Super() { this.property = true } Super.prototype.getSuperValue = function() { return this.property } function Sub() { this.subproperty = false } //繼承Super Sub.prototype = new Super() //使用字面量添加新方法,會(huì)導(dǎo)致上一行代碼無效 Sub.prototype = { getSubValue: function() { return this.subproperty } } var instance = new Sub() alert(instance.getSuperValue()) //error
如上,Sub繼承了Super,緊接著又將原型替換成了一個(gè)對(duì)象字面量而導(dǎo)致問題。由于現(xiàn)在的原型包含的是一個(gè)Object的實(shí)例,而非SuperType的實(shí)例,因此我們?cè)O(shè)想的原型鏈已經(jīng)被切斷。
使用原型鏈繼承的主要問題還是來自于包含引用類型值得原型。通過原型來實(shí)現(xiàn)繼承時(shí),原型實(shí)際上會(huì)變成另一個(gè)類型的實(shí)例。于是,原先的實(shí)例屬性也就成了現(xiàn)在的原型屬性了。因?yàn)樵蛯傩陨鲜菍?shí)例共享的,那么就會(huì)出現(xiàn)問題。
function Super() { this.colors = ["red", "blue", "green"] } function Sub() {} //繼承Super Sub.prototype = new Super() var p1 = new Sub() p1.colors.push("black") var p2 = new Sub() console.log(p2.colors) //["red", "blue", "green", "black"]
原型鏈的第二個(gè)問題就是,創(chuàng)建子類型實(shí)例時(shí),不能向超類型的構(gòu)造函數(shù)傳遞參數(shù)。實(shí)際上,應(yīng)該說是沒有辦法在不影響所有對(duì)象實(shí)例的情況下,給超類型的構(gòu)造函數(shù)傳遞參數(shù)。
在解決原型對(duì)象中包含引用類型值所帶來問題得過程中,使用一種叫做借用構(gòu)造函數(shù)的技術(shù)。
基本思想:在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。(函數(shù)只不過是在特定環(huán)境下執(zhí)行代碼的對(duì)象)因此,可以通過使用apply()和call()方法可以在新創(chuàng)建的對(duì)象上執(zhí)行構(gòu)造函數(shù)。
function Super() { this.colors = ["red", "blue", "green"] } function Sub() { //繼承了Super Super.call(this) } var p = new Sub() p.colors.push("black") console.log(p.colors) //"red, blud, green, black" var p2 = new Sub() console.log(p2.colors) //"red, blue, green, black"
如上,我們通過call()方法借調(diào)了超類的構(gòu)造函數(shù),實(shí)際上是在新創(chuàng)建的Sub實(shí)力環(huán)境下調(diào)用了Super構(gòu)造函數(shù)。這樣,就會(huì)在新Sub對(duì)象上執(zhí)行Super()函數(shù)中定義的所有對(duì)象初始化代碼,結(jié)果Sub的每個(gè)實(shí)例都會(huì)有自己的colors屬性的副本。
相對(duì)于原型鏈而言,借用構(gòu)造函數(shù)有一個(gè)很大的優(yōu)勢(shì),可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)。
function Super(name) { this.name = name } function Sub() { Super.call(this, "Nicholas") this.age = 29 } var p = new Sub() console.log(p.name) //"Nicholas" console.log(p.age) // 29
以上代碼中的Super只接受一個(gè)參數(shù)name,該參數(shù)會(huì)直接賦給一個(gè)屬性。在Sub構(gòu)造函數(shù)內(nèi)部調(diào)用Super構(gòu)造函數(shù)時(shí),實(shí)際上是為Sub的實(shí)例設(shè)置了name屬性。為了確保Super構(gòu)造函數(shù)不會(huì)重寫子類型的屬性,可以在調(diào)用超類型構(gòu)造函數(shù)后,再添加應(yīng)該在子類型中定義的屬性。
問題:利用借用構(gòu)造函數(shù)也會(huì)有一些問題,方法都在構(gòu)造函數(shù)中定義,因此函數(shù)服用就無從談起。而且超類型原型對(duì)象中定義的方法,對(duì)子類型而言也是不可見的。
組合繼承的基本思想:將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長(zhǎng)的一種繼承模式。
思路:使用原型鏈實(shí)現(xiàn)對(duì)原型屬性和方法的繼承,通過借用構(gòu)造函數(shù)是實(shí)現(xiàn)對(duì)實(shí)例屬性的繼承。
function Super(name) { this.name = name this.colors = ["red", "blue", "yellow"] } Super.prototype.sayName = function() { console.log(this.name) } function Sub(name, age) { Super.call(this, name) this.age = age } //繼承方法 Sub.prototype = new Super() Sub.prototype.constructor = Sub Sub.prototype.sayAge = function() { console.log(this.age) } var p1 = new Sub("Nicholas", 29) p1.colors.push("black") console.log(p1.colors) //"red, blue, green, black" p1.sayName() //"Nicholas" p1.sayAge() //29 var p2 = new Sub("Greg", 27) console.log(p2.colors) //"red, blue, green" p2.sayName() //"Greg" p2.sayAge() //27
在上面這個(gè)例子中,Super構(gòu)造函數(shù)定義了兩個(gè)屬性:name和colors。Super的原型定義了一個(gè)方法sayName()。Sub構(gòu)造函數(shù)在調(diào)用Super構(gòu)造函數(shù)時(shí)傳入了name參數(shù),緊接著又定義了它自己的屬性age。然后將Super的實(shí)例賦值給Sub的原型對(duì)象,然后又在該新原型上定義了方法sayAge()。這樣Sub構(gòu)造函數(shù)不同的實(shí)例分別擁有自己的屬性,又可以使用相同的方法了。
原型式繼承的想法時(shí)借助原型可以基于已有的對(duì)象創(chuàng)建新對(duì)象,同時(shí)還不必因此創(chuàng)建自定義類型。
function object(o) { function F() {} F.prototype = o return new F() }
如上,在object()函數(shù)內(nèi)部,先創(chuàng)建一個(gè)臨時(shí)性的構(gòu)造函數(shù),然后將傳入的對(duì)象作為這個(gè)構(gòu)造函數(shù)的原型, 最后返回了這個(gè)臨時(shí)類型的一個(gè)新實(shí)例。本質(zhì)上,object()對(duì)傳入其中的對(duì)象執(zhí)行了一次淺復(fù)制。
var person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] } var p1 = object(person) p1.name = "Greg" p1.friends.push("Rob") var p2 = object(person) p2.name = "Linda" p2.friends.push("Barbie") console.log(person.friends) //"Shelby, Court, Van, Rob, Barbie"
以上這種繼承方式要求你必須有一個(gè)對(duì)象作為另一個(gè)對(duì)象的基礎(chǔ)。如果有這么一個(gè)對(duì)象的話,可以把它傳給object()函數(shù),然后再根據(jù)具體需求對(duì)得到的對(duì)象加以修改即可。在上面例子中,person對(duì)象作為基礎(chǔ)對(duì)象,然后傳入到object(),然后該函數(shù)就會(huì)返回一個(gè)新對(duì)象。這個(gè)新對(duì)象將person作為原型,所以它的原型中就包含一個(gè)基本類型值屬性和一個(gè)引用類型值屬性。這意味著person.friends不僅屬于person所有,而且也會(huì)被p1和p2共享。實(shí)際上相當(dāng)于創(chuàng)建了person對(duì)象的兩個(gè)副本。
ECMAScript5通過新增object.create()方法規(guī)范化了原型式繼承。這個(gè)方法接受兩個(gè)參數(shù):一個(gè)用作新對(duì)象原型的對(duì)象和一個(gè)為新對(duì)象定義額外屬性的對(duì)象。在傳入一個(gè)參數(shù)的情況下,Object.create()與object()方法行為相同。
后面還有寄生式繼承和寄生組合繼承....表示看不下去了,前面這幾個(gè)已經(jīng)夠用, 有時(shí)間再學(xué)習(xí)下~
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/89411.html
摘要:構(gòu)造函數(shù)通常首字母大寫,用于區(qū)分普通函數(shù)。這種關(guān)系常被稱為原型鏈,它解釋了為何一個(gè)對(duì)象會(huì)擁有定義在其他對(duì)象中的屬性和方法。中所有的對(duì)象,都有一個(gè)屬性,指向?qū)嵗龑?duì)象的構(gòu)造函數(shù)原型由于是個(gè)非標(biāo)準(zhǔn)屬性,因此只有和兩個(gè)瀏覽器支持,標(biāo)準(zhǔn)方法是。 從這篇文章開始,復(fù)習(xí) MDN 中級(jí)教程 的內(nèi)容了,在初級(jí)教程中,我和大家分享了一些比較簡(jiǎn)單基礎(chǔ)的知識(shí)點(diǎn),并放在我的 【Cute-JavaScript】系...
摘要:本文是重溫基礎(chǔ)系列文章的第十二篇。注意對(duì)象的名稱,對(duì)大小寫敏感。基礎(chǔ)用法第一個(gè)參數(shù)是目標(biāo)對(duì)象,后面參數(shù)都是源對(duì)象。用途遍歷對(duì)象屬性。用途將對(duì)象轉(zhuǎn)為真正的結(jié)構(gòu)。使用場(chǎng)景取出參數(shù)對(duì)象所有可遍歷屬性,拷貝到當(dāng)前對(duì)象中。類似方法合并兩個(gè)對(duì)象。 本文是 重溫基礎(chǔ) 系列文章的第十二篇。 今日感受:需要總結(jié)下2018。 這幾天,重重的感冒發(fā)燒,在家休息好幾天,傷···。 系列目錄: 【復(fù)習(xí)資料...
摘要:本文是重溫基礎(chǔ)系列文章的第四篇。系列目錄復(fù)習(xí)資料資料整理個(gè)人整理重溫基礎(chǔ)語(yǔ)法和數(shù)據(jù)類型重溫基礎(chǔ)流程控制和錯(cuò)誤處理重溫基礎(chǔ)循環(huán)和迭代本章節(jié)復(fù)習(xí)的是中的基礎(chǔ)組件之一,函數(shù),用來復(fù)用特定執(zhí)行邏輯。箭頭函數(shù)不能使用命令,即不能用作函數(shù)。 本文是 重溫基礎(chǔ) 系列文章的第四篇。今日感受:常懷感恩之心,對(duì)人對(duì)己。 系列目錄: 【復(fù)習(xí)資料】ES6/ES7/ES8/ES9資料整理(個(gè)人整理) 【重溫基...
摘要:需要測(cè)試的函數(shù)構(gòu)造函數(shù)即用運(yùn)算符來檢測(cè)是否存在參數(shù)的原型鏈。區(qū)別只能用來判斷對(duì)象函數(shù)和數(shù)組,不能用來判斷字符串和數(shù)字等用于判斷一個(gè)表達(dá)式的原始值,返回一個(gè)字符串。一般返回結(jié)果有函數(shù)數(shù)組,對(duì)象。 最近開始在整理ES6/ES7/ES8/ES9的知識(shí)點(diǎn)(已經(jīng)上傳到 我的博客 上),碰到一些知識(shí)點(diǎn)是自己已經(jīng)忘記(用得少的知識(shí)點(diǎn)),于是也重新復(fù)習(xí)了一遍。 這篇文章要復(fù)習(xí)的 instanc...
摘要:今天把接下來引用類型中的一些內(nèi)容全部記錄完畢基本包裝類型為了便于操作基本類型值,還提供了種特殊的引用類型。這三種類型具有與各自的基本類型響應(yīng)的特殊行為。重寫后的返回對(duì)象表示的數(shù)值類型,另外兩個(gè)方法則返回字符串形式的數(shù)值。 今天把接下來引用類型中的一些內(nèi)容全部記錄完畢~ 基本包裝類型 為了便于操作基本類型值,JavaScript還提供了3種特殊的引用類型:Boolean, Number,...
閱讀 2667·2021-11-23 09:51
閱讀 3255·2021-11-22 14:44
閱讀 4589·2021-11-22 09:34
閱讀 5135·2021-10-08 10:14
閱讀 2459·2021-09-22 15:47
閱讀 3519·2021-09-22 15:40
閱讀 1520·2019-08-30 15:44
閱讀 1630·2019-08-28 18:23