成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

我來重新學(xué)習(xí)js的面向?qū)ο螅╬art 4)

MAX_zuo / 3505人閱讀

摘要:我是的可以改變函數(shù)的對象的指向拋出異常,沒有這個因?yàn)樽宇惡统惗际菢?gòu)造函數(shù),那么就會有之前說的,構(gòu)造函數(shù)在的時候,里面的方法函數(shù)會重復(fù)創(chuàng)建實(shí)例,導(dǎo)致資源浪費(fèi)。

我來重新學(xué)習(xí)js 的面向?qū)ο螅╬art 4)

續(xù)上一篇,隨著業(yè)務(wù)越來越大,要考慮一些繼承的玩意了,大千世界,各種東西我們要認(rèn)識和甄別是需要靠大智慧去分門別類,生物學(xué)中把動植物按界、門、綱、目、科、屬、種進(jìn)行分類的方法可能是最有代表的實(shí)例之一.........

說人話就是,我們終于要學(xué)習(xí)繼承的知識了,然后用這些知識去解決老板的問題。
一、繼承-原型鏈
繼承是 OOP 開發(fā)中的一個極為重要的概念,而在javascript 里面,實(shí)現(xiàn)繼承的方式主要依靠原型鏈來實(shí)現(xiàn)的。


圖片來自:https://www.lieyuncj.com/p/3087

圖一,一環(huán)扣一環(huán),形成了鏈條,可以適當(dāng)幫助理解原型鏈的概念,原型鏈,換言之就是原型對象構(gòu)成的鏈。


圖片來源于:https://hackernoon.com/unders...

回顧一下,構(gòu)造函數(shù),原型和實(shí)例的關(guān)系:每個構(gòu)造函數(shù)都有一個原型對象,原型對象都包含一個指向構(gòu)造函數(shù)的指針,而實(shí)例都包含一個指向原型對象的內(nèi)部指針,當(dāng)我們將原型對象等于另外一個類型的實(shí)例的時候,就會出現(xiàn)原型對象包含一個指向另外一個原型的指針,例如 dog原型對象 指向了 animal原型對象。

繼續(xù)回到現(xiàn)場,我們做了一些分類,食物下面分了水果分類:

// 定義一個 Food 的構(gòu)造函數(shù)
function Food() {
  this.type = "食物";
}
// 定義了 Food 的原型對象的一個方法 getType
Food.prototype.getType = function() {
  return this.type;
};
// 定義一個 Fruit 的構(gòu)造函數(shù)
function Fruit() {
  this.type = "水果";
}
// 將 Fruit 的原型對象指向 Food 的實(shí)例
Fruit.prototype = new Food();
// 定義 Fruit 的原型對象的一個方法 getType
Fruit.prototype.getType = function() {
  return this.type;
};

var food1 = new Fruit();
console.log(food1.getType()); // 返回 水果

前半段都是一樣的,直至將 Fruit 的原型對象指向 Food 的實(shí)例,于是Fruit原型不僅擁有了 Food 實(shí)例的全部屬性和方法,也擁有了 Food 實(shí)例的原型對象(因?yàn)?Food 實(shí)例里面有 prototype 指向Food Prototype

這種粗暴的直接將父對象的實(shí)例塞進(jìn)去子對象的原型里面的方式,直接促成了Fruit 繼承 Food。

我最喜歡用《javascript 高級程序設(shè)計》第三版的圖來說明,因?yàn)樗嫷谋容^詳細(xì)而且容易看明白(雖然我也是看了十來遍才看懂),借用他的例子和圖來解釋我們的例子:

可以看到現(xiàn)在這里子對象 subtype 的 原型對象是 superType,因?yàn)橐彩侵苯哟直┑娜M(jìn)去的。

如果要看完整的他的原型鏈,可以參看這個圖:

相當(dāng)詳細(xì),這里之所以有 Object 是因?yàn)?javascript 里面一切皆是對象,默認(rèn)的最頂級的原型就是 Object Prototype。(怎么看這個圖,可以翻看之前一集介紹原型的內(nèi)容)

下面需要注意一些原型對象的問題和技巧
1.1 確定原型和實(shí)例的關(guān)系

沒辦法準(zhǔn)確知道是繼承于哪一個,只要是在鏈條里面的,都會被認(rèn)為是繼承過來的。

console.log(food1 instanceof Fruit) // 返回 true
console.log(food1 instanceof Food) // 返回 true
console.log(food1 instanceof Object) // 返回 true

