欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

類和原型的設(shè)計(jì)模式之復(fù)制與委托差異

 更新時(shí)間:2022年07月06日 15:12:18   作者:掘金安東尼  
這篇文章主要為大家介紹了類和原型的設(shè)計(jì)模式之復(fù)制與委托差異詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

小引

JavaScript 技能持有者一定有問(wèn)過(guò)這個(gè)問(wèn)題:

JavaScript 是面向?qū)ο笳Z(yǔ)言嗎?

你期望得到的答案應(yīng)該為:“是” 或 “不是”。

但是可惜,你得不到這樣簡(jiǎn)單的答案!

你大概了解一通之后,你會(huì)被告知:

JavaScript 不是純粹的面向?qū)ο笳Z(yǔ)言!

wtf!為什么是不純粹?能不能純粹一點(diǎn)?!我們喜歡純粹,不喜歡混沌!

......

實(shí)際上,死扣定義真的沒(méi)太必要。定義背后的故事才是最重要的!

看完本篇,你就會(huì)明白這種“混沌”是什么、來(lái)自何處,以及去往何方!!

撰文不易,多多鼓勵(lì)。點(diǎn)贊再看,養(yǎng)成習(xí)慣。??????

“類”設(shè)計(jì)模式

婦孺皆知,面向?qū)ο笕筇匦裕骸痉庋b】、【繼承】、【多態(tài)】。

  • 所謂封裝,即把客觀事物封裝成抽象的類。
  • 所謂繼承,即子類繼承父類的能力。
  • 所謂多態(tài),即子類可以用更特殊的行為重寫(xiě)所繼承父類的通用行為。

其中,“類”的概念最最關(guān)鍵!【類】描述了一種代碼的組織結(jié)構(gòu)形式,它是軟件中對(duì)真實(shí)世界中問(wèn)題領(lǐng)域的建模方法。

舉個(gè)例子:

就好比我們現(xiàn)實(shí)中修房子,你要修一個(gè)“寫(xiě)字樓”、或者一個(gè)“居民樓”、或者一個(gè)“商場(chǎng)”,你就得分別找到修“寫(xiě)字樓”、“居民樓”、“商場(chǎng)”的【設(shè)計(jì)藍(lán)圖】。

但是設(shè)計(jì)藍(lán)圖只是一個(gè)建筑計(jì)劃,并不是真正的建筑。要想在真實(shí)世界實(shí)現(xiàn)這個(gè)建筑,就得由建筑工人將設(shè)計(jì)藍(lán)圖的各類特性(比如長(zhǎng)寬高、功能)【復(fù)制】到現(xiàn)實(shí)世界來(lái)。

這里的【設(shè)計(jì)藍(lán)圖】就是【類】,【復(fù)制】的過(guò)程就是【實(shí)例化】,【實(shí)例】就是【對(duì)象】。

類的內(nèi)部通常有一個(gè)同名的構(gòu)造方法,我們?cè)O(shè)想下,它的偽代碼就可能是這樣的:

class Mall { // “商場(chǎng)”類
    Mall( num ){ // 同名構(gòu)造方法
        garage = num // 地下車庫(kù)數(shù)量
    }
    shop( goods ) { // 買東西
       output( "We can buy: ", goods )
    }
}
// 構(gòu)造函數(shù)大多需要用 new 來(lái)調(diào),這樣語(yǔ)言引擎才知道你想要構(gòu)造一個(gè)新的類實(shí)例。
vanke = new Mall(1) // vanke 有 1 個(gè)地下車庫(kù)
vanke.shop("KFC") // "We can buy: KFC"

java 是典型的面向?qū)ο笳Z(yǔ)言?;?ldquo;類”,我們?cè)偻ㄟ^(guò)以下一段 java 代碼來(lái)看看對(duì)繼承和多態(tài)的理解。

public abstract class Animal{ // 抽象類
     abstract void sound();
}
public class Chicken extends Animal{ // 繼承
    public void sound(){
      sound("咯咯咯");
    }
}
public class Duck extends Animal{
    public void sound(){
      sound("嘎嘎嘎");
    }
}
public static void main(String args[]){
  Aninal chicken = new Chicken();
  Animal duck = new Duck();
  chicken.sound(); //咯咯咯
  duck.sound();  //嘎嘎嘎
}

雞和鴨都屬于動(dòng)物分類,都可以發(fā)出叫聲(繼承),但是它們卻可以發(fā)出不同的叫聲(多態(tài)),很容易理解。

繼承可以使子類獲得父類的全部功能; 多態(tài)可以使程序有良好的擴(kuò)展;

回想下:在 JS 中,我們可能會(huì)怎樣寫(xiě):

var Duck = function () {};
var Chicken = function () {};
var makeSound = function ( animal ) {
    if( animal instanceof Duck){
        console.log("嘎嘎嘎");    
    }else if( animal instanceof Chicken){
        console.log("咯咯咯");    
    }
};
makeSound(new Duck());
makeSound(new Chicken());

這里既沒(méi)用到繼承,也沒(méi)用到多態(tài)。這樣【寫(xiě)判斷】是代碼“不清爽”的罪魁禍?zhǔn)祝?/p>

此處留一個(gè)疑問(wèn),如果不用判斷,還可以怎么寫(xiě)?

在 vue2 中,我們可能會(huì)這么寫(xiě):

export default {
  data() {
      return {
      },
      mounted(){
          this.Chicken()
          this.Duck()
      },
      methods:{
          funtion AnimalSound(sound){
              console.log("叫聲:" + sound)
          },
          funtion Chicken(){
              this.AnimalSound("咯咯咯")
          },
          funtion Duck(){
              this.AnimalSound("嘎嘎嘎")
          }
      }
  }

像這種函數(shù)嵌套調(diào)用是很常見(jiàn)的。沒(méi)有看到繼承,也沒(méi)有看到多態(tài),甚至都沒(méi)有看到最根本的“類”?!

(實(shí)際上,每個(gè)函數(shù)都是一個(gè) Function 對(duì)象。按照最開(kāi)始定義所述,對(duì)象是類的實(shí)例,所以也是能在函數(shù)中看到“類”的?。?/p>

在 JavaScript 中,函數(shù)成了第一等公民! 函數(shù)似乎什么都能做!它可以返回一個(gè)對(duì)象,可以賦值給一個(gè)變量,可以作為數(shù)組項(xiàng),可以作為對(duì)象的一個(gè)屬性......

但這明顯不是“類的設(shè)計(jì)模式”吧!

“類的設(shè)計(jì)模式” 意味著對(duì)【設(shè)計(jì)藍(lán)圖】的【復(fù)制】,在 JS 各種函數(shù)調(diào)用的場(chǎng)景下基本看不到它的痕跡。

“原型”設(shè)計(jì)模式

其實(shí),眾所周知,JS 也是能做到【繼承】和【多態(tài)】的!只不過(guò)它不是通過(guò)類復(fù)制的方式,而是通過(guò)原型鏈委托的方式!

一圖看懂原型鏈?

看不懂?沒(méi)關(guān)系,記住這兩句話再來(lái)看:

  • 一個(gè)對(duì)象的顯示原型的構(gòu)造函數(shù)指向?qū)ο蟊旧恚ê苁煜び袥](méi)有?在本文哪里見(jiàn)過(guò)?)
  • 一個(gè)對(duì)象的隱式原型指向構(gòu)造這個(gè)對(duì)象的函數(shù)的顯示原型。

原來(lái),JS 不是通過(guò)在類里面寫(xiě)同名構(gòu)造函數(shù)的方式來(lái)進(jìn)一步實(shí)現(xiàn)的實(shí)例化,它的構(gòu)造函數(shù)在原型上!這種更加奇特的代碼服用機(jī)制有異于經(jīng)典類的代碼復(fù)用體系。

這里再附一個(gè)經(jīng)典問(wèn)題?JS new 操作會(huì)發(fā)生什么?

會(huì)是像類那樣進(jìn)行復(fù)制嗎?

答案是否定的!

JS 訪問(wèn)一個(gè)對(duì)象的屬性或方法的時(shí)候,先在對(duì)象本身中查找,如果找不到,則到原型中查找,如果還是找不到,則進(jìn)一步在原型的原型中查找,一直到原型鏈的最末端。復(fù)制不是它所做的,這種查找的方式才是!對(duì)象之間的關(guān)系更像是一種委托關(guān)系,就像找東西,你在我這找不到?就到有委托關(guān)系的其它人那里找找看,再找不到,就到委托委托關(guān)系的人那里找......直至盡頭,最后還找不到,指向 null。

所以:JavaScript 和面向?qū)ο蟮恼Z(yǔ)言不同,它并沒(méi)有類來(lái)作為對(duì)象的抽象模式或者設(shè)計(jì)藍(lán)圖。JavaScript 中只有對(duì)象,對(duì)象直接定義自己的行為。對(duì)象之間的關(guān)系是委托關(guān)系,這是一種極其強(qiáng)大的設(shè)計(jì)模式。在你的腦海中對(duì)象并不是按照父類到子類的關(guān)系垂直組織的,而是通過(guò)任意方向的委托關(guān)聯(lián)并排組織的!

不過(guò)你也可以通過(guò)這種委托的關(guān)系來(lái)模擬經(jīng)典的面向?qū)ο篌w系:類、繼承、多態(tài)。但“類”設(shè)計(jì)模式只是一種可選的設(shè)計(jì)模式,你可以模擬,也可以不模擬!

現(xiàn)實(shí)是 ES6 class 給我們模擬了:

class Widget { 
    constructor(width,height) { 
        this.width = width || 50; 
        this.height = height || 50; 
        this.$elem = null; 
    } 
    render($where){ 
        if (this.$elem) { 
            this.$elem.css( { 
                width: this.width + "px", 
                height: this.height + "px" 
            }).appendTo( $where ); 
        } 
    } 
} 
class Button extends Widget { 
    constructor(width,height,label) { 
        super( width, height ); 
        this.label = label || "Default"; 
        this.$elem = $( "<button>" ).text( this.label ); 
    } 
    render($where) { 
        super.render( $where ); 
        this.$elem.click( this.onClick.bind( this ) ); 
    } 
    onClick(evt) { 
        console.log( "Button '" + this.label + "' clicked!" ); 
    } 
}

