Cos'è il polimorfismo in Javascript?


90

Ho letto qualche possibile articolo che ho trovato su Internet sul polimorfismo . Ma penso di non essere riuscito a coglierne il significato e la sua importanza. La maggior parte degli articoli non dice perché è importante e come posso ottenere un comportamento polimorfico in OOP (ovviamente in JavaScript).

Non posso fornire alcun esempio di codice perché non ho l'idea di come implementarlo, quindi le mie domande sono di seguito:

  1. Che cos'è?
  2. Perché ne abbiamo bisogno?
  3. Come funziona?
  4. Come posso ottenere questo comportamento polimorfico in javascript?

Ho questo esempio. Ma è facilmente comprensibile quale sarà il risultato di questo codice. Non dà un'idea chiara del polimorfismo stesso.

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);

Questa domanda è probabilmente troppo ampia per funzionare bene con StackOverflow. Il meglio che potremmo fare sarebbe collegarti a qualche altra spiegazione del polimorfismo. StackOverflow è il modo migliore per rispondere a domande o chiarimenti specifici su un problema specifico, come "La fonte ha detto che il polimorfismo era XYZ, ma cosa significa Y?"
Vitruvio

2
non ne hai bisogno. affatto. non hai nemmeno bisogno di lezioni in JS e, in effetti, ci sono molti altri paradigmi, probabilmente migliori, per la costruzione di app. apply / call / bind elimina la necessità di omogeneità e, con soft-object, puoi modificare qualsiasi cosa per adattarla alle tue esigenze senza pre-decorarla o ereditare casi speciali.
dandavis

1
Il polimorfismo non è solo correlato all'OO e ha molti significati. Puoi leggere quest'altra risposta sotto le domande Il polimorfismo è possibile senza ereditarietà .
Edwin Dalorzo

L'ereditarietà viene solitamente eseguita in modo errato in JavaScript. Creare un'istanza di Parent da utilizzare come prototipo di Child mostra una mancanza di comprensione del ruolo svolto dalla funzione di costruzione e dal prototipo nella definizione e nella creazione di un oggetto. Maggiori informazioni sono disponibili in questa risposta: stackoverflow.com/a/16063711/1641941
HMR

Risposte:


99

Il polimorfismo è uno dei principi della programmazione orientata agli oggetti (OOP). È la pratica di progettare oggetti per condividere comportamenti ed essere in grado di sovrascrivere comportamenti condivisi con comportamenti specifici. Il polimorfismo sfrutta l'ereditarietà per far sì che ciò accada.

In OOP tutto è considerato modellato come un oggetto. Questa astrazione può essere portata fino ai dadi e ai bulloni per un'auto, o semplicemente come un tipo di auto con un anno, una marca e un modello.

Per avere uno scenario di auto polimorfico ci sarebbe il tipo di auto di base, e quindi ci sarebbero sottoclassi che erediterebbero dall'auto e fornirebbero i propri comportamenti in cima ai comportamenti di base che un'auto avrebbe. Ad esempio, una sottoclasse potrebbe essere TowTruck che avrebbe ancora un anno di marca e modello, ma potrebbe anche avere alcuni comportamenti e proprietà extra che potrebbero essere tanto semplici quanto una bandiera per IsTowing quanto complicate quanto le specifiche dell'ascensore.

Tornando all'esempio di persone e dipendenti, tutti i dipendenti sono persone, ma tutte le persone non sono dipendenti. Vale a dire che le persone saranno la super classe e i dipendenti la sottoclasse. Le persone possono avere età e peso, ma non hanno stipendio. I dipendenti sono persone quindi avranno intrinsecamente un'età e un peso, ma anche perché sono dipendenti avranno uno stipendio.

Quindi, per facilitare questo, scriveremo prima la super classe (Persona)

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

E daremo a Persona la possibilità di condividere le proprie informazioni

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

Successivamente desideriamo avere una sottoclasse di Person, Employee

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