console.log(Fruit.prototype.isPrototypeOf(food1)) // 返回 true
console.log(Food.prototype.isPrototypeOf(food1)) // 返回 true
console.log(Object.prototype.isPrototypeOf(food1)) // 返回 true
這里也跟javascript 的原型搜索機(jī)制有關(guān)系,當(dāng)訪問一個實(shí)例屬性時候,首先會在實(shí)例中搜索該屬性,如果沒有找到該屬性,就會繼續(xù)搜索實(shí)例的原型對象,在通過原型鏈實(shí)現(xiàn)繼承的情況下,搜索過程就會一直沿著原型鏈繼續(xù)向上搜索。

類似下圖:

圖片來源于:http://www.cnblogs.com/keepfo...

1.2 謹(jǐn)慎定義方法

① 給原型添加方法的代碼一定要放在替換原型的語句之后

正確的例子:

// 定義一個 Food 的構(gòu)造函數(shù)
function Food() {
  this.type = "食物";
}
// 定義了 Food 的原型對象的一個方法 getType
Food.prototype.getType = function() {
  return "food 的 getType 方法";
};
// 定義一個 Fruit 的構(gòu)造函數(shù)
function Fruit() {
  this.type = "水果";
}
// 將 Fruit 的原型對象指向 Food 的實(shí)例
Fruit.prototype = new Food();
// 給子類 Fruit 的原型添加一個新方法getSubType
Fruit.prototype.getSubType = function() {
  return "Fruit 的getSubType";
};
// 重寫父類 Food 的方法getType
Food.prototype.getType = function() {
  return false;
};
var food1 = new Fruit();

console.log(food1.getSubType()); // 返回 Fruit 的getSubType
console.log(food1.getType()); // 返回 false

子類 Fruit 重寫父類(超類)的原型對象的方法getType,在調(diào)用的時候會覆蓋屌父類 Food的原型對象的getType方法,直接使用子類Fruit的getType

子類 Fruit 添加一個方法到自己的原型對象里面,也是很正常的,能夠被直接使用。

錯誤的例子:

// 定義一個 Food 的構(gòu)造函數(shù)
function Food() {
  this.type = "食物";
}
// 定義了 Food 的原型對象的一個方法 getType
Food.prototype.getType = function() {
  return "food 的 getType 方法";
};
// 定義一個 Fruit 的構(gòu)造函數(shù)
function Fruit() {
  this.type = "水果";
}
// 給子類 Fruit 的原型添加一個新方法getSubType
Fruit.prototype.getSubType = function() {
  return "Fruit 的getSubType";
};
// 重寫父類 Food 的方法getType
Food.prototype.getType = function() {
  return false;
};
// 將 Fruit 的原型對象指向 Food 的實(shí)例
Fruit.prototype = new Food();

var food1 = new Fruit();

console.log(food1.getSubType()); // 拋出 error 異常
console.log(food1.getType()); // 返回 false

food1.getSubType() 直接拋出異常,提示說方法找不到或者未定義

主要就是因?yàn)樽釉蛯ο蟊惶鎿Q的時候會被完全覆蓋。
1.3 在通過原型鏈實(shí)現(xiàn)繼承時,不能使用對象字面量方法創(chuàng)建原型

主要是因?yàn)閷ο笞置媪糠椒〞貙懺玩?,這個原理在之前章節(jié)說過,這里只是再次提醒。

// 省略。。。
Fruit.prototype = new Food();

Fruit.prototype = { // 被重寫了原型鏈,就不屬于原來的原型鏈范圍了。
//  xxxxxxx
}
// 省略。。。
1.4 原型鏈的問題

原型鏈最大的問題是來自包含引用類型值的原型,這種類型值的原型屬性會被所有實(shí)例共享,導(dǎo)致沒辦法很好隔離,所以之前也是使用構(gòu)造函數(shù)和原型模式組合使用來解決這個問題,但當(dāng)時沒有觸及真正的繼承。

原型鏈另外一個問題是,在創(chuàng)建子類型的實(shí)例時,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù),或者說,是沒辦法在不影響所有對象實(shí)例情況下,給超類型的構(gòu)造函數(shù)傳遞參數(shù)。

基于以上2個問題,導(dǎo)致了實(shí)際環(huán)境中,很少會多帶帶使用原型鏈,會結(jié)合其他方式來使用原型鏈,畢竟 javascript 里,所有的繼承其實(shí)也是以原型鏈為基礎(chǔ)的。
二、繼承-借用構(gòu)造函數(shù)、偽造對象、經(jīng)典繼承


圖片來自:https://www.tvmao.com/drama/K...

鑒于之前原型鏈的問題兩大問題,所以機(jī)智的工程師想出來利用構(gòu)造函數(shù)來搭配使用,這個技術(shù)就叫做借用構(gòu)造函數(shù) constructor stealing(很 low 有沒有?。袝r候叫偽造對象,或者叫經(jīng)典繼承(逼格瞬間飆升到完全看不懂,但覺得很厲害,有木有?。?/p>

核心思想是在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型改造函數(shù)。

單純使用原型鏈繼承的時候:

function Food() {
  this.colors = ["red", "blue"];
}

function Fruit() {}

Fruit.prototype = new Food();

var food1 = new Fruit();
var food2 = new Fruit();
console.log(food1.colors); // 返回 [ "red", "blue" ]
console.log(food2.colors); // 返回 [ "red", "blue" ]
food1.colors.push("yellow");
console.log(food1.colors); // 返回 [ "red", "blue", "yellow" ]
console.log(food2.colors); // 返回 [ "red", "blue", "yellow" ]

使用借用構(gòu)造函數(shù)模式繼承的時候:

function Food() {
  this.colors = ["red", "blue"];
}

function Fruit() {
  Food.call(this); // call 可以改變函數(shù)的this對象的指向
}

var food1 = new Fruit();
console.log(food1.colors); // 返回 [ "red", "blue" ]

food1.colors.push("yellow");
console.log(food1.colors); // 返回 [ "red", "blue", "yellow" ]

var food2 = new Fruit();
console.log(food2.colors); // 返回 [ "red", "blue" ]

可以看到截然不同的兩種效果,后者的實(shí)例的數(shù)組(引用類型的數(shù)據(jù))并沒有跟隨其他實(shí)例變化而變化,是互相獨(dú)立的。

為什么可以這樣呢?

利用了函數(shù)的執(zhí)行環(huán)境上下文,這里的“繼承”的目的只是為了能夠使用超類的屬性和方法(不算是真正的繼承),所以直接將超類的構(gòu)造函數(shù)放到子類的構(gòu)造函數(shù)里面執(zhí)行,從而將他們進(jìn)行合體。

利用了 call(或者 apply 或者 bind 這種函數(shù))改變了構(gòu)造函數(shù)的 this 指向,才得以實(shí)現(xiàn)上面說到的將不同的構(gòu)造函數(shù)放到同一個執(zhí)行環(huán)境中執(zhí)行。

2.1 傳參

下面兩個例子分別說明了,這種繼承方式可以傳參的,并且傳參之后也是可以重寫超類的屬性的。

例子1:

function Food(name) {
  this.name = name;
  this.colors = ["red", "blue"];
}

function Fruit() {
  Food.call(this, "蘋果"); // call 可以改變函數(shù)的this對象的指向
}

var food1 = new Fruit();
console.log(food1.name); // 返回 蘋果

例子2:

function Food(name) { // 參數(shù)
  this.name = name;
  this.colors = ["red", "blue"];
}

function Fruit() {
  Food.call(this, "蘋果"); // call 可以改變函數(shù)的this對象的指向,加上了傳參
  this.place = "非洲"; // 添加屬性
  this.name = "香蕉"; // 重寫超類屬性
}

var food1 = new Fruit();
console.log(food1.name); // 返回 蘋果
console.log(food1.place); // 返回 非洲
2.2 這種方式的問題


圖片來自:https://www.youtube.com/watch...

正如之前所說,這種不是真正的繼承,只是想子類和父類進(jìn)行了強(qiáng)行合體罷了,這種合體方式能夠滿足一般繼承的要求,但是帶了其他問題:

沒辦法使用超類的原型對象里面定義的方法。

function Food() {
  this.colors = ["red", "blue"];
}
Food.prototype.getType = function () {
  console.log("我是 food 的getType");
}
function Fruit() {
  Food.call(this); // call 可以改變函數(shù)的this對象的指向
}

var food1 = new Fruit();
console.log(food1.getType()); // 拋出異常,沒有這個 function

因?yàn)樽宇惡统惗际菢?gòu)造函數(shù),那么就會有之前說的,構(gòu)造函數(shù)在 new 的時候,里面的方法(函數(shù))會重復(fù)創(chuàng)建 function 實(shí)例, 導(dǎo)致資源浪費(fèi)。

function Food() {
  this.colors = ["red", "blue"];
}

