免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
深入淺出JavaScript之原型鏈&繼承

Javascript語(yǔ)言的繼承機(jī)制,它沒有'子類'和'父類'的概念,也沒有'類'(class)和'實(shí)例'(instance)的區(qū)分,全靠一種很奇特的'原型鏈'(prototype chain)模式,來(lái)實(shí)現(xiàn)繼承。

這部分知識(shí)也是JavaScript里的核心重點(diǎn)之一,同時(shí)也是一個(gè)難點(diǎn)。我把學(xué)習(xí)筆記整理了一下,方便大家學(xué)習(xí),同時(shí)自己也加深印象。這部分代碼的細(xì)節(jié)很多,需要反復(fù)推敲。那我們就開始吧。

小試身手

原型鏈例子(要點(diǎn)寫在注釋里,可以把代碼復(fù)制到瀏覽器里測(cè)試,下同)

function foo{} //通過(guò)function foo{}定義一個(gè)函數(shù)對(duì)象foo.prototype.z = 3; //函數(shù)默認(rèn)帶個(gè)prototype對(duì)象屬性 (

typeof foo.prototype;//'object'

)var obj =new foo; //我們通過(guò)new foo構(gòu)造器的方式構(gòu)造了一個(gè)新的對(duì)象obj.y = 2; //通過(guò)賦值添加兩個(gè)屬性給objobj.x = 1; //

通過(guò)這種方式構(gòu)造對(duì)象,對(duì)象的原型會(huì)指向構(gòu)造函數(shù)的prototype屬性,也就是foo.prototype

obj.x; // 1 //當(dāng)訪問obj.x時(shí),發(fā)現(xiàn)obj上有x屬性,所以返回1obj.y; // 2 //當(dāng)訪問obj.y時(shí),發(fā)現(xiàn)obj上有y屬性,所以返回2obj.z; // 3 //當(dāng)訪問obj.z時(shí),發(fā)現(xiàn)obj上沒有z屬性,那怎么辦呢?它不會(huì)停止查找,它會(huì)查找它的原型,也就是foo.prototype,這時(shí)找到z了,所以返回3
//我們用字面量創(chuàng)建的對(duì)象或者函數(shù)的默認(rèn)prototype對(duì)象,實(shí)際上它也是有原型的,它的原型指向

Object.prototype

,然后Object.prototype也是有原型的,它的原型指向

null

。
//那這里的Object.prototype有什么作用呢?
typeof obj.toString; // ‘function'

//我們發(fā)現(xiàn)typeof obj.toString是一個(gè)函數(shù),但是不管在對(duì)象上還是對(duì)象的原型上都沒有toString方法,因?yàn)樵谒玩湹哪┒薾ull之前都有個(gè)Object.prototype方法,
//而toString正是Object.prototype上面的方法。這也解釋了為什么JS基本上所有對(duì)象都有toString方法
'z' in obj; // true //obj.z是從foo.prototype繼承而來(lái)的,所以'z' in obj返回了true
obj.hasOwnProperty('z'); // false //但是obj.hasOwnProperty('z')返回了false,表示z不是obj直接對(duì)象上的,而是對(duì)象的原型鏈上面的屬性。(hsaOwnProperty也是Object.prototype上的方法)

剛才我們?cè)L問x,y和z,分別通過(guò)原型鏈去查找,我們可以知道:當(dāng)我們?cè)L問對(duì)象的某屬性時(shí),而該對(duì)象上沒有相應(yīng)屬性時(shí),那么它會(huì)通過(guò)原型鏈向上查找,一直找到null還沒有話,就會(huì)返回undefined。

基于原型的繼承

function Foo{ this.y = 2; }Foo.prototype.x = 1;var obj3 = new Foo; //

①當(dāng)使用new去調(diào)用的時(shí)候,函數(shù)會(huì)作為構(gòu)造器去調(diào)用

②this會(huì)指向一個(gè)對(duì)象(這里是obj3),而這個(gè)對(duì)象的原型會(huì)指向構(gòu)造器的prototype屬性(這里是Foo.prototype)

obj3.y; //2
obj3.x; //1 //可以看到y(tǒng)是對(duì)象上的,x是原型鏈上的原型(也就是Foo.prototype上)
prototype屬性與原型

我們?cè)賮?lái)看看Foo.prototype是什么樣的結(jié)構(gòu),當(dāng)我們用函數(shù)聲明去創(chuàng)建一個(gè)空函數(shù)的時(shí)候,那么這個(gè)函數(shù)就有個(gè)prototype屬性,并且它默認(rèn)有兩個(gè)屬性,constructor和__proto__,