E sovrascriveremo il comportamento di getInfo definendone uno più adatto a un dipendente

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

Questi possono essere utilizzati in modo simile all'uso del codice originale

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

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

Tuttavia, non c'è molto da guadagnare usando l'ereditarietà qui poiché il costruttore di Employee è così simile a quello di person e l'unica funzione nel prototipo viene sovrascritta. Il potere del design polimorfico è condividere i comportamenti.


2
@ user3138436 - È corretto. La catena prototipale verrà ispezionata per la prima volta getInfoche sarà quella del dipendente in quanto è più alta nella catena rispetto a quella della persona. Questo era ciò che intendevo quando ho detto "sovrascritto".
Travis J

17
Non stai usando il costruttore (Person.call (this, arg)) e stai impostando Employee prototype su un'istanza di Person. Il prototipo è dove vanno i membri condivisi e la funzione di costruzione è dove vengono creati i membri specifici dell'istanza. I tuoi esempi usano il codice copia incolla riutilizzando la funzione di costruzione ed ereditano la parte del prototipo sbagliata (la persona ha membri specifici dell'istanza che non hanno affari su Employee.prototype, specialmente se avresti membri modificabili). Maggiori informazioni sull'ereditarietà utilizzando le funzioni del costruttore e il prototipo qui: stackoverflow.com/a/16063711/1641941
HMR

3
Affinché il dipendente possa riutilizzare ed estendere getinfo di persona, potrebbe semplicemente farlo return Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";invece di copiare e incollare il codice riutilizzabile.
HMR

3
qui dove si applicava il polimorfismo?
albert Jegani

2
@rpeg il punto del polimorfismo può essere visto meglio nell'esempio degli OP. la funzione showInfo (); accetta un oggetto generale. Il polimorfismo ora è la capacità di reagire in modo diverso a seconda del tipo di oggetto. Penso che questa risposta non lo renda abbastanza chiaro, poiché chiama getInfo () su ogni oggetto specifico.
Stefan

26

Come spiegato in quest'altra risposta , il polimorfismo ha interpretazioni diverse.

La migliore spiegazione sull'argomento che abbia mai letto è un articolo di Luca Cardelli , un noto teorico dei tipi. L'articolo è intitolato On Understanding Types, Data Abstraction e Polymorphism .

Che cos'è?

Cardelli definisce diversi tipi di polimorfismo in questo articolo:

  • universale
    • parametrico
    • inclusione
  • Ad hoc
    • oveloading
    • coercizione

Forse in JavaScript è un po 'più difficile vedere gli effetti del polimorfismo perché i tipi più classici di polimorfismo sono più evidenti nei sistemi di tipi statici, mentre JavaScript ha un sistema di tipi dinamico.

Quindi, ad esempio, non esiste alcun sovraccarico di metodi o funzioni o coercizioni automatiche di tipo in fase di compilazione in JavaScript. In un linguaggio dinamico, diamo la maggior parte di queste cose per scontate. Né abbiamo bisogno di qualcosa come il polimorfismo parametrico in JavaScript a causa della natura dinamica del linguaggio.

Tuttavia, JavaScript ha una forma di ereditarietà del tipo che emula le stesse idee di polimorfismo del sottotipo (classificato come polimorfismo di inclusione da Cardelli sopra) in un modo simile a quello che facciamo tipicamente in altri linguaggi di programmazione orientati agli oggetti come Java o C # (come spiegato in un'altra risposta che ho condiviso sopra).

Un'altra forma di polimorfismo molto tipica nei linguaggi dinamici è chiamata dattilografia .

È un errore credere che il polimorfismo sia correlato solo alla programmazione orientata agli oggetti. Altri modelli di programmazione (funzionali, procedurali, logici, ecc.) Offrono diverse forme di polimorfismo nei loro sistemi di tipi, probabilmente in un modo un po 'poco familiare a quelli usati solo per l'OOP.

Perché ne abbiamo bisogno?

Il polimorfismo promuove molte buone caratteristiche nel software, tra le altre cose promuove la modularità e la riusabilità e rende il sistema di tipi più flessibile e malleabile. Senza di esso, sarebbe davvero difficile ragionare sui tipi. Il polimorfismo assicura che un tipo possa essere sostituito da altri compatibili a condizione che soddisfino un'interfaccia pubblica, quindi questo favorisce anche l'occultamento delle informazioni e la modularità.

Come funziona?

Non è semplice rispondere, lingue diverse hanno modi diversi per implementarlo. Nel caso di JavaScript, come accennato in precedenza, lo vedrai materializzarsi sotto forma di gerarchie di tipi utilizzando l' ereditarietà prototipale e puoi anche sfruttarlo utilizzando la digitazione a papera.

L'argomento è un po 'ampio e hai aperto due tante domande in un unico post. Forse è meglio che tu inizi leggendo l'articolo di Cardelli e poi cerchi di comprendere il polimorfismo indipendentemente da qualsiasi linguaggio o paradigma di programmazione, quindi inizierai a fare associazioni tra i concetti teorici e ciò che ogni linguaggio particolare come JavaScript ha da offrire per implementare quelle idee.


14

Qual è lo scopo del polimorfismo?

Il polimorfismo rende un sistema di tipo statico più flessibile senza perdere (significativa) sicurezza del tipo statico allentando le condizioni per l'equivalenza del tipo. La prova rimane che un programma verrà eseguito solo se non contiene errori di tipo.

Una funzione polimorfica o un tipo di dati è più generale di uno monomorfico, perché può essere utilizzato in una gamma più ampia di scenari. In questo senso il polimorfismo rappresenta l'idea di generalizzazione in linguaggi strettamente tipizzati.

Come si applica a Javascript?

Javascript ha un sistema di tipi debole e dinamico. Un tale sistema di tipi è equivalente a un sistema di tipi rigoroso contenente un solo tipo. Possiamo pensare a un tale tipo come a un tipo di unione enorme (pseudo sintassi):

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

Ogni valore verrà associato a una di queste alternative di tipo in fase di esecuzione. E poiché Javascript è digitato in modo debole, ogni valore può cambiare il suo tipo un numero qualsiasi di volte.

Se prendiamo una prospettiva teorica dei tipi e consideriamo che esiste un solo tipo, possiamo dire con certezza che il sistema di tipi di Javascript non ha una nozione di polimorfismo. Invece abbiamo la dattilografia e la coercizione del tipo implicito.

Ma questo non dovrebbe impedirci di pensare ai tipi nei nostri programmi. A causa della mancanza di tipi in Javascript, dobbiamo dedurli durante il processo di codifica. La nostra mente deve sostituire il compilatore mancante, cioè non appena guardiamo un programma dobbiamo riconoscere non solo gli algoritmi, ma anche i tipi sottostanti (forse polimorfici). Questi tipi ci aiuteranno a creare programmi più affidabili e robusti.

Per farlo correttamente, ti fornirò una panoramica delle manifestazioni più comuni di polimorfismo.

Polimorfismo parametrico (noto anche come generici)

Il polimorfismo parametrico afferma che tipi diversi sono intercambiabili perché i tipi non contano affatto. Una funzione che definisce uno o più parametri di tipo polimorfico parametrico non deve sapere nulla degli argomenti corrispondenti ma trattarli tutti uguali, perché possono adattarsi a qualsiasi tipo. Questo è abbastanza restrittivo, perché una tale funzione può funzionare solo con quelle proprietà dei suoi argomenti che non fanno parte dei loro dati:

// 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"]

Polimorfismo ad hoc (noto anche come sovraccarico)

Il polimorfismo ad hoc afferma che tipi diversi sono equivalenti solo per uno scopo specifico. Per essere equivalente in questo senso un tipo deve implementare un insieme di funzioni specifiche a tale scopo. Una funzione che definisce uno o più parametri di tipo polimorfico ad hoc deve quindi sapere quali insiemi di funzioni sono associati a ciascuno dei suoi argomenti.

Il polimorfismo ad hoc rende una funzione compatibile con un più ampio dominio di tipi. Il seguente esempio illustra lo scopo della "mappatura" e come i tipi possono implementare questo vincolo. Invece di un insieme di funzioni, il vincolo "mappabile" include solo una singola mapfunzione:

// 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 {}
);

Sottotipo polimorfismo

Poiché altre risposte coprono già il polimorfismo del sottotipo, lo salto.

Polimorfismo strutturale (noto anche come sottotipizzazione strutrual)

Il polimorfismo strutturale dice che tipi diversi sono equivalenti, se contengono la stessa struttura in modo tale, che un tipo ha tutte le proprietà dell'altro ma può includere proprietà aggiuntive. Detto questo, il polimorfismo strutturale è la digitazione a papera in fase di compilazione e offre sicuramente qualche sicurezza aggiuntiva. Ma sostenendo che due valori sono dello stesso tipo solo perché condividono alcune proprietà, ignora completamente il livello semantico dei valori:

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

Purtroppo, speedè considerato un sottotipo di weighte non appena confrontiamo le valueproprietà stiamo virtualmente confrontando le mele con le arance.


1
Sebbene questo sia per molti aspetti più accurato (e sicuramente più approfondito) della risposta accettata, non ha la stessa accessibilità: questa risposta presume che il richiedente sia già troppo intelligente per preoccuparsi della domanda :)
Jared Smith

@ JaredSmith ho cercato di ridurre l'argomento ad alcuni paragrafi facili da comprendere. Ma più approfondisco, più diventa complesso. Non ho mai trovato una buona fonte per il polimorfismo in lingue non tipizzate, quindi penso che questa risposta sia comunque preziosa.

