Javascript(三)之原型繼承理解

進(jìn)階路線

image.png

3 原型繼承

3.1 優(yōu)秀文章

  1. 最詳盡的 JS 原型與原型鏈終極詳解 一
  2. 最詳盡的 JS 原型與原型鏈終極詳解 二
  3. 最詳盡的 JS 原型與原型鏈終極詳解 三
  4. 【THE LAST TIME】一文吃透所有JS原型相關(guān)知識點
  5. 代碼復(fù)用模式

3.2知識點總結(jié)

以下討論都以Person構(gòu)造函數(shù)為例

function Person(name, age, job) {
this.name = name;
this.age = age;
this.job = job;
this.sayName = function() { alert(this.name) } 
}
var person1 = new Person('Zaxlct', 28, 'Software Engineer');
var person2 = new Person('Mick', 23, 'Doctor');

3.2.1 對象和函數(shù)

1典予、萬物皆對象蕾盯!但對象也是有區(qū)別的习瑰。分為普通對象和函數(shù)對象阀坏,Object 茵汰、Function 是 JS 自帶的函數(shù)對象奥此。怎么區(qū)分,其實很簡單朵逝,凡是通過 new Function() 創(chuàng)建的對象都是函數(shù)對象,其他的都是普通對象
2乡范、構(gòu)造函數(shù)的實例都有(constructor)屬性配名,該屬性指向構(gòu)造函數(shù)

person1.constructor == Person

3.2.2 原型對象--protoptye

在規(guī)范里啤咽,prototype 被定義為:給其它對象提供共享屬性的對象。
prototype 自己也是對象渠脉,只是被用以承擔(dān)某個職能罷了.

1宇整、每當(dāng)定義一個對象(函數(shù)也是對象)時候,對象中都會包含一些預(yù)定義的屬性芋膘。其中每個函數(shù)對象都有一個prototype 屬性没陡,這個屬性指向函數(shù)的原型對象∷魃停【只有函數(shù)對象才有 prototype 屬性】
2、原型對象(Person.prototype)是構(gòu)造函數(shù)的一個特殊實例贴彼,所以有一個constructor屬性指向構(gòu)造函數(shù)

Person.prototype.constructor == Person
person1.constructor == Person --對比細(xì)品

3潜腻、原型對象其實就是普通對象,但 Function.prototype 除外器仗,它是函數(shù)對象

function Person(){};
 console.log(Person.prototype) //Person{}
 console.log(typeof Person.prototype) //Object
 console.log(typeof Function.prototype) // Function融涣,這個特殊
 console.log(typeof Object.prototype) // Object
 console.log(typeof Function.prototype.prototype) //undefined

3.2.3 [__ proto __] 指向構(gòu)造函數(shù)的原型對象

1、JS 在創(chuàng)建對象(不論是普通對象還是函數(shù)對象)的時候精钮,都有一個叫做proto 的內(nèi)置屬性威鹿,用于指向創(chuàng)建它的構(gòu)造函數(shù)的原型對象

person1.__proto__ == Person.prototype
image.png

根據(jù)上面的圖片可以得到以下結(jié)論

Person.prototype.constructor == Person;
person1.__proto__ == Person.prototype;
person1.constructor == Person;

3.2.4 構(gòu)造器

1、構(gòu)造函數(shù)(Object)本身就是一個函數(shù)(就是上面說的函數(shù)對象)轨香,它和上面的構(gòu)造函數(shù) Person 差不多忽你,可以創(chuàng)建對象的構(gòu)造器不僅僅有 Object,也可以是 Array臂容,Date科雳,F(xiàn)unction等

ar obj = new Object()
obj.constructor === Object
obj.__proto__ === Object.prototype

var b = new Array();
b.constructor === Array;
b.__proto__ === Array.prototype;

var c = new Date(); 
c.constructor === Date;
c.__proto__ === Date.prototype;

var d = new Function();
d.constructor === Function;
d.__proto__ === Function.prototype;

這些構(gòu)造器都是函數(shù)對象


image.png

3.2.5 原型鏈

1、person1.__proto__ 是什么脓杉?
    答:person1.__proto__ == Person.prototype

2糟秘、Person.__proto__ 是什么?
    答:Person.__proto__ == Function.prototype
       因為 Person.__proto__ === Person的構(gòu)造函數(shù).prototype
       因為 Person的構(gòu)造函數(shù) === Function
       所以 Person.__proto__ === Function.prototype

3球散、Person.prototype.__proto__ 是什么尿赚?
     答:Person.prototype 是一個普通對象凌净,我們無需關(guān)注它有哪些屬性丑婿,只要記住它是一個普通對象约计。
        因為一個普通對象的構(gòu)造函數(shù) === Object
        所以 Person.prototype.__proto__ === Object.prototype

