摘要:因此,請(qǐng)慎重重寫原型繼承說到就不得說繼承,我們通過給上添加屬性和方法,就能使該構(gòu)造函數(shù)所有的實(shí)例對(duì)象擁有屬性和方法。
本人博客:【www.xiabingbao.com】
在本文中,我們講解prototype的內(nèi)容主要由:什么是prototype,prototype與函數(shù)之間的關(guān)系,prototype與實(shí)例對(duì)象之間的關(guān)系,使用proto實(shí)現(xiàn)一個(gè)簡單的繼承。
1. prototype的簡要介紹在javascript中,創(chuàng)建的每個(gè)函數(shù)天生都自帶著一個(gè)prototype屬性。這里我們要強(qiáng)調(diào)的是:這個(gè)prototype屬性是一個(gè)指針,指向一個(gè)對(duì)象,在這里,我們稱指向的這個(gè)看不到但確實(shí)存在的對(duì)象為原型對(duì)象。其實(shí)可以用下面一個(gè)簡單的例子來說明:
var proto = { name : "wenzi", age : 25 } function Person(){ } Person.prototype = proto;
這樣Person.protptype就指向到了proto,如果還有其他的引用(A)也指向到了proto,那么這個(gè)引用和Person.prototype就是相等的:A==Person.prototype;
prototype指向的這個(gè)對(duì)象是真實(shí)存在的,可能很多的同學(xué)對(duì)prototype屬性和原型對(duì)象有些混淆,我們?cè)谶@里把原型對(duì)象叫做大SB。大SB與普通對(duì)象的一個(gè)不同之處就是,他也有一個(gè)天生的屬性:constructor,這個(gè)constructor重新指回到了函數(shù)。不過,既然大SB也是一個(gè)對(duì)象,那么它也是繼承于Object的,擁有Object所有的方法和屬性,比如toString()等。
大SB = { constructor : Person, say : function(){ return "hello world"; } }
那么現(xiàn)在我們就能得到兩個(gè)引用關(guān)系:
大SB = Person.prototype; // 原型對(duì)象 = 函數(shù).prototype;
Person = 大SB.constructor(Person.prototype.constructor); // 函數(shù) = 原型對(duì)象.constructor(函數(shù).prototype.constructor)
從運(yùn)行的代碼中,可以驗(yàn)證我們的觀點(diǎn),Person.prototype的類型是object,Person.prototype.constructor重新指回到了Person。
其實(shí)在實(shí)例對(duì)象中,也存在著一個(gè)屬性指向到大SB中:
var John = new Person(); 大SB = John.__proto__;
即 John.__proto__ 和 Person.prototype 指向的是同一個(gè)引用:John.__proto__==Person.prototype。
2. 對(duì)象實(shí)例和prototype中的屬性在構(gòu)造函數(shù)能設(shè)置屬性和方法,在prototype中也能設(shè)置屬性和方法,那new出的對(duì)象使用的是哪個(gè)呢?我們來看一個(gè)例子:
function Person(){ this.name = "wenzi"; } Person.prototype.name = "bing"; Person.prototype.say = function(){ return "hello world"; } var John = new Person(); alert(John.name); // "wenzi" alert(John.say()); // "hello world"
從運(yùn)行的結(jié)果我們可以看到,John.name輸出的是構(gòu)造函數(shù)中的屬性值"wenzi",John.say()輸出的是"hello world"。這是因?yàn)?,?dāng)讀取某個(gè)對(duì)象的屬性值時(shí),會(huì)首先在實(shí)例對(duì)象中進(jìn)行搜索,若搜索到則直接進(jìn)行返回;若搜索不到則去原型對(duì)象中進(jìn)行尋找。因此在使用John調(diào)用say()方法時(shí)是正確的,不會(huì)報(bào)錯(cuò)。而且是進(jìn)行了兩次的搜索。
雖然say()是掛載在prototype上,John也同樣能直接進(jìn)行訪問。但是,我們不能直接對(duì)John.say進(jìn)行修改從而影響prototype的say()方法,如下:
John.say = function(){ return "this has changed"; } John.say(); // "this has changed" var Tom = new Person(); Tom.say(); // "hello world"
從運(yùn)行John.say()的結(jié)果來看,say()方法確實(shí)發(fā)生了變化。但是,當(dāng)我們?cè)賜ew出一個(gè)新對(duì)象Tom時(shí),Tom.say()返回的還是"hello world",這是為什么呢?
因?yàn)?,?duì)John.say()進(jìn)行修改時(shí),不是修改了prototype上的say(),而是給John這個(gè)實(shí)例對(duì)象添加了一個(gè)say()方法,從而屏蔽了prototype上的say()方法,由上面的尋找順序我們可以知道,若實(shí)例對(duì)象存在這個(gè)方法就直接返回了,不再去原型對(duì)象上尋找。而new出的新對(duì)象Tom在調(diào)用say()方法時(shí),Tom自己是沒有say()方法的,只能去原型對(duì)象上尋找,因此返回的還是"hello world"。所以,對(duì)John.say進(jìn)行修改時(shí),不是修改了prototype上的say()方法,而是給John添加了實(shí)例方法,屏蔽掉了prototype上的方法而已。那如何才能修改prototype上的方法呢,好辦,直接在prototype上修改:
Person.prototype.say = function(){ return "wenzi"s blog"; }
這樣就能修改prototype的say()方法了。
3. 重寫prototype在上面的例子中,我們都是大部分給prototype添加屬性或方法,本來prototype指向的是大SB,若我們給prototype添加屬性或方法時(shí),就是給大SB添加屬性或方法:
Person.prototype.name = "wenzi";
而大SB里其他的屬性和方法是不受影響的,constructor依然指回到Person。但是,若我們這樣寫:
Person.prototype = { name : "wenzi", say : function(){ return "my name is name" } }
就是對(duì)Person的prototype重寫了,讓prototype進(jìn)行了重新的指向,本來Person.prototype指向的是大SB,可是現(xiàn)在卻指向了CTM,而CTM里也沒有constructor屬性指回到Person,若是想重新指回到Person,還得手動(dòng)添加一個(gè)constructor屬性:
Person.prototype = { constructor : Person, // 重新指回到Person name : "wenzi", say : function(){ return "my name is name" } }
這樣就能手動(dòng)構(gòu)造出新的原型對(duì)象了,new出的實(shí)例對(duì)象也能使用這個(gè)prototype上的屬性和方法。
但是這里還存在一個(gè)問題,若之前已經(jīng)有new出的實(shí)例對(duì)象,然后再修改Person.prototype,之前的實(shí)例對(duì)象是無法使用新的原型對(duì)象(CTM)上的屬性和方法的,因?yàn)橹暗膶?shí)例對(duì)象的__proto__指向的依然是大SB。因此,請(qǐng)慎重重寫prototype
4. 原型繼承說到prototype就不得說prototype繼承,我們通過給prototype上添加屬性和方法,就能使該構(gòu)造函數(shù)所有的實(shí)例對(duì)象擁有屬性和方法。我們先來看下面的代碼:
function Father(){ this.name = "father"; this.age = 43; } Father.prototype.job = "Doctor"; Father.prototype.getName = function(){ return this.name; } function Son(){ this.name = "son"; } Son.prototype = new Father(); // Son的prototype指向Father的實(shí)例對(duì)象 var John = new Son(); for(var k in John){ console.log(k+" : "+john[k]); } /* 輸出結(jié)果: name : son age : 43 job : Doctor getName : function (){ return this.name; } */
從上面的例子中可以看到,Son的實(shí)例對(duì)象繼承了Father中所有的屬性和方法。當(dāng)然,若Son的對(duì)象實(shí)例中存在的,還依然保留。不過Son原型中的屬性和方法是會(huì)被徹底覆蓋的。我們來分析一下這是為什么?
Son的prototype指向的是Father的一個(gè)實(shí)例,我們把這拆成兩步:
var father = new Father(); Son.prototype = father;
在實(shí)例father中既有name,age屬性,也有Father.prototype中的屬性和方法,我們對(duì)father循環(huán)看一下:
for(var k in father){ console.log(k, father[k]); } /* name father age 43 job Doctor getName Father.getName() */
由于constructor是不可枚舉的類型,在for~in循環(huán)里是輸出不了constructor的,其實(shí)father是這樣的:
father = { constructor : Father, name : father age : 43 job : Doctor getName : Father.getName() }
因此Son的prototype指向的就是上面father的內(nèi)容。到此,Son的prototype中的constructor的指向也發(fā)生了改變,本來Son.prototype是指向到Son的,現(xiàn)在指向到了Father。即完全重寫了Son的prototype,重新指向了另一個(gè)引用。所以,我們就能知道為什么對(duì)象實(shí)例中的屬性能夠保存,而prototype中的會(huì)徹底被刪除了。
如果想給Son添加原型方法或?qū)傩?,那只能是?b>Son.prototype = new Father();之后進(jìn)行添加:
Son.prototype = new Father(); Son.prototype.getJob = function(){ return this.job; }
這樣就能添加上getJob方法了。
到這里,會(huì)有人問,如果Son.prototype = Father.prototype會(huì)怎么樣呢?我們剛才也說了Father.prototype指向的是大SB,而大SB也是一個(gè)實(shí)例對(duì)象,是Object的一個(gè)實(shí)例,這就相當(dāng)于:
Son.prototype == 大SB; // 指向了大SB
因此,如果是Son.prototype = Father.prototype的話,那么Son只能繼承到Father.prototype上的job和getName(),其他的屬性是獲取不到的。
5. 總結(jié)本文簡單的介紹了一下prototype,讓我們能對(duì)prototype有個(gè)淺顯的認(rèn)識(shí)。當(dāng)然,博主才疏學(xué)淺,文章里會(huì)有很多疏漏和不完善的地方,歡迎大家批評(píng)指正。
本人博客:【www.xiabingbao.com】
歡迎大家關(guān)注我的微信公眾號(hào):wenzichel
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/85740.html
摘要:在這其中我們就逃不開要討論繼承原型對(duì)象構(gòu)造函數(shù)實(shí)例了。想要獲得某一類型的構(gòu)造函數(shù)可以用來獲得,也可以對(duì)該屬性進(jìn)行賦值操作。三上面就提到一點(diǎn)是指構(gòu)造函數(shù)的原型對(duì)象,它是一個(gè)對(duì)象,它是構(gòu)造函數(shù)的屬性。 原型鏈這一個(gè)話題,需要和很多概念一起講,才能串成一個(gè)比較系統(tǒng)的知識(shí)點(diǎn)。在這其中我們就逃不開要討論繼承、原型對(duì)象、構(gòu)造函數(shù)、實(shí)例了。 一、構(gòu)造函數(shù) 構(gòu)造函數(shù)是一類特殊的函數(shù),它的作用是用來生成...
摘要:組合方式實(shí)現(xiàn)繼承原型鏈構(gòu)造函數(shù)喵喵喵汪汪汪與的唯一區(qū)別是多了這一句組合方式實(shí)現(xiàn)了對(duì)構(gòu)造函數(shù)內(nèi)和原型上所有屬性和方法的繼承,并且的實(shí)例對(duì)象之間也不會(huì)相互干擾。 前言 關(guān)于JavaScript繼承相關(guān)的定義和方法網(wǎng)上已經(jīng)有很多解釋啦,本菜鳥就不抄抄寫寫惹人嫌了,本文主要探討三種基本的繼承方式并且給出優(yōu)化方案。 正文 借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承 function Parent1() { ...
摘要:數(shù)組的構(gòu)造函數(shù)是原型鏈的指向與其他除以外的構(gòu)造函數(shù)相同,的也指向頂級(jí)原型對(duì)象,每一個(gè)數(shù)組都是的實(shí)例,都指向。實(shí)例對(duì)象查找構(gòu)造函數(shù)原型對(duì)象的方法一般會(huì)把對(duì)象共有的屬性和方法都放在構(gòu)造函數(shù)的原型對(duì)象上。 showImg(https://segmentfault.com/img/remote/1460000018998704?w=900&h=506); 閱讀原文 概述 在 JavaScr...
摘要:中的和是一門很靈活的語言,尤其是。即然是面向?qū)ο蟮木幊陶Z言,那也是不可或缺的。在中,永遠(yuǎn)指向的是他的調(diào)用者。定義是存在于實(shí)例化后對(duì)象的一個(gè)屬性,并且指向原對(duì)象的屬性。我們?cè)跀U(kuò)展的時(shí)候,同時(shí)父類也會(huì)有對(duì)應(yīng)的方法,這很顯然是一個(gè)很嚴(yán)重的問題。 javascript中的this和new javascript是一門很靈活的語言,尤其是function。他即可以以面向過程的方式來用,比如: f...
摘要:綁定使用方式進(jìn)行調(diào)用函數(shù)時(shí),會(huì)發(fā)生構(gòu)造函數(shù)的調(diào)用。先上圖,然后根據(jù)文字閱讀使用調(diào)用函數(shù)之后,該函數(shù)才作為構(gòu)造函數(shù)進(jìn)行調(diào)用,構(gòu)造一個(gè)全新的對(duì)象賦值給,而對(duì)象的指向了的對(duì)象,的對(duì)象有一個(gè)屬性指向的構(gòu)造函數(shù)這個(gè)就是的原型鏈,也是的特性。 javascript語言是在運(yùn)行時(shí)前即進(jìn)行編譯的,而this的綁定也是在運(yùn)行時(shí)進(jìn)行綁定的。也就是說,this實(shí)際上是在函數(shù)被調(diào)用時(shí)候發(fā)生綁定的,它指向什么完...
閱讀 3404·2023-04-26 02:09
閱讀 2722·2021-11-24 09:39
閱讀 3372·2021-11-16 11:52
閱讀 3681·2021-10-26 09:50
閱讀 2841·2021-10-08 10:05
閱讀 2523·2021-09-22 15:25
閱讀 3365·2019-08-30 13:14
閱讀 990·2019-08-29 17:06