首頁  >  文章  >  web前端  >  重學JavaScript 對象

重學JavaScript 對象

coldplay.xixi
coldplay.xixi轉載
2020-10-09 16:41:431709瀏覽

JavaScript專欄為大家介紹JavaScript的對象,重新認識。

重學JavaScript 對象

這裡我們繼續學習兩個比較重要的型別,就是 ObjectSymbol。我們主要講的是 Object,相對 Object 來說 Symbol 只是配角。

關於物件這個概念大家非常早就會接觸到了,其實人大概在 5 歲的時候就會產生物件的抽象。很多時候我們看起來好像物件是我們學程式的時候才知道有物件導向。但是從認知的角度來說,應該是比我們平常對數字中的值這個類型的認知要早的多。所以歷史的角度也一直被評價為,對像是更貼近人類的自然思維的。

剛剛說到我們從小時候就已經產生了物件的概念了,那為什麼說從小就有呢? Object 在英文裡其實它的意思是一個很廣泛的東西,他是任何一個物體,可以是抽象的物體,也可以是一個實際的物體。但在我們中文裡,找不到一個合適的詞,可以代表保羅萬物的詞來表達 Object 的意思。所以在中文我們就直接翻譯成 “對象”。

所以這個中文翻譯過來的詞,就造成了我們對 Object 的某種誤解。因為物件在英文中,我覺得更接近 target 這個字的意思。其實在台灣就會把 Object 翻譯成 「物件」。物件這個詞在語意上確實會更貼合一些,但是物件這個詞大家也不是特別熟悉,所以它就演變成了一個技術的專用名詞。

但是不論如何,我們腦子裡面應該是有這麼一個概念的,從小我們就應該知道我們有三條一模一樣的魚,但是其實他是三個不同的對象。那為什麼一模一樣的魚,他們是不同的對象呢?

重學JavaScript 對象

我們可以這麼理解哈,突然有一天其中一條魚的尾巴被咬掉了。很驚訝的發現,另外兩條魚並不會受到影響。因此,當我們在計算機中描述這三條魚的時候,那肯定是三組相同的數據的對象,但是是單獨儲存了三份,互相獨立的

這種魚和魚之間的差異其實就是,他們的物件的一個特性的體現。一些認知學的研究認為我們人在小時候大概5 歲的時候就有

這樣的認知了,其實現在的孩子發育的比較早,5歲已經是一個最低的年齡了。 2 ~ 3 歲的時候大家都知道這個蘋果跟那個蘋果是不一樣的,這個咬一口,另外一個蘋果安然無事。

所以如果我們在計算機裡面描述這三條魚的時候,我們就必須要把資料單獨儲存三份,因為是三個物件的狀態,而不是我們把同一個數據存了三份,而是恰巧他們是相等而已。其實這個正是所有的物件導向程式設計的一個基礎,也就是說,他是這條魚就是這條魚,不是這條魚就不是這條魚,不會因為物件本身的狀態改變而變得有區別。

所以我們對物件的認知是?

任何一個物件都是唯一的,這與它本身的狀態無關,狀態是由物件決定的

即使狀態完全一致的兩個對象,也不相等。所以有時候我們會把物件當資料用,但這個其實是一種語言的使用技巧而已,並不是把物件當做物件用,例如我們傳一個config,其實傳config 的過程其實它並不是把物件當對象去傳,而是我們把物件當成一種資料載體去傳。這時候就牽涉到我們對物件類型的使用,跟語言本身的設計用途的偏差。

我們用狀態來描述對象,例如我們有一個對象“魚”,然後他的狀態就是,它有沒有“尾巴”、“眼睛多大”,我們都會用這些狀態值來描述一個對象。

我們的狀態的改變既是行為,狀態的改變就是魚的尾巴沒有了,被咬掉了。然後過了一段時間它又長出一條新尾巴了,然後尾巴還可以來回擺動。這些都屬於它的狀態的改變。而這些狀態的改變都是行為。

重學JavaScript 對象

Object 三要素

重學JavaScript 對象

  • Identifier —— 唯一標識
  • State —— 狀態
  • Behavior —— 行為

其實哲學家他們就會研究一個Object,例如魚的唯一標識是什麼,這條魚的骨頭全部挑出來看還是不是這條魚。然後把肉都切下來,再拼起來看是不是這條魚,這就是著名的哲學問題 「忒修斯之船」。