4细卧、Object.__proto__ 是什么蜘犁?
    答:Object.__proto__ == Function.prototype(原因同第二題)

5导披、Object.prototype__proto__ 是什么
    答:Object.prototype 對象也有proto屬性,但它比較特殊滓技,為 null 。因為 null 處于原型鏈的頂端纬朝,這個只能記住判没。
        Object.prototype.__proto__ === null

3.2.6 函數(shù)對象

1嫉沽、所有函數(shù)對象的proto都指向Function.prototype,它是一個空函數(shù)(Empty function)

Number.__proto__ === Function.prototype  // true
Number.constructor == Function //true

Boolean.__proto__ === Function.prototype // true
Boolean.constructor == Function //true

String.__proto__ === Function.prototype  // true
String.constructor == Function //true

// 所有的構(gòu)造器都來自于Function.prototype俏竞,甚至包括根構(gòu)造器Object及Function自身
Object.__proto__ === Function.prototype  // true
Object.constructor == Function // true

// 所有的構(gòu)造器都來自于Function.prototype绸硕,甚至包括根構(gòu)造器Object及Function自身
Function.__proto__ === Function.prototype // true
Function.constructor == Function //true

Array.__proto__ === Function.prototype   // true
Array.constructor == Function //true

RegExp.__proto__ === Function.prototype  // true
RegExp.constructor == Function //true

Error.__proto__ === Function.prototype   // true
Error.constructor == Function //true

Date.__proto__ === Function.prototype    // true
Date.constructor == Function //true

2、所有的構(gòu)造器都來自于 Function.prototype魂毁,甚至包括根構(gòu)造器Object及Function自身玻佩。所有構(gòu)造器都繼承了·Function.prototype·的屬性及方法。Function.prototype也是唯一一個typeof XXX.prototype為 function的prototype席楚。其它的構(gòu)造器的prototype都是一個對象

console.log(typeof Function.prototype) // function
console.log(typeof Object.prototype)   // object
console.log(typeof Number.prototype)   // object
console.log(typeof Boolean.prototype)  // object
console.log(typeof String.prototype)   // object
console.log(typeof Array.prototype)    // object
console.log(typeof RegExp.prototype)   // object
console.log(typeof Error.prototype)    // object
console.log(typeof Date.prototype)     // object
console.log(typeof Object.prototype)   // object

3夺蛇、知道了所有構(gòu)造器(含內(nèi)置及自定義)的proto都是Function.prototype,那Function.prototype的proto是誰呢 Object.prototype的proto是誰酣胀?

Function.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null // true

4、原型鏈的形成是真正是靠proto 而非prototype

1)Object.setPropertyOf娶聘,給我兩個對象闻镶,我把其中一個設(shè)置為另一個的原型。

2)Object.create丸升,給我一個對象铆农,它將作為我創(chuàng)建的新對象的原型。

3.2.7 問答

面試官:談?wù)勀銓?JS 原型和原型鏈的理解狡耻?

候選人:JS 原型是指為其它對象提供共享屬性訪問的對象墩剖。在創(chuàng)建對象時,每個對象都包含一個隱式引用指向它的原型對象或者 null夷狰。

原型也是對象岭皂,因此它也有自己的原型。這樣構(gòu)成一個原型鏈沼头。

面試官:原型鏈有什么作用爷绘?

候選人:在訪問一個對象的屬性時,實際上是在查詢原型鏈进倍。這個對象是原型鏈的第一個元素土至,先檢查它是否包含屬性名,如果包含則返回屬性值猾昆,否則檢查原型鏈上的第二個元素陶因,以此類推。

面試官:那如何實現(xiàn)原型繼承呢垂蜗?

候選人:有兩種方式楷扬。一種是通過 Object.create 或者 Object.setPrototypeOf 顯式繼承另一個對象解幽,將它設(shè)置為原型。

另一種是通過 constructor 構(gòu)造函數(shù)毅否,在使用 new 關(guān)鍵字實例化時亚铁,會自動繼承 constructor 的 prototype 對象,作為實例的原型螟加。

在 ES2015 中提供了 class 的風(fēng)格徘溢,背后跟 constructor 工作方式一樣,寫起來更內(nèi)聚一些捆探。

3.2.8 繼承

繼承的目的是代碼復(fù)用然爆,繼承只是代碼復(fù)用的一種手段或者實現(xiàn)方式

一個在構(gòu)造函數(shù)上常用的規(guī)則是,用于復(fù)用的成員(譯注:屬性和方法)應(yīng)該被添加到原型上黍图。

3.2.8.1 類式繼承1——默認(rèn)模式

子類構(gòu)造函數(shù)的原型對象指向父類構(gòu)造函數(shù)的實例實現(xiàn)繼承

