JavaScript中怎么實(shí)現(xiàn)原型鏈和繼承,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
創(chuàng)新互聯(lián)公司公司2013年成立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目網(wǎng)站設(shè)計、成都網(wǎng)站建設(shè)網(wǎng)站策劃,項目實(shí)施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元申扎做網(wǎng)站,已為上家服務(wù),為申扎各地企業(yè)和個人服務(wù),聯(lián)系電話:18980820575
原型鏈例子(要點(diǎn)寫在注釋里,可以把代碼復(fù)制到瀏覽器里測試,下同)
function foo(){} //通過function foo(){}定義一個函數(shù)對象 foo.prototype.z = 3; //函數(shù)默認(rèn)帶個prototype對象屬性 (typeof foo.prototype;//"object") var obj =new foo(); //我們通過new foo()構(gòu)造器的方式構(gòu)造了一個新的對象 obj.y = 2; //通過賦值添加兩個屬性給obj obj.x = 1; //通過這種方式構(gòu)造對象,對象的原型會指向構(gòu)造函數(shù)的prototype屬性,也就是foo.prototype obj.x; // 1 //當(dāng)訪問obj.x時,發(fā)現(xiàn)obj上有x屬性,所以返回1 obj.y; // 2 //當(dāng)訪問obj.y時,發(fā)現(xiàn)obj上有y屬性,所以返回2 obj.z; // 3 //當(dāng)訪問obj.z時,發(fā)現(xiàn)obj上沒有z屬性,那怎么辦呢?它不會停止查找,它會查找它的原型,也就是foo.prototype,這時找到z了,所以返回3 //我們用字面量創(chuàng)建的對象或者函數(shù)的默認(rèn)prototype對象,實(shí)際上它也是有原型的,它的原型指向Object.prototype,然后Object.prototype也是有原型的,它的原型指向null。 //那這里的Object.prototype有什么作用呢? typeof obj.toString; // ‘function' //我們發(fā)現(xiàn)typeof obj.toString是一個函數(shù),但是不管在對象上還是對象的原型上都沒有toString方法,因?yàn)樵谒玩湹哪┒薾ull之前都有個Object.prototype方法, //而toString正是Object.prototype上面的方法。這也解釋了為什么JS基本上所有對象都有toString方法 'z' in obj; // true //obj.z是從foo.prototype繼承而來的,所以'z' in obj返回了true obj.hasOwnProperty('z'); // false //但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接對象上的,而是對象的原型鏈上面的屬性。(hsaOwnProperty也是Object.prototype上的方法)
剛才我們訪問x,y和z,分別通過原型鏈去查找,我們可以知道:當(dāng)我們訪問對象的某屬性時,而該對象上沒有相應(yīng)屬性時,那么它會通過原型鏈向上查找,一直找到null還沒有話,就會返回undefined。
基于原型的繼承
function Foo(){ this.y = 2; } Foo.prototype.x = 1; var obj3 = new Foo(); //①當(dāng)使用new去調(diào)用的時候,函數(shù)會作為構(gòu)造器去調(diào)用②this會指向一個對象(這里是obj3),而這個對象的原型會指向構(gòu)造器的prototype屬性(這里是Foo.prototype) obj3.y; //2 obj3.x; //1 //可以看到y(tǒng)是對象上的,x是原型鏈上的原型(也就是Foo.prototype上)
prototype屬性與原型
我們再來看看Foo.prototype是什么樣的結(jié)構(gòu),當(dāng)我們用函數(shù)聲明去創(chuàng)建一個空函數(shù)的時候,那么這個函數(shù)就有個prototype屬性,并且它默認(rèn)有兩個屬性,constructor和__proto__,
constructor屬性會指向它本身Foo,__proto__是在chrome中暴露的(不是一個標(biāo)準(zhǔn)屬性,知道就行),那么Foo.prototype的原型會指向Object.prototype。因此Object.prototype上
的一些方法toString,valueOf才會被每個一般的對象所使用。
function Foo(){} typeof Foo.prototype; // "object" Foo.prototype.x = 1; var obj3 = new Foo();
總結(jié)一下:我們這里有個Foo函數(shù),這個函數(shù)有個prototype的對象屬性,它的作用就是當(dāng)使用new Foo()去構(gòu)造實(shí)例的時候,這個構(gòu)造器的prototype屬性會用作new出來的這些對象的原型。
所以我們要搞清楚,prototype和原型是兩回事,prototype是函數(shù)對象上的預(yù)設(shè)屬性,原型通常是構(gòu)造器上的prototype屬性。
實(shí)現(xiàn)一個class繼承另外一個class
function Person(name, age) { this.name = name; //直接調(diào)用的話,this指向全局對象(this知識點(diǎn)整理) this.age = age; //使用new調(diào)用Peoson的話,this會指向原型為Person.prototype的空對象,通過this.name給空對象賦值,***this作為return值 } Person.prototype.hi = function() { //通過Person.prototype.hi創(chuàng)建所有Person實(shí)例共享的方法,(可以參考上節(jié)的左圖:對象的原型會指向構(gòu)造器的prototype屬性,所以想讓obj1,obj2,obj3共享一些方法的話,只需在原型對象上一次性地添加屬性和方法就可以了); console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now.')//這里的this是全局對象 }; Person.prototype.LEGS_NUM = 2; //再設(shè)置一些對Person類的所有實(shí)例共享的數(shù)據(jù) Person.prototype.ARMS_NUM = 2; Person.prototype.walk = function() { console.log(this.name + ' is walking...'); }; function Student(name, age, className) { //每個學(xué)生都屬于人 Person.call(this, name, age); //在Student這個子類里面先調(diào)用一下父類 this.className = className; } //下一步就是我們怎么去把Student的實(shí)例繼承Person.prototype的一些方法 Student.prototype = Object.create(Person.prototype); //Object.create():創(chuàng)建一個空對象,并且這個對象的原型指向它的參數(shù) //這樣子我們可以在訪問Student.prototype的時候可以向上查找到Person.prototype,又可以在不影響Person的情況下,創(chuàng)建自己的方法 Student.prototype.constructor = Student; //保持一致性,不設(shè)置的話constructor會指向Person Student.prototype.hi = function() { //通過Student.prototype.hi這樣子的賦值可以覆蓋我們基類Person.prototype.hi console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now, and from ' + this.className + '.'); } Student.prototype.learn = function(subject) { //同時,我們又有自己的learn方法 console.log(this.name + 'is learning ' + subject + ' at' + this.className + '.'); }; //test var yun = new Student('Yunyun', 22, 'Class 3,Grade 2'); yun.hi(); //Hi,my name is Yunyun,I'm 22 years old now,and from Class 3, Grade 2. console.log(yun.ARMS_NUM); // 2 //我們本身對象是沒有的,對象的原型也就是Student.prototype也沒有,但是我們用了繼承,繼續(xù)向上查找,找到了Person.prototype.ARMS_NUM,所以返回2 yun.walk(); //Yunyun is walking... yun.learn('math'); //Yunyun is learning math at Class 3,Grade 2.
結(jié)合圖我們來倒過來分析一下上面代碼:我們先通過new Student創(chuàng)建了一個Student的實(shí)例yun,yun的原型指向構(gòu)造器的prototype屬性(這里就是Student.prototype), Student.prototype上有hi方法和learn方法,Student.prototype是通過Object.create(Person.prototype)構(gòu)造的,所以這里的Student.prototype是空對象,并且這個對象的原型指向Person.prototype,接著我們在Person.prototype上也設(shè)置了LEGS_NUM,ARMS_NUM屬性以及hi,walk方法。然后我們直接定義了一個Person函數(shù),Person.prototype就是一個預(yù)置的對象,它本身也會有它的原型,它的原型就是Object.prototype,也正是因?yàn)檫@樣,我們隨便一個對象才會有hasOwnProperty,valueOf,toString這樣些公共的函數(shù),這些函數(shù)都是從Object.prototype上來的。這樣子就實(shí)現(xiàn)了基于原型鏈的繼承。 那我們調(diào)用hi,walk,learn方法的時候發(fā)生了什么呢?比如我們調(diào)用hi方法的時候,我們首先看這個對象yun上有沒有hi方法,但是在這個實(shí)例中沒有所以會向上查找,查找到y(tǒng)un的原型也就是Student.protoype上有這hi方法,所以最終調(diào)用的是Student.prototype.hi,調(diào)用其他方法也是類似的。
改變prototype
我們知道JavaScript中的prototype原型不像Java中的class,Java中的class一旦寫好就很難動態(tài)的去改變了,但是JavaScript中的原型實(shí)際上也是普通的對象,那就意味著在程序運(yùn)行的階段,我們也可以動態(tài)的給prototype添加或刪除些屬性。
在上述代碼的基礎(chǔ)上,我們已經(jīng)有yun這個實(shí)例了,我們接著來進(jìn)行實(shí)驗(yàn):
tudent.prototype.x = 101; //通過Student.prototype.x把yun的原型動態(tài)地添加一個屬性x yun.x; //101 //那我們發(fā)現(xiàn)所有的實(shí)例都會受到影響 //接著我們做個有趣的實(shí)驗(yàn) Student.prototype = {y:2}; //我們直接修改構(gòu)造器的prototype屬性,把它賦值為一個新的對象 yun.y; //undefined yun.x; //101 //所以我們得出:當(dāng)我們修改Student.prototype值的時候,并不能修改已經(jīng)實(shí)例化的對象 var Tom = new Student('Tom',3,'Class LOL KengB'); Tom.x; //undefined //但當(dāng)我們創(chuàng)建一個新的實(shí)例時,這一次x就不見了, Tom.y; //2 //并且y是新的值
所以說當(dāng)動態(tài)修改prototype的時候,是會影響所有已創(chuàng)建或新創(chuàng)建的實(shí)例的,但是修改整個prototype賦值為新的對象的話,對已創(chuàng)建的實(shí)例是不會影響的,但是會影響后續(xù)的實(shí)例。
實(shí)現(xiàn)繼承的方式
實(shí)現(xiàn)繼承有多種方式,下面我們還是以Person和Student來分析
function Person() {
}
function Student() {
}
Student.prototype = Person.prototype; // 我們可不可用這種方式呢?這種方法是錯誤的:因?yàn)樽宇怱tudent有自己的一些方法
//,如果通過這樣子賦值,改變Student的同時也改變了Person。
Student.prototype = new Person(); //這種方式是可以實(shí)現(xiàn)的,但是調(diào)用構(gòu)造函數(shù)有時候也是有問題的,比如要傳進(jìn)Person一個name和age
//,這里的Student是個類,還沒實(shí)例化,這時候有些奇怪了,傳什么都不是。
Student.prototype = Object.create(Person.prototype); //相對來說這中方式是比較理想的,這里我們創(chuàng)建了一個空的對象
//,并且對象的原型指向Person.prototype,這樣我們既保證了繼承了Person.prototype上的方法,并且Student.prototype又有自己空的對象。
//但是Object.create是ES5以后才有的
看完上述內(nèi)容,你們掌握J(rèn)avaScript中怎么實(shí)現(xiàn)原型鏈和繼承的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
名稱欄目:JavaScript中怎么實(shí)現(xiàn)原型鏈和繼承
本文地址:http://www.ekvhdxd.cn/article24/pjeice.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、微信小程序、網(wǎng)站設(shè)計公司、網(wǎng)頁設(shè)計公司、微信公眾號、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)