constructor屬性會(huì)指向它本身Foo,__proto__是在chrome中暴露的(不是一個(gè)標(biāo)準(zhǔn)屬性,知道就行),那么Foo.prototype的原型會(huì)指向Object.prototype。因此Object.prototype上

的一些方法toString,valueOf才會(huì)被每個(gè)一般的對(duì)象所使用。

function Foo{}typeof Foo.prototype; // 'object'Foo.prototype.x = 1;var obj3 = new Foo;

總結(jié)一下:我們這里有個(gè)Foo函數(shù),這個(gè)函數(shù)有個(gè)prototype的對(duì)象屬性,它的作用就是當(dāng)使用new Foo去構(gòu)造實(shí)例的時(shí)候,這個(gè)構(gòu)造器的prototype屬性會(huì)用作new出來(lái)的這些對(duì)象的原型。

所以我們要搞清楚,prototype和原型是兩回事,prototype是函數(shù)對(duì)象上的預(yù)設(shè)屬性,原型通常是構(gòu)造器上的prototype屬性。

實(shí)現(xiàn)一個(gè)class繼承另外一個(gè)class
function Person(name, age) { this.name = name; //直接調(diào)用的話,this指向全局對(duì)象(this知識(shí)點(diǎn)整理) this.age = age; //

使用new調(diào)用Peoson的話,this會(huì)指向原型為Person.prototype的空對(duì)象,通過(guò)this.name給空對(duì)象賦值,最后this作為return值

}Person.prototype.hi = function {

//通過(guò)Person.prototype.hi創(chuàng)建所有Person實(shí)例共享的方法,(可以參考上節(jié)的左圖:對(duì)象的原型會(huì)指向構(gòu)造器的prototype屬性,所以想讓obj1,obj2,obj3共享一些方法的話,只需在原型對(duì)象上一次性地添加屬性和方法就可以了);

console.log('Hi, my name is ' + this.name + ',I am ' + this.age + ' years old now.')//這里的this是全局對(duì)象};Person.prototype.LEGS_NUM = 2; //再設(shè)置一些對(duì)Person類的所有實(shí)例共享的數(shù)據(jù)Person.prototype.ARMS_NUM = 2;Person.prototype.walk = function { console.log(this.name + ' is walking...');};function Student(name, age, className) { //每個(gè)學(xué)生都屬于人 Person.call(this, name, age); //在Student這個(gè)子類里面先調(diào)用一下父類 this.className = className;}
//下一步就是我們?cè)趺慈グ裇tudent的實(shí)例繼承Person.prototype的一些方法

Student.prototype = Object.create(Person.prototype); //Object.create:創(chuàng)建一個(gè)空對(duì)象,并且這個(gè)對(duì)象的原型指向它的參數(shù) //這樣子我們可以在訪問Student.prototype的時(shí)候可以向上查找到Person.prototype,又可以在不影響Person的情況下,創(chuàng)建自己的方法

Student.prototype.constructor = Student; //保持一致性,不設(shè)置的話constructor會(huì)指向PersonStudent.prototype.hi = function { //通過(guò)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) { //同時(shí),我們又有自己的learn方法 console.log(this.name + 'is learning ' + subject + ' at' + this.className + '.');};//testvar 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 //我們本身對(duì)象是沒有的,對(duì)象的原型也就是Student.prototype也沒有,但是我們用了繼承,繼續(xù)向上查找,找到了Person.prototype.ARMS_NUM,所以返回2yun.walk; //Yunyun is walking...yun.learn('math'); //Yunyun is learning math at Class 3,Grade 2.

結(jié)合圖我們來(lái)倒過(guò)來(lái)分析一下上面代碼:我們先通過(guò)new Student創(chuàng)建了一個(gè)Student的實(shí)例yun,yun的原型指向構(gòu)造器的prototype屬性(這里就是Student.prototype), Student.prototype上有hi方法和learn方法,Student.prototype是通過(guò)Object.create(Person.prototype)構(gòu)造的,所以這里的Student.prototype是空對(duì)象,并且這個(gè)對(duì)象的原型指向Person.prototype,接著我們?cè)赑erson.prototype上也設(shè)置了LEGS_NUM,ARMS_NUM屬性以及hi,walk方法。然后我們直接定義了一個(gè)Person函數(shù),Person.prototype就是一個(gè)預(yù)置的對(duì)象,它本身也會(huì)有它的原型,它的原型就是Object.prototype,也正是因?yàn)檫@樣,我們隨便一個(gè)對(duì)象才會(huì)有hasOwnProperty,valueOf,toString這樣些公共的函數(shù),這些函數(shù)都是從Object.prototype上來(lái)的。這樣子就實(shí)現(xiàn)了基于原型鏈的繼承。 那我們調(diào)用hi,walk,learn方法的時(shí)候發(fā)生了什么呢?比如我們調(diào)用hi方法的時(shí)候,我們首先看這個(gè)對(duì)象yun上有沒有hi方法,但是在這個(gè)實(shí)例中沒有所以會(huì)向上查找,查找到y(tǒng)un的原型也就是Student.protoype上有這hi方法,所以最終調(diào)用的是Student.prototype.hi,調(diào)用其他方法也是類似的。