// 定義繼承函數(shù)
function inherit(C, P) {
    C.prototype = new P();
}
//parent構(gòu)造函數(shù)
function Parent(name) {
    this.name = name || 'Adam';
}

//給原型增加方法
Parent.prototype.say = function () {
    return this.name;
};

//空的child構(gòu)造函數(shù)
function Child(name) {}

//繼承
inherit(Child, Parent);

var child1 = new Child("hh");
alert(child1 .hasOwnProperty('name')); // false

缺點

  • 由于子類通過其原型prototype對父類實例化曾雕,繼承了父類,所以說父類中如果共有屬性是引用類型助被,就會在子類中被所有的實例所共享剖张,因此一個子類的實例更改子類原型從父類構(gòu)造函數(shù)中繼承的共有屬性就會直接影響到其他的子類(概括為:子類的實例修改父類的屬性,會影響其他子類的實例的屬性
  • 由于子類實現(xiàn)的繼承是靠其原型prototype對父類進(jìn)行實例化實現(xiàn)的揩环,因此在創(chuàng)建父類的時候搔弄,是無法向父類傳遞參數(shù)的。因而在實例化父類的時候也無法對父類構(gòu)造函數(shù)內(nèi)的屬性進(jìn)行初始化(概括:無法向子類傳遞參數(shù)
3.2.8.2 構(gòu)造函數(shù)繼承

下面這種模式解決了從子對象傳遞參數(shù)到父對象的問題丰滑。它借用了父對象的構(gòu)造函數(shù)顾犹,將子對象綁定到this,同時傳入?yún)?shù).使用這種模式時褒墨,只能繼承在父對象的構(gòu)造函數(shù)中添加到this的屬性炫刷,不能繼承原型上的成員。使用借用構(gòu)造函數(shù)的模式郁妈,子對象通過復(fù)制的方式繼承父對象的成員浑玛,而不是像類式繼承1中那樣獲得引用。下面的例子展示了這兩者的不同:

function Child(a, c, b, d) {
    Parent.apply(this, arguments);
}

//父構(gòu)造函數(shù)
function Article() {
    this.tags = ['js', 'css'];
}
//StaticPage通過借用構(gòu)造函數(shù)的方式從Article繼承
function StaticPage() {
    Article.call(this);
}
var page = new StaticPage();
alert(page.hasOwnProperty('tags')); // true

利用借用構(gòu)造函數(shù)模式實現(xiàn)多繼承

function Cat() {
    this.legs = 4;
    this.say = function () {
        return "meaowww";
    }
}

function Bird() {
    this.wings = 2;
    this.fly = true;
}

function CatWings() {
    Cat.apply(this);
    Bird.apply(this);
}

var jane = new CatWings();
console.dir(jane);

優(yōu)缺點

  • 由于這種類型的繼承沒有涉及到原型prototype噩咪,所以父類的原型方法自然不會被子類繼承锄奢,違背了代碼復(fù)用的原則。
  • 這種模式的一個好處是獲得了父對象自己成員的拷貝剧腻,不存在子對象意外改寫父對象屬性的風(fēng)險拘央。
3.2.8.3 組合式繼承

綜合以上兩種模式,首先借用父對象的構(gòu)造函數(shù)书在,然后將子對象的原型設(shè)置為父對象的一個新實例灰伟。這樣做的好處是子對象獲得了父對象自己的成員,也獲得了父對象中可復(fù)用的(在原型中實現(xiàn)的)方法。子對象也可以傳遞任何參數(shù)給父構(gòu)造函數(shù)栏账。這種行為可能是最接近Java的帖族,子對象繼承了父對象的所有東西,同時可以安全地修改自己的屬性而不用擔(dān)心修改到父對象

function Child(a, c, b, d) {
    Parent.apply(this, arguments);
}
Child.prototype = new Parent();

缺點

  • 一個弊端是父構(gòu)造函數(shù)被調(diào)用了兩次挡爵,所以不是很高效竖般。最后,(父對象)自己的屬性(比如這個例子中的name)也被繼承了兩次茶鹃。

測試

//父構(gòu)造函數(shù)
function Parent(name) {
    this.name = name || 'Adam';
}

//在原型上添加方法
Parent.prototype.say = function () {
    return this.name;
};

//子構(gòu)造函數(shù)
function Child(name) {
    Parent.apply(this, arguments);
}
Child.prototype = new Parent();

var kid = new Child("Patrick");
kid.name; // "Patrick"
kid.say(); // "Patrick"
delete kid.name;
kid.say(); // "Adam"
.3.2.8.4 原型式繼承(共享原型)

F()函數(shù)是一個空函數(shù)涣雕,它充當(dāng)了子對象和父對象的代理。F()的prototype屬性指向父對象的原型闭翩。子對象的原型是一這個空函數(shù)的一個實例

