簡體   English   中英

什么是 Javascript 中的多態性?

[英]What is polymorphism in Javascript?

我已經閱讀了一些我可以在互聯網上找到的關於polymorphism 的可能的文章。 但我想我不能完全理解它的含義和重要性。 大多數文章沒有說明為什么它很重要以及我如何在 OOP 中實現多態行為(當然在 JavaScript 中)。

我無法提供任何代碼示例,因為我不知道如何實現它,所以我的問題如下:

  1. 它是什么?
  2. 為什么我們需要它?
  3. 這個怎么運作?
  4. 如何在 javascript 中實現這種多態行為?

我有這個例子。 但是很容易理解這段代碼的結果。 它沒有給出關於多態性本身的任何清晰的概念。

function Person(age, weight) {
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo.";
    }
}
function Employee(age, weight, salary) {
    this.salary = salary;
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo " +
        "and earns " + this.salary + " dollar.";
    }
}

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
  // The argument, 'obj', can be of any kind
  // which method, getInfo(), to be executed depend on the object
  // that 'obj' refer to.

function showInfo(obj) {
    document.write(obj.getInfo() + "<br>");
}

var person = new Person(50,90);
var employee = new Employee(43,80,50000);
showInfo(person);
showInfo(employee);

多態性是面向對象編程 (OOP) 的原則之一。 它是設計對象以共享行為並能夠用特定的行為覆蓋共享行為的實踐。 多態利用繼承來實現這一點。

在 OOP 中,一切都被認為是作為對象建模的。 這種抽象可以一直深入到汽車的具體細節,也可以簡單地概括為具有年份、品牌和型號的汽車類型。

要擁有多態汽車場景,將有基本汽車類型,然后會有從汽車繼承的子類,並在汽車將具有的基本行為之上提供自己的行為。 例如,一個子類可能是 TowTruck,它仍然有一個年份的品牌和型號,但也可能有一些額外的行為和屬性,這些行為和屬性可能像 IsTowing 的標志一樣基本,也可能像電梯的細節一樣復雜。

回到人和員工的例子,所有的員工都是人,但所有的人都不是員工。 也就是說,人是超類,員工是子類。 人們可能有年齡和體重,但他們沒有薪水。 員工是人,所以他們天生就有年齡和體重,但也因為他們是員工,他們會有薪水。

所以為了方便,我們先寫出超類(Person)

function Person(age,weight){
 this.age = age;
 this.weight = weight;
}

我們將賦予 Person 共享其信息的能力

Person.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo.";
};

接下來我們希望有一個 Person、Employee 的子類

function Employee(age,weight,salary){
 this.age = age;
 this.weight = weight;
 this.salary = salary;
}
Employee.prototype = new Person();

我們將通過定義一個更適合 Employee 的行為來覆蓋 getInfo 的行為

Employee.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo " +
    "and earns " + this.salary + " dollar.";  
};

這些可以類似於您的原始代碼使用

var person = new Person(50,90);
var employee = new Employee(43,80,50000);

console.log(person.getInfo());
console.log(employee.getInfo());

然而,在這里使用繼承並沒有太多好處,因為 Employee 的構造函數與 person 的構造函數如此相似,並且原型中唯一的函數被覆蓋了。 多態設計的力量在於共享行為。

正如在另一個答案中所解釋的,多態性有不同的解釋。

我讀過的關於這個主題的最佳解釋是著名類型理論家Luca Cardelli的一篇文章。 這篇文章名為On Understanding Types, Data Abstraction, and Polymorphism

它是什么?

Cardelli 在這篇文章中定義了幾種類型的多態:

  • 普遍的
    • 參數
    • 包容
  • 特設
    • 超載
    • 強迫

或許在 JavaScript 中,要看到多態的影響有點困難,因為更經典的多態類型在靜態類型系統中更為明顯,而 JavaScript 具有動態類型系統。

因此,例如,在 JavaScript 中,在編譯時沒有方法或函數重載或自動類型強制。 在動態語言中,我們認為大多數這些事情都是理所當然的。 由於語言的動態特性,我們也不需要 JavaScript 中的參數多態之類的東西。

