O que é polimorfismo em Javascript?

90

Eu li alguns possíveis artigos que pude encontrar na internet sobre polimorfismo . Mas acho que não consegui entender o significado disso e sua importância. A maioria dos artigos não diz por que isso é importante e como posso obter um comportamento polimórfico em OOP (é claro em JavaScript).

Não posso fornecer nenhum exemplo de código porque não tenho a ideia de como implementá-lo, então minhas perguntas estão abaixo:

  1. O que é isso?
  2. Por que precisamos disso?
  3. Como funciona?
  4. Como posso conseguir esse comportamento polimórfico em javascript?

Eu tenho esse exemplo. Mas é facilmente compreensível qual será o resultado desse código. Não dá nenhuma ideia clara sobre o polimorfismo em si.

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
fonte
Esta questão é provavelmente muito ampla para funcionar bem com StackOverflow. O melhor que poderíamos fazer seria vinculá-lo a alguma outra explicação de polimorfismo. StackOverflow é melhor para responder a perguntas específicas ou esclarecimentos sobre um problema específico, como "A fonte disse que o polimorfismo era XYZ, mas o que Y significa?"
Vitruvius
2
você não precisa disso. em absoluto. você nem precisa de classes em JS e, na verdade, existem muitos outros paradigmas, indiscutivelmente melhores, para a construção de aplicativos. apply / call / bind elimina a necessidade de homogeneidade e, com soft-object, você pode modificar qualquer coisa para atender às suas necessidades sem pré-decorar ou herdar casos especiais.
dandavis
1
O polimorfismo não está relacionado apenas a OO e tem muitos significados. Você pode querer ler esta outra resposta nas perguntas O polimorfismo é possível sem herança .
Edwin Dalorzo
Herdar geralmente é feito incorretamente em JavaScript. Criar uma instância de Parent para ser usada como protótipo de Child mostra uma falta de compreensão do papel que a função construtora e o protótipo desempenham na definição e criação de um objeto. Mais informações estão disponíveis nesta resposta: stackoverflow.com/a/16063711/1641941
HMR

Respostas:

99

O polimorfismo é um dos princípios da Programação Orientada a Objetos (OOP). É a prática de projetar objetos para compartilhar comportamentos e ser capaz de substituir comportamentos compartilhados por comportamentos específicos. O polimorfismo tira proveito da herança para fazer isso acontecer.

Em OOP, tudo é considerado modelado como um objeto. Essa abstração pode ser levada até as porcas e parafusos para um carro, ou tão ampla quanto simplesmente um tipo de carro com um ano, marca e modelo.

Para ter um cenário de carro polimórfico, haveria o tipo básico de carro e, então, haveria subclasses que herdariam do carro e forneceriam seus próprios comportamentos além dos comportamentos básicos que um carro teria. Por exemplo, uma subclasse poderia ser TowTruck, que ainda teria uma marca e um modelo de ano, mas também poderia ter alguns comportamentos e propriedades extras que poderiam ser tão básicos quanto um sinalizador para IsTowing a tão complicado quanto as especificações do elevador.

Voltando ao exemplo de pessoas e funcionários, todos os funcionários são pessoas, mas nem todas as pessoas são funcionários. O que quer dizer que as pessoas serão a superclasse e os funcionários a subclasse. As pessoas podem ter idade e peso, mas não têm salário. Funcionários são pessoas, portanto, inerentemente terão idade e peso, mas também por serem funcionários terão um salário.

Então, para facilitar isso, vamos primeiro escrever a superclasse (Pessoa)

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

E daremos à pessoa a capacidade de compartilhar suas informações

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

Em seguida, desejamos ter uma subclasse de Pessoa, Funcionário

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

E vamos substituir o comportamento de getInfo, definindo um que seja mais adequado para um funcionário

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

Eles podem ser usados ​​de forma semelhante ao uso do código original

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

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

No entanto, não há muito ganho usando herança aqui, pois o construtor de Employee é muito semelhante ao de pessoa, e a única função no protótipo está sendo substituída. O poder do design polimórfico é compartilhar comportamentos.

Travis J
fonte
2
@ user3138436 - Correto. A cadeia prototípica será inspecionada para a primeira ocorrência da getInfoqual será do Funcionário, pois é mais alta na cadeia do que a da Pessoa. Isso foi o que eu quis dizer quando disse "substituído".
Travis J
17
Você não está usando o construtor (Person.call (this, arg)) e definindo o protótipo Employee para uma instância de Person. O protótipo é para onde os membros compartilhados vão e a função do construtor é onde os membros específicos da instância são criados. Suas amostras usam copiar e colar o código, reutilizar a função do construtor e herdar a parte do protótipo incorreta (Person tem membros específicos da instância que não têm negócios em Employee.prototype, especialmente se você tiver membros mutáveis). Mais informações sobre herança usando funções de construtor e protótipo aqui: stackoverflow.com/a/16063711/1641941
HMR
3
Para Employee reutilizar e estender getinfo de pessoa, isso poderia simplesmente fazer em return Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";vez de copiar e colar o código novamente.
HMR
3
aqui onde o polimorfismo aplicado?
Albert Jegani
2
@rpeg o ponto de polimorfismo pode ser visto melhor no exemplo de OPs. a função showInfo (); aceita um objeto geral. polimorfismo agora é a capacidade de reagir de forma diferente dependendo do tipo de objeto. Acho que essa resposta não deixa isso claro o suficiente, uma vez que chama getInfo () em cada objeto específico.
Stefan
26

