摘要:構(gòu)造函數(shù),實例構(gòu)造函數(shù),是用來創(chuàng)建對象的函數(shù),本質(zhì)上也是函數(shù)。這里剛好解釋一下時,說到的,可以通過實例的訪問構(gòu)造函數(shù),但是本質(zhì)上是原型對象的屬性。
前言
最近在學vue,到周末終于有空寫一些東西了(想想又能騙贊,就有點小激動!)。在javascript基礎中,除了閉包之外,繼承也是一個難點。因為考慮到篇幅較長,所以打算分成兩個部分來寫。同樣基于《javascript高級程序設計》,做一個詳細的講解,如果有不對的地方歡迎指正。
準備知識為了更好的講解繼承,先把一些準備知識放在前面。
1.構(gòu)造函數(shù),實例構(gòu)造函數(shù),是用來創(chuàng)建對象的函數(shù),本質(zhì)上也是函數(shù)。與其他函數(shù)的區(qū)別在于調(diào)用方式不同:
如果通過new操作符來調(diào)用的,就是構(gòu)造函數(shù)
如果沒有通過new操作符來調(diào)用的,就是普通函數(shù)
例子:
function Person(name, age) { this.name = name; this.age = age; } //當做構(gòu)造函數(shù)調(diào)用 var person1 = new Person("Mike",10); //當做普通函數(shù)調(diào)用,這里相當于給window對象添加了name和age屬性,這個不是重點,只要注意調(diào)用方式 Person("Bob",12); console.log(person1)//Person {name: "Mike", age: 10} console.log(name)//Bob console.log(age)//12
在var person1 = new Person("Mike",10);中,通過new操作符調(diào)用了函數(shù)Person,并且生成了person1,
這里的Person就稱為構(gòu)造函數(shù),person1稱為Person函數(shù)對象的一個實例。實可以通過實例的constructor訪問對應的構(gòu)造函數(shù)(但是其實上這個constructor不是實例的屬性,后面會解釋為什么),看下面的例子:
function Person(name, age) { this.name = name; this.age = age; } var person1 = new Person("Mike",10); var person2 = new Person("Alice",20); console.log(person1.constructor)//function Person(){省略內(nèi)容...} console.log(person2.constructor)//function Person(){省略內(nèi)容...}2.原型對象
當我們每次創(chuàng)建一個函數(shù)的時候,函數(shù)對象都會有一個prototype屬性,這個屬性是一個指針,指向它的原型對象。原型對象的本質(zhì)也是一個對象。初次看這句話可能有點難以理解,舉個例子,還是剛剛那個函數(shù):
function Person(name, age) { this.name = name; this.age = age; } console.log(Person.prototype)//object{constructor:Person}
可以看到Person.prototype指向了一個對象,即Person的原型對象,并且這個對象有一個constructor屬性,又指向了Person函數(shù)對象。是不是有點暈?沒關系,接下來我們就上比舉例子更好的手段--畫圖。
3.構(gòu)造函數(shù),原型對象和實例的關系在前面,我們剛剛介紹過了構(gòu)造函數(shù),實例和原型對象,接下來我們用一張圖來表示這三者之間的關系(用ps畫這種圖真是麻煩的要死,大家有好的工具推薦一下):
從圖上我們可以看到:
函數(shù)對象的prototype指向原型對象,原型對象的constructor指向函數(shù)對象
實例對象的[Protoptype]屬性指向原型對象,這里的[Protoptype]是內(nèi)部屬性,可以先理解為它是存在的,但是不允許我們訪問(雖然在有些瀏覽器是允許訪問這個屬性的,但是我們先這樣理解),這個屬性的作用是:允許實例通過該屬性訪問原型對象中的屬性和方法。比如說:
function Person(name, age) { this.name = name; this.age = age; } //在原型對象中添加屬性或者方法 Person.prototype.sex = "男"; var person1 = new Person("Mike",10); var person2 = new Person("Alice",20); //只給person2設置性別 person2.sex = "女"; console.log(person1.sex)//"男" console.log(person2.sex)//"女"
這里我們沒有給person1實例設置sex屬性,但是因為[Protoptype]的存在,會訪問原型對象中對應的屬性;
同時我們給person2設置sex屬性后輸出的是"女",說明只有當實例本身不存在對應的屬性或方法時,才會去找原型對象上的對應屬性或方法
補充一下:ECMA-262第五版的時候這個內(nèi)部屬性叫[Prototype],而_proto_是Firefox,Chrome和Safari瀏覽器提供的一個屬性,在其他的實現(xiàn)里面,這個內(nèi)部屬性是沒法訪問的。所以我們能從控制臺看到的是_proto_屬性,但是我在文中用的還是[Prototype],個人認為這樣較符合它的本質(zhì)。
tips:這里剛好解釋一下console.log(person1.constructor)時,說到的,可以通過實例的constructor訪問構(gòu)造函數(shù),但是constructor本質(zhì)上是原型對象的屬性。
繼承 原型鏈在js中,繼承的主要思路就是利用原型鏈,因此如果理解了原型鏈,繼承問題就理解了一半。在這里可以稍微休息一下,如果對前面的準備知識已經(jīng)理解差不多了,就開始講原型鏈了。
原型鏈的原理是:讓一個引用類型繼承另一個引用類型的屬性和方法。
先回顧一下剛剛講過的知識:
原型對象通過constructor屬性指向構(gòu)造函數(shù)
實例通過[Prototype]屬性指向原型對象
那現(xiàn)在我們來思考一個問題:如果讓原型對象等于另一個構(gòu)造函數(shù)的實例會怎么樣?
例如:
function A() { } //在A的原型上綁定sayA()方法 A.prototype.sayA = function(){ console.log("from A") } function B(){ } //讓B的原型對象指向A的一個實例 B.prototype = new A(); //在B的原型上綁定sayB()方法 B.prototype.sayB = function(){ console.log("from B") } //生成一個B的實例 var a1 = new A(); var b1 = new B(); //b1可以調(diào)用sayB和sayA b1.sayB();//"from B" b1.sayA();//"from A"
為了方便理解剛剛發(fā)生了什么,我們再上一張圖:
現(xiàn)在結(jié)合圖片來看代碼:
首先,我們創(chuàng)建了A和B兩個函數(shù)對象,同時也就生成了它們的原型對象
接著,我們給A的原型對象添加了sayA()方法
* 然后是關鍵性的一步B.prototype = new A();,我們讓函數(shù)對象B的protytype指針指向了一個A的實例,請注意我的描述:是讓函數(shù)對象B的protytype指針指向了一個A的實例,這也是為什么最后,B的原型對象里面不再有constructor屬性,其實B本來有一個真正的原型對象,原本可以通過B.prototype訪問,但是我們現(xiàn)在改寫了這個指針,使它指向了另一個對象,所以B真正的原型對象現(xiàn)在沒法被訪問了,取而代之的這個新的原型對象是A的一個實例,自然就沒有constructor屬性了
接下來我們給這個B.prototype指向的對象,增加一個sayB方法
然后,我們生成了一個實例b1
最后我們調(diào)用了b1的sayB方法,可以執(zhí)行,為什么?
因為b1有[Prototype]屬性可以訪問B prototype里面的方法;
我們調(diào)用了b1的sayA方法,可以執(zhí)行,為什么?
因為b1沿著[Prototype]屬性可以訪問B prototype,B prototype繼續(xù)沿著[Prototype]屬性訪問A prototype,最終在A.prototype上找到了sayA()方法,所以可以執(zhí)行
所以,現(xiàn)在的結(jié)果就相當于,b1繼承了A的屬性和方法,這種由[Prototype]不斷把實例和原型對象聯(lián)系起來的結(jié)構(gòu)就是原型鏈。也是js中,繼承主要的實現(xiàn)方式。
小結(jié)因為這部分知識理解起來比較難,所以第一部分先寫到這里(當然不是因為我想多寫一篇來騙贊和關注啦),大家讀到這里也可以歇口氣了,如果這一塊理解深刻,下一部分就會很輕松。
為了測試一下大家對于本文的理解程度,問一下幾個問題:
在最后一個例子里,console.log(b1.constructor),結(jié)果是什么?
B.prototype = new A();和 B.prototype.sayB = function(){ console.log("from B") }這兩句的執(zhí)行順序能不能交換
最后再思考一下. 在最后一個例子里,A看似已經(jīng)是原型鏈的最頂層,那A還能再往上嗎?
以上答案在下篇中解答,讀者可以自己先試試,思考一下,有疑問也可以在評論中提出。最后,如果這篇文章對你有幫助,請大方的點收藏和推薦吧(每次都是收藏比推薦多!,組織語言,畫圖和排版都很辛苦的),你們的支持會給我更大的動力~以上內(nèi)容屬于個人見解,如果有不同意見,歡迎指出和探討。請尊重作者的版權(quán),轉(zhuǎn)載請注明出處,如作商用,請與作者聯(lián)系,感謝!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/82081.html
摘要:也就是說當使用后,當前執(zhí)行上下文中的對象已被替換為,后續(xù)執(zhí)行將以所持有的狀態(tài)屬性繼續(xù)執(zhí)行。借用的方法替換的實例去調(diào)用相應的方法。實現(xiàn)引用類型的繼承其實沒有類這一概念,我們平時使用的等嚴格來說被稱作引用類型。 call 方法:object.method.call(targetObj[, argv1, argv2, .....]) apply 方法:object.method.apply(...
摘要:當然這還沒完,因為我們還有重要的一步?jīng)]完成,沒錯就是上面的第行代碼,如果沒有這行代碼實例中的指針是指向構(gòu)造函數(shù)的,這樣顯然是不對的,因為正常情況下應該指向它的構(gòu)造函數(shù),因此我們需要手動更改使重新指向?qū)ο蟆? 第一節(jié)內(nèi)容:javaScript原型及原型鏈詳解(二) 第一節(jié)中我們介紹了javascript中的原型和原型鏈,這一節(jié)我們來講利用原型和原型鏈我們可以做些什么。 普通對象的繼承 ...
摘要:理解的函數(shù)基礎要搞好深入淺出原型使用原型模型,雖然這經(jīng)常被當作缺點提及,但是只要善于運用,其實基于原型的繼承模型比傳統(tǒng)的類繼承還要強大。中文指南基本操作指南二繼續(xù)熟悉的幾對方法,包括,,。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。 怎樣使用 this 因為本人屬于偽前端,因此文中只看懂了 8 成左右,希望能夠給大家?guī)韼椭?...(據(jù)說是阿里的前端妹子寫的) this 的值到底...
摘要:可以通過構(gòu)造函數(shù)和原型的方式模擬實現(xiàn)類的功能。原型式繼承與類式繼承類式繼承是在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型的構(gòu)造函數(shù)。寄生式繼承這種繼承方式是把原型式工廠模式結(jié)合起來,目的是為了封裝創(chuàng)建的過程。 js繼承的概念 js里常用的如下兩種繼承方式: 原型鏈繼承(對象間的繼承) 類式繼承(構(gòu)造函數(shù)間的繼承) 由于js不像java那樣是真正面向?qū)ο蟮恼Z言,js是基于對象的,它沒有類的概念。...
閱讀 1713·2021-11-25 09:43
閱讀 2671·2019-08-30 15:53
閱讀 1823·2019-08-30 15:52
閱讀 2905·2019-08-29 13:56
閱讀 3326·2019-08-26 12:12
閱讀 570·2019-08-23 17:58
閱讀 2134·2019-08-23 16:59
閱讀 941·2019-08-23 16:21