改變prototype

我們知道JavaScript中的prototype原型不像Java中的class,Java中的class一旦寫好就很難動(dòng)態(tài)的去改變了,但是JavaScript中的原型實(shí)際上也是普通的對(duì)象,那就意味著在程序運(yùn)行的階段,我們也可以動(dòng)態(tài)的給prototype添加或刪除些屬性。

在上述代碼的基礎(chǔ)上,我們已經(jīng)有yun這個(gè)實(shí)例了,我們接著來(lái)進(jìn)行實(shí)驗(yàn):

Student.prototype.x = 101; //通過(guò)Student.prototype.x把yun的原型動(dòng)態(tài)地添加一個(gè)屬性xyun.x; //101 //那我們發(fā)現(xiàn)所有的實(shí)例都會(huì)受到影響//接著我們做個(gè)有趣的實(shí)驗(yàn)Student.prototype = {y:2}; //我們直接修改構(gòu)造器的prototype屬性,把它賦值為一個(gè)新的對(duì)象yun.y; //undefined yun.x; //101 //所以我們得出:當(dāng)我們

修改Student.prototype值的時(shí)候,并不能修改已經(jīng)實(shí)例化的對(duì)象

var Tom = new Student('Tom',3,'Class LOL KengB');
Tom.x; //undefined //但當(dāng)我們創(chuàng)建一個(gè)新的實(shí)例時(shí),這一次x就不見了,
Tom.y; //2 //并且y是新的值

所以說(shuō)當(dāng)動(dòng)態(tài)修改prototype的時(shí)候,是會(huì)影響所有已創(chuàng)建或新創(chuàng)建的實(shí)例的,但是修改整個(gè)prototype賦值為新的對(duì)象的話,對(duì)已創(chuàng)建的實(shí)例是不會(huì)影響的,但是會(huì)影響后續(xù)的實(shí)例。

實(shí)現(xiàn)繼承的方式

實(shí)現(xiàn)繼承有多種方式,下面我們還是以Person和Student來(lái)分析

function Person {}function Student {}Student.prototype = Person.prototype; // 我們可不可用這種方式呢?這種方法是錯(cuò)誤的:因?yàn)樽宇怱tudent有自己的一些方法
//,如果通過(guò)這樣子賦值,改變Student的同時(shí)也改變了Person。Student.prototype = new Person; //這種方式是可以實(shí)現(xiàn)的,但是調(diào)用構(gòu)造函數(shù)有時(shí)候也是有問題的,比如要傳進(jìn)Person一個(gè)name和age
//,這里的Student是個(gè)類,還沒實(shí)例化,這時(shí)候有些奇怪了,傳什么都不是。Student.prototype = Object.create(Person.prototype); //相對(duì)來(lái)說(shuō)這中方式是比較理想的,這里我們創(chuàng)建了一個(gè)空的對(duì)象
//,并且對(duì)象的原型指向Person.prototype,這樣我們既保證了繼承了Person.prototype上的方法,并且Student.prototype又有自己空的對(duì)象。
//但是Object.create是ES5以后才有的
總結(jié)

做項(xiàng)目的時(shí)候才發(fā)現(xiàn)這些基礎(chǔ)概念有多么的重要,如果不把它們逐個(gè)落實(shí)了,真的是一不小心就會(huì)掉進(jìn)坑里。后續(xù)會(huì)繼續(xù)對(duì)JavaScript其他部分以及node作相關(guān)總結(jié),歡迎關(guān)注。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
詳解prototype與
ECMAScript:原型鏈 (prototype 與
字節(jié)跳動(dòng)最愛考的web前端面試題:計(jì)算機(jī)網(wǎng)絡(luò)基礎(chǔ)
JavaScript構(gòu)造函數(shù)及原型對(duì)象
js的原型及繼承實(shí)現(xiàn) | 一鍋亂燉
深入理解javascript原型和閉包(6)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服