Conforme explicado nesta outra resposta , o polimorfismo tem interpretações diferentes.

A melhor explicação sobre o assunto que já li é um artigo de Luca Cardelli , um renomado teórico do tipo. O artigo é denominado Sobre tipos de compreensão, abstração de dados e polimorfismo .

O que é isso?

Cardelli define vários tipos de polimorfismo neste artigo:

  • Universal
    • paramétrico
    • inclusão
  • Ad hoc
    • sobrecarregando
    • coerção

Talvez em JavaScript seja um pouco mais difícil ver os efeitos do polimorfismo porque os tipos mais clássicos de polimorfismo são mais evidentes em sistemas de tipos estáticos, enquanto o JavaScript tem um sistema de tipos dinâmico.

Portanto, por exemplo, não há sobrecarga de método ou função ou coerção automática de tipo em tempo de compilação em JavaScript. Em uma linguagem dinâmica, consideramos muitas dessas coisas como certas. Nem precisamos de algo como polimorfismo paramétrico em JavaScript devido à natureza dinâmica da linguagem.

Ainda assim, o JavaScript tem uma forma de herança de tipo que emula as mesmas ideias de polimorfismo de subtipo (classificado como polimorfismo de inclusão por Cardelli acima) de maneira semelhante ao que normalmente fazemos em outras linguagens de programação orientadas a objetos como Java ou C # (conforme explicado em outra resposta que compartilhei acima).

Outra forma de polimorfismo muito comum em linguagens dinâmicas é chamada de digitação em pato .

É um erro acreditar que o polimorfismo está relacionado apenas à programação orientada a objetos. Outros modelos de programação (funcional, procedural, lógico, etc.) oferecem diferentes formas de polimorfismo em seus sistemas de tipo, provavelmente de uma forma um pouco desconhecida para aqueles usados ​​apenas para OOP.

Por que precisamos disso?

O polimorfismo promove muitos atributos bons em software, entre outras coisas, promove modularidade e capacidade de reutilização e torna o sistema de tipos mais flexível e maleável. Sem ele, seria realmente difícil raciocinar sobre os tipos. O polimorfismo garante que um tipo pode ser substituído por outro compatível, desde que satisfaça uma interface pública, de modo que também promove o ocultamento e a modularidade das informações.

Como funciona?

Não é fácil responder, linguagens diferentes têm maneiras diferentes de implementá-lo. No caso do JavaScript, como mencionado acima, você o verá se materializar na forma de hierarquias de tipo usando a herança prototípica e também poderá explorá-lo usando a digitação duck.

O assunto é um pouco amplo e você abriu várias perguntas em um único post. Talvez seja melhor que você comece lendo o artigo de Cardelli e tente entender o polimorfismo independentemente de qualquer linguagem ou paradigma de programação, então você começará a fazer associações entre os conceitos teóricos e o que qualquer linguagem particular como JavaScript tem a oferecer para implementar essas ideias.

Edwin Dalorzo
fonte
14

Qual é o propósito do polimorfismo?

O polimorfismo torna um sistema de tipo estático mais flexível sem perder (significativa) a segurança do tipo estático ao afrouxar as condições de equivalência de tipo. A prova é que um programa só será executado se não contiver erros de tipo.

Uma função polimórfica ou tipo de dados é mais geral do que um monomórfico, porque pode ser usado em uma ampla gama de cenários. Nesse sentido, o polimorfismo representa a ideia de generalização em linguagens estritamente tipadas.

Como isso se aplica ao Javascript?

Javascript tem um sistema de tipo dinâmico e fraco. Esse sistema de tipo é equivalente a um sistema de tipo estrito contendo apenas um tipo. Podemos pensar nesse tipo como um tipo de união enorme (pseudo sintaxe):

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

Cada valor será associado a uma dessas alternativas de tipo em tempo de execução. E como o Javascript é mal digitado, cada valor pode mudar seu tipo quantas vezes quiser.

Se tomarmos uma perspectiva teórica de tipo e considerarmos que existe apenas um tipo, podemos dizer com certeza que o sistema de tipos do Javascript não possui uma noção de polimorfismo. Em vez disso, temos a digitação duck e a coerção de tipo implícita.

Mas isso não deve nos impedir de pensar sobre os tipos em nossos programas. Devido à falta de tipos em Javascript, precisamos inferi-los durante o processo de codificação. Nossa mente tem que substituir o compilador ausente, ou seja, assim que olhamos para um programa, devemos reconhecer não apenas os algoritmos, mas também os tipos subjacentes (talvez polimórficos). Esses tipos nos ajudarão a construir programas mais confiáveis ​​e robustos.