function inherit(C, P) {
    var F = function () {};
    F.prototype = P.prototype;
    C.prototype = new F();
}

這種模式通常情況下都是一種很棒的選擇挣郭,因為原型本來就是存放復(fù)用成員的地方。在這種模式中疗韵,父構(gòu)造函數(shù)添加到this中的任何成員都不會被繼承兑障。
我們來創(chuàng)建一個子對象并且檢查一下它的行為:

var kid = new Child();

如果你訪問kid.name將得到undefined。在這個例子中蕉汪,name是父對象自己的屬性流译,而在繼承的過程中我們并沒有調(diào)用new Parent(),所以這個屬性并沒有被創(chuàng)建者疤。當(dāng)訪問kid.say()時福澡,它在3號對象中不可用,所以在原型鏈中查找宛渐,4號對象也沒有,但是1號對象有眯搭,它在內(nèi)在中的位置會被所有從Parent()創(chuàng)建的構(gòu)造函數(shù)和子對象所共享窥翩。

存儲父類(Superclass)
在上一種模式的基礎(chǔ)上,還可以添加一個指向原始父對象的引用鳞仙。這很像其它語言中訪問超類(superclass)的情況寇蚊,有時候很方便。

function inherit(C, P) {
    var F = function () {};
    F.prototype = P.prototype;
    C.prototype = new F();
    C.uber = P.prototype;
}

重置構(gòu)造函數(shù)引用
這個近乎完美的模式上還需要做的最后一件事情就是重置構(gòu)造函數(shù)(constructor)的指向,如果不重置構(gòu)造函數(shù)的指向棍好,那所有的子對象都會認(rèn)為Parent()是它們的構(gòu)造函數(shù)仗岸,而這個結(jié)果完全沒有用

// parent, child, inheritance
function Parent() {}
function Child() {}
inherit(Child, Parent);

// testing the waters
var kid = new Child();
kid.constructor.name; // "Parent"
kid.constructor === Parent; // true

原型繼承的最終實現(xiàn)

function inherit(C, P) {
    var F = function () {};
    F.prototype = P.prototype;
    C.prototype = new F();
    C.uber = P.prototype;
    C.prototype.constructor = C;
}
3.2.8.5 現(xiàn)代繼承之原型繼承

讓我們從一個叫作“原型繼承”的模式來討論沒有類的現(xiàn)代繼承模式。在這種模式中借笙,沒有任何類進(jìn)來扒怖,在這里,一個對象繼承自另外一個對象业稼。你可以這樣理解它:你有一個想復(fù)用的對象盗痒,然后你想創(chuàng)建第二個對象,并且獲得第一個對象的功能低散。下面是這種模式的用法

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}  
//需要繼承的對象
var parent = {
    name: "Papa"
};

//新對象
var child = object(parent);

//測試
alert(child.name); // "Papa"

在ECMAScript 5中俯邓,原型繼承已經(jīng)正式成為語言的一部分骡楼。這種模式使用Object.create方法來實現(xiàn)。換句話說稽鞭,你不再需要自己去寫類似object()的函數(shù)鸟整,它是語言原生的了:

var child = Object.create(parent);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市朦蕴,隨后出現(xiàn)的幾起案子篮条,更是在濱河造成了極大的恐慌,老刑警劉巖梦重,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兑燥,死亡現(xiàn)場離奇詭異,居然都是意外死亡琴拧,警方通過查閱死者的電腦和手機降瞳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚓胸,“玉大人挣饥,你說我怎么就攤上這事∨嫔牛” “怎么了扔枫?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長锹安。 經(jīng)常有香客問我短荐,道長,這世上最難降的妖魔是什么叹哭? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任忍宋,我火速辦了婚禮,結(jié)果婚禮上风罩,老公的妹妹穿的比我還像新娘糠排。我一直安慰自己,他們只是感情好超升,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布入宦。 她就那樣靜靜地躺著,像睡著了一般室琢。 火紅的嫁衣襯著肌膚如雪乾闰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天盈滴,我揣著相機與錄音汹忠,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛宽菜,可吹牛的內(nèi)容都是我干的谣膳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼铅乡,長吁一口氣:“原來是場噩夢啊……” “哼继谚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起阵幸,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤花履,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后挚赊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诡壁,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年荠割,在試婚紗的時候發(fā)現(xiàn)自己被綠了妹卿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡蔑鹦,死狀恐怖夺克,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嚎朽,我是刑警寧澤铺纽,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站哟忍,受9級特大地震影響狡门,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜锅很,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一其馏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粗蔚,春花似錦尝偎、人聲如沸饶火。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肤寝。三九已至当辐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鲤看,已是汗流浹背缘揪。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人找筝。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓蹈垢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親袖裕。 傳聞我的和親對象是個殘疾皇子曹抬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355