盡管如此,JavaScript 還是有一種類型繼承的形式,它以類似於我們在其他面向對象的編程語言(如 Java 或 C#)中通常所做的方式模擬子類型多態性(上面被 Cardelli 歸類為包含多態性)的相同思想(如在我在上面分享的另一個答案)。

動態語言中非常典型的另一種多態形式稱為鴨子類型

認為多態只與面向對象編程有關是錯誤的。 其他編程模型(函數式、過程式、邏輯式等)在它們的類型系統中提供了不同形式的多態性,這對於那些只使用過 OOP 的人來說可能有點陌生。

為什么我們需要它?

多態性在軟件中培養了許多良好的屬性,除其他外,它促進了模塊化和可重用性,並使類型系統更加靈活和可塑。 沒有它,就很難對類型進行推理。 多態確保一種類型可以被其他兼容的類型替換,前提是它們滿足公共接口,因此這也促進了信息隱藏和模塊化。

它是如何工作的?

這並不容易回答,不同的語言有不同的實現方式。 在 JavaScript 的情況下,如上所述,您將看到它使用原型繼承以類型層次結構的形式具體化,您也可以使用鴨子類型來利用它。

這個主題有點寬泛,你在一篇文章中提出了兩個很多問題。 也許你最好先閱讀 Cardelli 的論文,然后嘗試理解多態性,而不考慮任何語言或編程范式,然后你將開始在理論概念與任何特定語言(如 JavaScript)之間建立關聯,以實現這些想法。

多態的目的是什么?

多態性通過放寬類型等價的條件,使靜態類型系統更加靈活,而不會失去(重要的)靜態類型安全性。 證據仍然是程序只有在不包含任何類型錯誤時才會運行。

多態函數或數據類型比單態函數或數據類型更通用,因為它可以用於更廣泛的場景。 在這個意義上,多態代表了嚴格類型語言中的泛化思想。

這如何適用於 Javascript?

Javascript 有一個弱的動態類型系統。 這樣的類型系統等價於只包含一種類型的嚴格類型系統。 我們可以將這樣的類型視為一個巨大的聯合類型(偽語法):

type T =
 | Undefined
 | Null
 | Number
 | String
 | Boolean
 | Symbol
 | Object
 | Array
 | Map
 | ...

在運行時,每個值都將與這些類型替代之一相關聯。 由於 Javascript 是弱類型的,因此每個值都可以多次更改其類型。

如果我們從類型理論的角度來看,並認為只有一種類型,我們可以肯定地說 Javascript 的類型系統沒有多態的概念。 相反,我們有鴨子類型和隱式類型強制。

但這不應該阻止我們考慮程序中的類型。 由於 Javascript 中缺少類型,我們需要在編碼過程中推斷它們。 我們的頭腦必須為缺少的編譯器提供支持,即我們在查看程序時不僅必須識別算法,還必須識別底層(可能是多態)類型。 這些類型將幫助我們構建更可靠、更健壯的程序。

為了正確地做到這一點,我將向您概述多態性最常見的表現形式。

參數多態性(又名泛型)

參數多態說不同的類型是可以互換的,因為類型根本不重要。 定義一個或多個參數多態類型參數的函數必須不了解相應參數的任何信息,但將它們全部視為相同,因為它們可以采用任何類型。 這是非常有限的,因為這樣的函數只能使用不屬於其數據的參數的那些屬性:

// parametric polymorphic functions

const id = x => x;

id(1); // 1
id("foo"); // "foo"

const k = x => y => x;
const k_ = x => y => y;

k(1) ("foo"); // 1
k_(1) ("foo"); // "foo"

const append = x => xs => xs.concat([x]);

append(3) ([1, 2]); // [1, 2, 3]
append("c") (["a", "b"]); // ["a", "b", "c"]

臨時多態性(又名重載)

Ad-hoc 多態性表示不同類型僅對於特定目的是等效的。 要在這個意義上等效,類型必須實現一組特定於該目的的函數。 定義一個或多個特定多態類型參數的函數然后需要知道哪些函數集與其每個參數相關聯。

Ad-hoc 多態性使函數與更大的類型域兼容。 以下示例說明了“map-over”的目的以及類型如何實現此約束。 “可映射”約束僅包含一個map函數,而不是一組函數:

 // Option type class Option { cata(pattern, option) { return pattern[option.constructor.name](option.x); } map(f, opt) { return this.cata({Some: x => new Some(f(x)), None: () => this}, opt); } }; class Some extends Option { constructor(x) { super(x); this.x = x; } }; class None extends Option { constructor() { super(); } }; // ad-hoc polymorphic function const map = f => t => t.map(f, t); // helper/data const sqr = x => x * x; const xs = [1, 2, 3]; const x = new Some(5); const y = new None(); // application console.log( map(sqr) (xs) // [1, 4, 9] ); console.log( map(sqr) (x) // Some {x: 25} ); console.log( map(sqr) (y) // None {} );

亞型多態性

由於其他答案已經涵蓋了子類型多態性,我跳過它。

結構多態性(又名結構子類型)

結構多態性表示不同類型是等效的,如果它們以某種方式包含相同的結構,則一種類型具有另一種類型的所有屬性,但可能包含其他屬性。 話雖如此,結構多態性在編譯時是鴨子類型,當然提供了一些額外的類型安全。 但是僅僅因為它們共享某些屬性就聲稱兩個值屬於同一類型,它完全忽略了值的語義級別:

const weight = {value: 90, foo: true};
const speed =  {value: 90, foo: false, bar: [1, 2, 3]};

不幸的是, speed被認為是weight一個子類型,一旦我們比較value屬性,我們實際上就是在比較蘋果和橙子。

它是什么?

多 = 多,態射 = 形式或行為轉移。

為什么我們需要它?

在編程中,當我們希望函數(假設函數 X 的)接口足夠靈活以接受不同類型或數量的參數時,會使用它。 此外,基於改變參數類型或數字,我們可能希望函數 X 表現不同(態射)。

這個怎么運作?

我們編寫了 X 函數的多個實現,其中每個實現都接受不同的參數類型或參數數量。 根據參數的類型或數量,編譯器(在運行時)決定當從某些代碼調用 X 時應該執行 X 的哪個實現。

如何在 javascript 中實現這種多態行為?

JS 不是一種類型化語言,所以它真的不打算使用像多態這樣的 OOP 概念。 但是,較新版本的 JS 現在包含類,並且多態性也有可能在 JS 中開始變得有意義。 其他答案提供了一些有趣的解決方法。

多態意味着能夠在不同的對象上調用相同的方法並且每個對象以不同的方式響應稱為POLYMORPHISM

 function Animal(sound){ this.sound=sound; this.speak=function(){ return this.sound; } } //one method function showInfo(obj){ console.log(obj.speak()); } //different objects var dog = new Animal("woof"); var cat = new Animal("meow"); var cow = new Animal("humbow"); //responds different ways showInfo(dog); showInfo(cat); showInfo(cow);

JavaScript 是一種解釋型語言,而不是一種編譯型語言。

編譯時多態(或靜態多態)編譯時多態不過是java,c++中的方法重載

所以在javascript中方法重載是不可能的。

但是動態(運行時)多態性是運行時存在的多態性,因此在 javascript 中方法覆蓋是可能的

另一個例子是 PHP。

多態性是定義通用行為類型的能力,該行為在應用於不同類型時表現不同。

假設我們有一個實現talk 方法的Animal 類。 如果類 Dog 和 Cat 從類 Animal 繼承 talk(),則對象 dog 和對象 cat 都會說話,但形式不同。

當我們迭代一個集合時,該集合必須支持可迭代協議。 集合對象的形式是數組還是字典都沒有關系。

+,-,*,/ 運算符是多態的。 因為它們適用於不同的類型:復數、十進制、整數。

多態的主要好處是恢復代碼。 如果您有重復的代碼,您需要額外的內存空間,這會降低性能,並且您還需要某人或某物來維護該重復代碼。

由於您重用單個代碼,它有助於輕松調試您的代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM