¿Qué es el polimorfismo en Javascript?

92

He leído algún posible artículo que pude encontrar en Internet sobre polimorfismo . Pero creo que no pude captar del todo el significado y su importancia. La mayoría de los artículos no dicen por qué es importante y cómo puedo lograr un comportamiento polimórfico en OOP (por supuesto en JavaScript).

No puedo proporcionar ningún ejemplo de código porque no tengo la idea de cómo implementarlo, por lo que mis preguntas están a continuación:

  1. ¿Qué es?
  2. ¿Por qué lo necesitamos?
  3. ¿Cómo funciona?
  4. ¿Cómo puedo lograr este comportamiento polimórfico en javascript?

Tengo este ejemplo. Pero es fácilmente comprensible cuál será el resultado de este código. No da una idea clara sobre el polimorfismo en sí.

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);
AL-zami
fuente
Esta pregunta probablemente sea demasiado amplia para funcionar bien con StackOverflow. Lo mejor que podríamos hacer sería vincularlo con alguna otra explicación del polimorfismo. StackOverflow es mejor para responder preguntas específicas o aclaraciones sobre un problema específico, como "La fuente dijo que el polimorfismo era XYZ, pero ¿qué significa Y?"
Vitruvio
2
no lo necesitas. en absoluto. ni siquiera necesita clases en JS y, de hecho, hay muchos otros paradigmas, posiblemente mejores, para la construcción de aplicaciones. apply / call / bind elimina la necesidad de homogeneidad, y con soft-object, puede modificar cualquier cosa para que se adapte a sus necesidades sin decorarlo previamente ni heredar casos especiales.
dandavis
1
El polimorfismo no solo está relacionado con OO y tiene muchos significados. Es posible que desee leer esta otra respuesta en las preguntas ¿Es posible el polimorfismo sin herencia ?
Edwin Dalorzo
Heredar generalmente se hace incorrectamente en JavaScript. Crear una instancia de Parent para usarla como prototipo de Child muestra una falta de comprensión del papel que desempeñan la función constructora y el prototipo en la definición y creación de un objeto. Hay más información disponible en esta respuesta: stackoverflow.com/a/16063711/1641941
HMR

Respuestas:

99

El polimorfismo es uno de los principios de la programación orientada a objetos (OOP). Es la práctica de diseñar objetos para compartir comportamientos y poder anular los comportamientos compartidos con otros específicos. El polimorfismo se aprovecha de la herencia para que esto suceda.

En OOP todo se considera modelado como un objeto. Esta abstracción se puede llevar hasta las tuercas y tornillos para un automóvil, o tan amplia como simplemente un tipo de automóvil con un año, marca y modelo.

Para tener un escenario de automóvil polimórfico, habría el tipo de automóvil base, y luego habría subclases que heredarían del automóvil y proporcionarían sus propios comportamientos además de los comportamientos básicos que tendría un automóvil. Por ejemplo, una subclase podría ser TowTruck, que todavía tendría una marca y modelo de un año, pero también podría tener algunos comportamientos y propiedades adicionales que podrían ser tan básicos como una bandera para IsTowing, tan complicados como los detalles del ascensor.

Volviendo al ejemplo de las personas y los empleados, todos los empleados son personas, pero no todas las personas son empleados. Lo que quiere decir que la gente será la superclase y el empleado la subclase. Las personas pueden tener edades y pesos, pero no tienen salarios. Los empleados son personas, por lo que inherentemente tendrán una edad y un peso, pero también porque son empleados tendrán un salario.

Entonces, para facilitar esto, primero escribiremos la superclase (Persona)

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

Y le daremos a la Persona la capacidad de compartir su información.

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

A continuación, deseamos tener una subclase de Persona, Empleado

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

Y anularemos el comportamiento de getInfo definiendo uno que sea más adecuado para un empleado

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

Estos se pueden usar de manera similar a su uso de código original

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

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

Sin embargo, no se gana mucho con la herencia aquí, ya que el constructor de Employee es muy similar al de la persona y la única función en el prototipo está siendo anulada. El poder del diseño polimórfico es compartir comportamientos.

Travis J
fuente
2
@ user3138436 - Eso es correcto. Se inspeccionará la cadena prototípica para ver si la primera ocurrencia getInfoserá la del Empleado, ya que es más alta en la cadena que la de la Persona. Eso fue lo que quise decir cuando dije "anulado".
Travis J
17
No está utilizando el constructor (Person.call (this, arg)) y configurando el prototipo de empleado en una instancia de Person. El prototipo es donde van los miembros compartidos y la función constructora es donde se crean los miembros específicos de la instancia. Sus muestras usan copiar y pegar código, reutilizar la función constructora y heredar la parte del prototipo incorrectamente (la persona tiene miembros específicos de instancia que no tienen nada que ver con Employee.prototype, especialmente si tiene miembros mutables). Más información sobre la herencia usando funciones de constructor y prototipo aquí: stackoverflow.com/a/16063711/1641941
HMR
3
Para que el empleado reutilice y extienda la getinfo de la persona, simplemente podría hacerlo en return Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";lugar de copiar y pegar el código.
HMR
3
aquí donde se aplicó el polimorfismo?
albert Jegani
2
@rpeg, el punto de polimorfismo se puede ver mejor en el ejemplo de OP. la función showInfo (); acepta un objeto general. El polimorfismo ahora es la capacidad de reaccionar de manera diferente según el tipo de objeto. Creo que esta respuesta no deja esto lo suficientemente claro, ya que llama a getInfo () en cada objeto específico.
Stefan
26

Como se explica en esta otra respuesta , el polimorfismo tiene diferentes interpretaciones.

La mejor explicación sobre el tema que he leído es un artículo de Luca Cardelli , un renombrado teórico de la tipografía. El artículo se llama Sobre la comprensión de tipos, abstracción de datos y polimorfismo .

¿Qué es?

Cardelli define varios tipos de polimorfismo en este artículo:

  • Universal
    • paramétrico
    • inclusión
  • Ad hoc
    • desbordante
    • coerción

Quizás en JavaScript, es un poco más difícil ver los efectos del polimorfismo porque los tipos más clásicos de polimorfismo son más evidentes en los sistemas de tipos estáticos, mientras que JavaScript tiene un sistema de tipos dinámicos.

Entonces, por ejemplo, no hay sobrecarga de métodos o funciones o coacciones de tipo automáticas en tiempo de compilación en JavaScript. En un lenguaje dinámico, damos por sentado la mayoría de estas cosas. Tampoco necesitamos algo como el polimorfismo paramétrico en JavaScript debido a la naturaleza dinámica del lenguaje.

Aún así, JavaScript tiene una forma de herencia de tipos que emula las mismas ideas de polimorfismo de subtipo (clasificado como polimorfismo de inclusión por Cardelli anteriormente) de una manera similar a lo que normalmente hacemos en otros lenguajes de programación orientados a objetos como Java o C # (como se explica en otra respuesta que compartí arriba).

Otra forma de polimorfismo muy típica en los lenguajes dinámicos se llama tipificación de pato .

Es un error creer que el polimorfismo solo está relacionado con la programación orientada a objetos. Otros modelos de programación (funcionales, procedimentales, lógicos, etc.) ofrecen diferentes formas de polimorfismo en sus sistemas de tipos, probablemente de una manera un poco desconocida para aquellos que solo se utilizan para OOP.

¿Por qué lo necesitamos?

El polimorfismo fomenta muchos atributos buenos en el software, entre otras cosas fomenta la modularidad y la reutilización y hace que el sistema de tipos sea más flexible y maleable. Sin él, sería muy difícil razonar sobre tipos. El polimorfismo asegura que un tipo pueda ser sustituido por otros compatibles siempre que satisfagan una interfaz pública, por lo que también fomenta la ocultación de información y la modularidad.

¿Como funciona?

Esto no es fácil de responder, diferentes lenguajes tienen diferentes formas de implementarlo. En el caso de JavaScript, como se mencionó anteriormente, lo verá materializarse en forma de jerarquías de tipos usando la herencia prototípica y también puede explotarlo usando el tipo pato.

El tema es un poco amplio y abriste dos preguntas en una sola publicación. Quizás sea mejor que comiences leyendo el artículo de Cardelli y luego trates de entender el polimorfismo independientemente de cualquier lenguaje o paradigma de programación, luego comenzarás a hacer asociaciones entre los conceptos teóricos y lo que cualquier lenguaje en particular como JavaScript tiene para ofrecer para implementar esas ideas.

Edwin Dalorzo
fuente
14

¿Cuál es el propósito del polimorfismo?

El polimorfismo hace que un sistema de tipo estático sea más flexible sin perder la seguridad (significativa) del tipo estático al aflojar las condiciones para la equivalencia de tipo. La prueba es que un programa solo se ejecutará si no contiene ningún error de tipo.

Una función o tipo de datos polimórficos es más general que uno monomórfico, porque se puede utilizar en una gama más amplia de escenarios. En este sentido, el polimorfismo representa la idea de generalización en lenguajes estrictamente tipificados.

¿Cómo se aplica esto a Javascript?

Javascript tiene un sistema de tipos dinámico y débil. Tal sistema de tipos es equivalente a un sistema de tipos estricto que contiene solo un tipo. Podemos pensar en un tipo como un tipo de unión enorme (pseudo sintaxis):

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

Cada valor se asociará a uno de estos tipos de alternativas en tiempo de ejecución. Y dado que Javascript tiene un tipo débil, cada valor puede cambiar su tipo cualquier cantidad de veces.

Si tomamos una perspectiva teórica de tipos y consideramos que solo hay un tipo, podemos decir con certeza que el sistema de tipos de Javascript no tiene una noción de polimorfismo. En su lugar, tenemos tipado pato y coerción de tipo implícita.

Pero esto no debería impedirnos pensar en los tipos de nuestros programas. Debido a la falta de tipos en Javascript, necesitamos inferirlos durante el proceso de codificación. Nuestra mente debe reemplazar al compilador que falta, es decir, tan pronto como miramos un programa, debemos reconocer no solo los algoritmos, sino también los tipos subyacentes (tal vez polimórficos). Estos tipos nos ayudarán a crear programas más fiables y robustos.

Para hacer esto correctamente, les daré una descripción general de las manifestaciones más comunes del polimorfismo.

Polimorfismo paramétrico (también conocido como genéricos)

El polimorfismo paramétrico dice que los diferentes tipos son intercambiables porque los tipos no importan en absoluto. Una función que defina uno o más parámetros de tipo polimórfico paramétrico no debe saber nada de los argumentos correspondientes pero tratarlos de igual forma, porque pueden adoptar a cualquier tipo. Esto es bastante restrictivo, porque dicha función solo puede trabajar con aquellas propiedades de sus argumentos que no son parte de sus datos:

// 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 (también conocido como sobrecarga)

El polimorfismo ad-hoc dice que los diferentes tipos son equivalentes solo para un propósito específico. Para ser equivalente en este sentido, un tipo debe implementar un conjunto de funciones específicas para ese propósito. Una función que define uno o más parámetros de tipo polimórfico ad-hoc necesita saber qué conjuntos de funciones están asociados a cada uno de sus argumentos.

El polimorfismo ad-hoc hace que una función sea compatible con un dominio de tipos más amplio. El siguiente ejemplo ilustra el propósito de la "asignación" y cómo los tipos pueden implementar esta restricción. En lugar de un conjunto de funciones, la restricción "asignable" solo incluye una única mapfunción:

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

Polimorfismo de subtipo

Dado que otras respuestas ya cubren el polimorfismo de subtipo, lo omito.

Polimorfismo estructural (también conocido como subtipo estructural)

El polimorfismo estructural dice que diferentes tipos son equivalentes, si contienen la misma estructura de tal manera que un tipo tiene todas las propiedades del otro pero puede incluir propiedades adicionales. Dicho esto, el polimorfismo estructural consiste en escribir pato en tiempo de compilación y ciertamente ofrece cierta seguridad de tipo adicional. Pero al afirmar que dos valores son del mismo tipo solo porque comparten algunas propiedades, ignora por completo el nivel semántico de valores:

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

Desafortunadamente, speedse considera un subtipo de weighty tan pronto como comparamos las valuepropiedades, estamos comparando virtualmente manzanas con naranjas.


fuente
1
Si bien esto es en muchos aspectos más preciso (y ciertamente más completo) que la respuesta aceptada, no tiene la misma accesibilidad: esta respuesta supone que el autor de la pregunta ya es demasiado inteligente para molestarse con la pregunta :)
Jared Smith
@JaredSmith Traté de resumir el tema en algunos párrafos fáciles de entender. Pero cuanto más profundo taladro, más complejo se vuelve. Nunca encontré una buena fuente para el polimorfismo en lenguajes no tipificados, por lo tanto, creo que esta respuesta es valiosa.
la edición mejora enormemente la accesibilidad. En cuanto a la utilidad del polimorfismo en los lenguajes dinámicos, hay muchos, pero estoy luchando por pensar en un buen ejemplo en JS. Un mejor ejemplo serían los métodos mágicos de Python que permiten que los tipos definidos por el usuario trabajen con funciones polimórficas como len. O quizás conjde clojure.
Jared Smith
8

¿Qué es?

Poli = muchos, morfismo = cambio de forma o comportamiento.

por qué lo necesitamos?

En programación, se usa cuando queremos que la interfaz de una función (digamos la función X) sea lo suficientemente flexible como para aceptar diferentes tipos o cantidad de parámetros. Además, según los tipos o números de parámetros cambiantes, es posible que deseemos que la función X se comporte de manera diferente (morfismo).

¿Cómo funciona?

Escribimos múltiples implementaciones de la función X donde cada implementación acepta diferentes tipos de parámetros o número de parámetros. Según el tipo o número de parámetro, el compilador (en tiempo de ejecución) decide qué implementación de X debe ejecutarse cuando se llama a X desde algún código.

¿Cómo puedo lograr este comportamiento polimórfico en javascript?

JS no es un lenguaje mecanografiado, por lo que en realidad no está destinado a utilizar conceptos de programación orientada a objetos como polimorfismo. Sin embargo, la versión más reciente de JS ahora incluye clases y existe la posibilidad de que el polimisfismo también comience a tener sentido en JS. Otras respuestas proporcionan algunas soluciones alternativas interesantes.

hadaytullah
fuente
4

Polimorfismo significa Habilidad de llamar al mismo método en diferentes objetos y cada objeto responde de manera diferente se llama 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);

SooRaj Patil
fuente
2

JavaScript es un lenguaje interpretado, no un lenguaje compilado.

Polimorfismo en tiempo de compilación (o polimorfismo estático) El polimorfismo en tiempo de compilación no es más que la sobrecarga de métodos en java, c ++

Entonces, la sobrecarga de métodos no es posible en javascript.

Pero el polimorfismo dinámico (en tiempo de ejecución) es el polimorfismo que existía en el tiempo de ejecución, por lo que es posible anular el método en javascript

otro ejemplo es PHP.

Apoorv
fuente