這個我們就不用關心,我們就說變數它是有一個唯一識別性,這個也是物件的一個核心要素具備了。

物件就要有狀態,狀態是可以被改變的,改變就是行為。這樣對象的三要素就成立了。

我們腦中的任何一個概念和現實中的任何一個物品,都可以成為一個對象,只要三要素是齊備的。

重學JavaScript 對象

Object —— Class(類別)

首先Class 類別和Type 類型是兩個不一樣的概念。

重學JavaScript 對象

我們認識物件的一個重要的方式叫做分類,我們可以用分類的方式去描述物件。例如我們研究透測一條魚之後,它與所有同類型的魚特性都是類似的,所以我們就可以把這些魚歸為一類,叫 “魚類”(Fish Class)。

其實在魚的分類上還有更大的為 “動物分類 (Animal)”,那麼動物下面還有其他動物的分類,比如說羊 (Sheep)。所以說魚和羊之間他們的共通性就會用 “動物” 來描述。然後我們一層一層的抽象,在 "Animal" 之上還會有 Object。

類別是一個非常常見的描述對象的一種方式,比如說我們剛剛講到的生物,用對象可以把所有的生物分成界門綱目科屬種,是一個龐大的分類體系。在寫程式碼的時候,分類是一個為業務服務的,我們沒有必要分的那麼細。通常我們會把有共性的需要寫在代碼裡的,我們就把 Animal 提出來,就不再分這個哺乳動物,還是卵生,還是脊索動物等等。

分類有兩個流派,一種是歸類,一種是分類

  • 歸類 —— 就是我們去研究單一對象,然後我們從裡面提取共性變成類,之後我們又在類別之間去提取共性,把它們變成更高的抽象類別。例如我們在 “羊” 和 “魚” 中提取共通性,然後把它們之間的共享再提取出來變成 “動物” 的類。對於 「歸類」 方法而言,多繼承是非常自然的事情,如 C 中的菱形繼承,三角形繼承等。
  • 分類 —— 則是把世界萬物都抽象化為一個基底類別 Object,然後定義這個 Object 中有什麼。採用分類思想的電腦語言,則是單一繼承結構。並且會有一個基底類別 Object。

JavaScript 這個語言比較接近 “分類” 這個思想,但是它也不完全是分類的思想,因為它是一個多範式的物件導向語言。

重學JavaScript 對象

Object —— Prototype(原型)

接下來我們來講JavaScript 描述物件的方式。

重學JavaScript 對象

其實分類Class Based 的Object 並不是一個唯一的認識對象的方法,我們還有一個更接近人類自然認知的。分類的能力可能至少要到小學才有的。但是我們認識對象之後,幾乎是馬上就可以得到另外一種描述對象的方式。那就是 “原型”。

原型其實實用 「照貓畫老虎」 來理解 ,其實照貓畫虎就是用的一種原型方法。因為貓和老虎很像,所以我們只需要把它們直接的有差別的地方分出來就可以了。

比如說我們現在想研究魚,那麼找一種典型的魚,比如找一條具體的鯉魚,然後我們把這條鯉魚所有的特徵都加到魚類的原型上。其他的魚只要有對象,我們就根據魚的原型進行修改。比如說鯰魚比鯉魚更能吃,它是吃肉的,而且身上還是滑滑的,所以我們就可以在鯉魚的原型基礎上把這些特徵加上,這樣我們就能描述出鯰魚了。

那麼在羊類裡面,我們也選取一隻小綿羊來做我們的基礎原型。然後如果我們找到一隻山羊,我們分析出它的特性是多鬍子,腳是彎點,又長又硬又能爬山,那麼我們就在小綿羊的原型上加上這些特性,那我們就描述了一隻山羊了。

那麼在上級的「動物」 中我們也選一隻典型的動物,比如說老虎,有四個蹄,但是不一定所有動物都有4個蹄子,不過原型選擇相對來說它是比較自由的。例如我們選擇蛇作為動物的原型的話,那麼我們在描述魚的時候就特別費勁了,描述貓的時候就更費勁了。

原型裡面也會有一個最終版的原型叫 Object Prototype,這個就是所有物品的典型的物品,也可以說是我們所有物件的老祖宗。我們描述任何物件都是從它與描述物件的區別來進行描述的。

然後在 Object Prototype 之上一般來說是不會再有原型了,但是有些語言裡面會允許有一種 Nihilo 原型。 Nihilo 的意思是虛無空虛,這個是語言中立的講法。如果我們用JavaScript 的具體的設施來描述,那麼這個Nihilo 原型就是null,這個大家就很容易理解了,我們很容易就可以簡歷一個null 物件的原型。

小總結:

  • 我們這種原型是更接近人類原始認知的描述物件的方法
  • 所以物件導向的各種方法其實沒有絕對的對錯,只存在在不同場景下不同的代價
  • 原型的認知成本低,選錯的成本也比較低,所以原型適合一些不是那麼清晰和描述上比較自由的場景
  • 而分類(Class)比較適合用在一些比較嚴謹的場景,而Class 有一個優點,它天然的跟類型系統有一定的整合的,所以很多的語言就會選擇把Class的繼承關係整合進類型系統的繼承關係當中
重學JavaScript 對象

小練習

我們如果需要寫一個「狗咬人」 的Class,我們需要怎麼設計呢?

如果我們依照一個比較樸素的方法,我們就會去定義一個 Dog Class,然後裡面給這個 Class 一個 bite 的方法。

class Dog {
  bite(Human) {    // ......
  }
}复制代码

這樣的一段程式碼是跟我們的題目是一模一樣的,但是這個抽像是一個錯誤的抽象。因為這違背了物件導向的基本特徵,不管我們是怎麼設計,只要這個 bite 發生在狗身上就是錯的。

為什麼?

重學JavaScript 對象

因為我們前面講到了物件導向的三要素,物件的狀態必須是物件本身的行為才能改變的。那麼如果我們在狗的 Class 中寫 bite 這個動作,但是改變的狀態是 “人”,最為狗咬了人之後,只會對人造成傷害。所以在這個行為中 「人」 的狀態是改變的,那麼如果行為是在狗的 Class 中就違反了物件導向的特徵了。

當然如果是狗吃人,那我們勉強是可以成立的,因為狗吃了人狗就飽了,那對狗的狀態是有發生改變的。但是狗咬人,我們基本上可以認為這個行為對狗的狀態是沒有改變的。

所以我們應該在 「人」 的 Class 中設計一個行為。那麼有些同學就會問,我們是應該在人的身上加入一個 biteBy 行為嗎?就是人被咬的一個行為?似乎也不對,因為人 Class 裡面的行為應該是用來改變人的狀態的,那這個行為的命名應該是怎麼樣的呢?

這裡更合理的行為應該是 hurt 表示被傷害了,然後傳入這個行為的參數就是受到的傷害程度 damage。因為這裡人只關心它受到的傷害有多少就可以了,他是不需要關心是狗咬的還是什麼咬的。

class Human {
  hurt(damage) {    //......
  }
}复制代码

狗咬人在实际开发场景中,是一个业务逻辑,我们只需要设计改变人 Human 对象内部的状态的行为,所以它正确的命名应该是 hurt。这里的 damage,可以从狗 Class 中咬 bite, 的行为方法中计算或者生成出来的一个对象,但是如果我们直接传狗 Dog 的对象进来的话,肯定是不符合我们对对象的抽象原则的。

最终我们的代码实现逻辑如下:

class Human {  constructor(name = '人') {    this.name = name;    this.hp = 100;
  }

  hurt(damage) {    this.hp -= damage;    console.log(`${this.name} 受到了 ${damage} 点伤害,剩余生命中为 ${this.hp}`);
  }
}class Dog {  constructor(name = '狗') {    this.name = name;    this.attackPower = 10; // 攻击力
  }

  bite() {    return this.attackPower;
  }
}let human = new Human('三钻');let dog = new Dog();

human.hurt(dog.bite()); // 输出:三钻 受到了 10 点伤害,剩余生命中为 90复制代码

设计对象的原则

  • 我们不应该受到语言描述的干扰(特别是业务需求的干扰)
  • 在设计对象的状态和行为时,我们总是遵循 “行为改变状态” 的原则
  • 违背了这个原则,整个对象的内聚性就没有了,这个对架构上会造成巨大的破坏

相关免费学习推荐:javascript(视频)

以上是重學JavaScript 對象的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除