la modifica migliora notevolmente l'accessibilità. Per quanto riguarda l'utilità del polimorfismo nei linguaggi dinamici, ce ne sono molti, ma faccio fatica a pensare a un buon esempio in JS. Un esempio migliore sarebbero i metodi magici di Python che consentono ai tipi definiti dall'utente di lavorare con funzioni polimorfiche come len. O forse conjda clojure.
Jared Smith

8

che cos'è?

Poly = molti, morfismo = forma o comportamento mutevole.

perché ne abbiamo bisogno?

Nella programmazione, viene utilizzato quando vogliamo che l'interfaccia di una funzione (diciamo la funzione X) sia abbastanza flessibile da accettare diversi tipi o numero di parametri. Inoltre, in base alla modifica dei tipi o dei numeri dei parametri, potremmo volere che la funzione X si comporti in modo diverso (morfismo).

Come funziona?

Scriviamo più implementazioni della funzione X in cui ogni implementazione accetta diversi tipi di parametri o numero di parametri. In base al tipo o al numero di parametro, il compilatore (in fase di esecuzione) decide quale implementazione di X deve essere eseguita quando X viene chiamato da un codice.

come posso ottenere questo comportamento polimorfico in javascript?

JS non è un linguaggio tipizzato, quindi non intende usare concetti OOP come il polimorfismo. Tuttavia, la versione più recente di JS ora include classi e c'è la possibilità che il polimosfismo possa iniziare ad avere senso anche in JS. Altre risposte forniscono alcune soluzioni interessanti.


3

Polimorfismo significa capacità di chiamare lo stesso metodo su oggetti diversi e ogni oggetto risponde in modo diverso è chiamato POLIMORFISMO .

    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);


2

JavaScript è un linguaggio interpretato, non un linguaggio compilato.

Il polimorfismo del tempo di compilazione (o polimorfismo statico) Il polimorfismo del tempo di compilazione non è altro che il sovraccarico del metodo in java, c ++

Quindi il sovraccarico del metodo non è possibile in javascript.

Ma il polimorfismo dinamico (tempo di esecuzione) è il polimorfismo esistente in fase di esecuzione, quindi l'override del metodo è possibile in javascript

un altro esempio è PHP.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.