function Fruit() {
  Food.call(this); // call 可以改變函數(shù)的this對象的指向
  this.getType = function() {
    console.log("我是 food 的getType");
  };
}

var food1 = new Fruit();
var food2 = new Fruit();

console.log(food1.getType == food2.getType); // 返回 false
鑒于這種問題,在小規(guī)模程序設(shè)計里面還好,但是一旦規(guī)模稍微變得復(fù)雜之后,就沒法控制代碼了,那我們機(jī)智的工程師們還要繼續(xù)想想辦法。
參考內(nèi)容

紅寶書,javascript 高級程序設(shè)計第三版

原文轉(zhuǎn)載:https://www.godblessyuan.com/...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98325.html

相關(guān)文章

  • 我來重新學(xué)習(xí) javascript 面向對象part 1)

    摘要:其實(shí)在之前的工廠模式里面,也存在這個問題,不過工廠模式更徹底,直接完全創(chuàng)建一個新對象,而構(gòu)造函數(shù)模式的話只是方法會被重新創(chuàng)建。 我來重新學(xué)習(xí) javascript 的面向?qū)ο螅╬art 1) 很多job 的描述都說要求精通 javascript 面向?qū)ο缶幊?,但是根?jù)一般的套路,寫精通其實(shí)就是熟練,寫熟練其實(shí)就是一般,寫一般其實(shí)就是懵逼! showImg(https://segment...

    myshell 評論0 收藏0
  • 我來重新學(xué)習(xí)js 面向對象part 5)

    摘要:無限增殖返回蘋果返回香蕉返回返回使用的新語法方法會創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的。是新增的,用來規(guī)范原型式繼承。這里將返回的新對象放到子類的原型對象里面,這樣子類就擁有了父類的原型對象,也就實(shí)現(xiàn)了方法的繼承。 這是最后的最后了,我會順便總結(jié)一下各種繼承方式的學(xué)習(xí)和理解。(老板要求什么的,管他呢) 一、繼承-組合繼承、偽經(jīng)典繼承 showImg(https://seg...

    BicycleWarrior 評論0 收藏0
  • 我來重新學(xué)習(xí) javascript 面向對象part 3)

    摘要:二動態(tài)原型模式動態(tài)原型模式的特點(diǎn)是,在構(gòu)造函數(shù)里面增加判斷處理是否添加原型對象屬性。他依然有一個嚴(yán)重的問題,就是原型對象和實(shí)例和構(gòu)造函數(shù)之間沒辦法關(guān)聯(lián),這樣不適合在有一定規(guī)模復(fù)雜度的程序開發(fā)中使用。 續(xù)上一集內(nèi)容,有一些數(shù)據(jù)不需要共享的時候,但是又想實(shí)現(xiàn)共享數(shù)據(jù)處理,魚與熊掌,都要兼得(老板就是這么霸氣),那么經(jīng)過工程師們的智慧交流,他們發(fā)現(xiàn)現(xiàn)實(shí)并非那么殘酷,還有一些辦法可取的,也就是...

    Elle 評論0 收藏0
  • 我來重新學(xué)習(xí) javascript 面向對象part 2)

    摘要:先來說其實(shí)構(gòu)造函數(shù)也有,原型對象有,實(shí)例有也有,或者更加籠統(tǒng)的說,所有對象都是有的。構(gòu)造函數(shù)的原型對象上的會指向構(gòu)造函數(shù)。由于屬性是可以變更的,所以未必真的指向?qū)ο蟮臉?gòu)造函數(shù),只是一個提示。 續(xù)上一集內(nèi)容,通過構(gòu)造函數(shù)的方式,成功地更新了生產(chǎn)技術(shù),老板笑呵呵,工人少奔波,只是問題總比辦法多,又遇到一個新問題,就是會造成一些資源的重復(fù)和浪費(fèi),那么經(jīng)過工程師們的智慧交流,他們產(chǎn)生了一個新技...

    silvertheo 評論0 收藏0
  • 2017年 最好javascript 書籍

    摘要:請記住,這些書中的一些可能不是最新的,但概念和基礎(chǔ)仍應(yīng)適用。是最好的老師之一。的秘密由部分組成。在你完成這些書后,查看書籍和最好的本土?xí)? 我看過三本,第1本,第二本,第四本。第一本買的的實(shí)體書,其他兩本看的是電子書。第一本是大名鼎鼎老道寫的,書很薄,但是非常經(jīng)典。javascirpt忍者秘籍是jquery的作者寫的,也是非常經(jīng)典。you dont kown js系列也是非常好。看了...

    mingzhong 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<