Para fazer isso corretamente, vou apresentar uma visão geral das manifestações mais comuns do polimorfismo.

Polimorfismo paramétrico (também conhecido como genérico)

O polimorfismo paramétrico diz que os diferentes tipos são intercambiáveis ​​porque os tipos não importam em absoluto. Uma função que define um ou mais parâmetros do tipo polimórfico paramétrico não deve saber nada sobre os argumentos correspondentes, mas tratá-los todos iguais, pois podem adotar qualquer tipo. Isso é bastante restritivo, porque tal função só pode funcionar com as propriedades de seus argumentos que não fazem parte de seus dados:

// 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 (também conhecido como sobrecarga)

O polimorfismo ad-hoc diz que tipos diferentes são equivalentes apenas para um propósito específico. Para ser equivalente nesse sentido, um tipo deve implementar um conjunto de funções específicas para esse propósito. Uma função que define um ou mais parâmetros do tipo polimórfico ad-hoc precisa saber quais conjuntos de funções estão associados a cada um de seus argumentos.

O polimorfismo ad-hoc torna uma função compatível com um domínio maior de tipos. O exemplo a seguir ilustra a finalidade de "mapeamento" e como os tipos podem implementar essa restrição. Em vez de um conjunto de funções, a restrição "mapeável" inclui apenas uma única mapfunção:

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

Como outras respostas já cobrem o polimorfismo de subtipo, eu pulo.

Polimorfismo estrutural (também conhecido como subtipagem estrutural)

O polimorfismo estrutural diz que tipos diferentes são equivalentes, se contiverem a mesma estrutura de tal forma, que um tipo tenha todas as propriedades do outro, mas pode incluir propriedades adicionais. Dito isso, o polimorfismo estrutural é a digitação reduzida em tempo de compilação e certamente oferece alguma segurança de tipo adicional. Mas, ao afirmar que dois valores são do mesmo tipo apenas porque compartilham algumas propriedades, ele ignora completamente o nível semântico dos valores:

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

Infelizmente, speedé considerado um subtipo de weighte assim que comparamos as valuepropriedades, estamos virtualmente comparando maçãs com laranjas.


fonte
1
Embora isso seja em muitos aspectos mais preciso (e certamente mais completo) do que a resposta aceita, não tem a mesma acessibilidade: esta resposta presume que quem fez a pergunta já é muito inteligente para se preocupar com a pergunta :)
Jared Smith
@JaredSmith Tentei resumir o assunto em alguns parágrafos fáceis de entender. Mas quanto mais fundo eu perfuro, mais complexo fica. Nunca encontrei uma boa fonte de polimorfismo em linguagens não digitadas, portanto, acho que esta resposta é valiosa.
a edição melhora muito a acessibilidade. Quanto à utilidade do polimorfismo em linguagens dinâmicas, há muitos, mas estou lutando para pensar em um bom exemplo em JS. Um exemplo melhor seriam os métodos mágicos do Python, que permitem que tipos definidos pelo usuário trabalhem com funções polimórficas como len. Ou talvez conjde clojure.
Jared Smith
8

O que é isso?

Poli = muitos, morfismo = mudança de forma ou comportamento.

porque precisamos disso?

Na programação, é usado quando queremos que a interface de uma função (digamos, função X) seja flexível o suficiente para aceitar diferentes tipos ou número de parâmetros. Além disso, com base na alteração dos tipos ou números dos parâmetros, podemos querer que a função X se comporte de maneira diferente (morfismo).

Como funciona?

Escrevemos várias implementações da função X, onde cada implementação aceita diferentes tipos de parâmetros ou número de parâmetros. Com base no tipo ou número de parâmetro, o compilador (em tempo de execução) decide qual implementação de X deve ser executada quando X é chamado de algum código.

como posso conseguir esse comportamento polimórfico em javascript?

JS não é uma linguagem digitada, portanto, não significa usar conceitos OOP como polimorfismo. No entanto, a versão mais recente do JS agora inclui classes e existe a possibilidade de que o polimosfismo também comece a fazer sentido no JS. Outras respostas fornecem algumas soluções alternativas interessantes.

Hadaytullah
fonte
3

Polimorfismo significa capacidade de chamar o mesmo método em objetos diferentes e cada objeto responde de maneira diferente é chamado de 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
fonte
2

JavaScript é uma linguagem interpretada, não uma linguagem compilada.

Polimorfismo do tempo de compilação (ou polimorfismo estático) O polimorfismo do tempo de compilação nada mais é do que a sobrecarga do método em java, c ++

Portanto, a sobrecarga de método não é possível em javascript.

Mas o polimorfismo dinâmico (tempo de execução) é o polimorfismo que existia no tempo de execução, então a substituição do método é possível em javascript

outro exemplo é o PHP.

Apoorv
fonte