看起來(lái),非常不錯(cuò),很清晰!

沒(méi)有 .prototype 顯示原型復(fù)雜的寫(xiě)法,也無(wú)需設(shè)置 .proto 隱式原型。還似乎用 extends 、super 實(shí)現(xiàn)了繼承和多態(tài)。

然而,這只是語(yǔ)法糖的陷阱!JS 沒(méi)有類,沒(méi)有復(fù)制,它的機(jī)制是“委托”。

class 并不會(huì)像傳統(tǒng)面向類的語(yǔ)言一樣在申明時(shí)作靜態(tài)復(fù)制的行為,如果你有意或者無(wú)意修改了父類,那子類也會(huì)收到影響。

舉例:

class C { 
 constructor() { 
    this.num = Math.random(); 
 } 
 rand() { 
    console.log( "Random: " + this.num ); 
 } 
} 
var c1 = new C(); 
c1.rand(); // "Random: 0.4324299..."
C.prototype.rand = function() { 
    console.log( "Random: " + Math.round( this.num * 1000 )); 
}; 
var c2 = new C(); 
c2.rand(); // "Random: 867"
c1.rand(); // "Random: 432" ——噢!

ES6 class 混淆了“類設(shè)計(jì)模式”和“原型設(shè)計(jì)模式&rdquo;。它最大的問(wèn)題在于,它的語(yǔ) 法有時(shí)會(huì)讓你認(rèn)為,定義了一個(gè) class 后,它就變成了一個(gè)(未來(lái)會(huì)被實(shí)例化的)東西的 靜態(tài)定義。你會(huì)徹底忽略 Class 是一個(gè)對(duì)象,是一個(gè)具體的可以直接交互的東西。當(dāng)然,它還有其它細(xì)節(jié)問(wèn)題,比如屬性覆蓋方法、super 綁定的問(wèn)題,有興趣自行了解。

總地來(lái)說(shuō),ES6 的 class 想偽裝成一種很好的語(yǔ)法問(wèn)題的解決方案,但是實(shí)際上卻讓問(wèn)題更難解決而且讓 JavaScript 更加難以理解。 —— 《你不知道的 JavaScript》

小結(jié)

  • “類設(shè)計(jì)模式”的構(gòu)造函數(shù)掛在同名的類里面,類的繼承意味著復(fù)制,多態(tài)意味著復(fù)制 + 自定義。
  • “原型設(shè)計(jì)模式”的構(gòu)造函數(shù)掛在原型上,原型的查找是一種自下而上的委托關(guān)系。
  • “類設(shè)計(jì)模式”的類定義之后就不支持修改。
  • “原型設(shè)計(jì)模式”講究的是一種動(dòng)態(tài)性,任何對(duì)象的定義都可以修改,這和 JavaScript 作為腳本語(yǔ)言所需的動(dòng)態(tài)十分契合!

你可以用“原型設(shè)計(jì)模式”來(lái)模擬“類設(shè)計(jì)模式”,但是這大概率是得不償失的。

最后,如果再被問(wèn)道:JavaScript 是面向?qū)ο笳Z(yǔ)言嗎?

如果這篇文章看懂了,就可以圍繞:“類設(shè)計(jì)模式”和“原型設(shè)計(jì)模式”來(lái)吹了。

如果本文沒(méi)有看懂,就把下面的標(biāo)答背下來(lái)吧......

參考

命名函數(shù)表達(dá)式探秘

函數(shù)式和面向?qū)ο缶幊逃惺裁磪^(qū)別?

tutorials/js.mp

JavaScript 輕量級(jí)函數(shù)式編程

你不知道的JavaScript

以上就是類和原型的設(shè)計(jì)模式之復(fù)制與委托差異的詳細(xì)內(nèi)容,更多關(guān)于類原型設(shè)計(jì)模式復(fù)制委托差異的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Array.reduce使用原理示例詳解

    Array.reduce使用原理示例詳解

    這篇文章主要為大家介紹了Array.reduce使用原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 微信小程序 使用canvas制作K線實(shí)例詳解

    微信小程序 使用canvas制作K線實(shí)例詳解

    這篇文章主要介紹了微信小程序 使用canvas制作K線實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下
    2017-01-01
  • requestAnimationFrame使用示例詳解

    requestAnimationFrame使用示例詳解

    這篇文章主要為大家介紹了requestAnimationFrame使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 采用CSS和JS,剛好我最近有個(gè)站點(diǎn)要用到下拉菜單!

    采用CSS和JS,剛好我最近有個(gè)站點(diǎn)要用到下拉菜單!

    采用CSS和JS,剛好我最近有個(gè)站點(diǎn)要用到下拉菜單!...
    2006-06-06
  • 微信小程序 animation API詳解及實(shí)例代碼

    微信小程序 animation API詳解及實(shí)例代碼

    這篇文章主要介紹了 微信小程序 animation API詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • 最新評(píng)論