Ambos os exemplos de código que você demonstrou em sua pergunta fazem uso da herança prototípica. Na verdade, qualquer código orientado a objetos que você escreve em JavaScript é um paradigma de herança prototípica. JavaScript simplesmente não tem herança clássica. Isso deve esclarecer um pouco as coisas:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
Como você pode ver, a herança prototípica e a herança clássica são dois paradigmas diferentes de herança. Algumas linguagens como Self, Lua e JavaScript suportam herança prototípica. No entanto, a maioria das linguagens como C ++, Java e C # suportam herança clássica.
Uma rápida visão geral da programação orientada a objetos
Tanto a herança prototípica quanto a herança clássica são paradigmas de programação orientada a objetos (ou seja, eles lidam com objetos). Objetos são simplesmente abstrações que encapsulam as propriedades de uma entidade do mundo real (ou seja, eles representam coisas reais no programa). Isso é conhecido como abstração.
Abstração: A representação de coisas do mundo real em programas de computador.
Teoricamente, uma abstração é definida como "um conceito geral formado pela extração de características comuns de exemplos específicos". No entanto, por causa desta explicação, vamos usar a definição acima mencionada.
Agora, alguns objetos têm muitas coisas em comum. Por exemplo, uma bicicleta de lama e uma Harley Davidson têm muito em comum.
Uma bicicleta de lama:
Uma Harley Davidson:
Uma bicicleta de lama e uma Harley Davidson são duas bicicletas. Portanto, uma bicicleta é uma generalização tanto de uma bicicleta de lama quanto de uma Harley Davidson.
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
No exemplo acima, a bicicleta, a bicicleta de lama e a Harley Davidson são todas abstrações. No entanto, a bicicleta é uma abstração mais geral da bicicleta de lama e da Harley Davidson (ou seja, a bicicleta de lama e a Harley Davidson são tipos específicos de bicicletas).
Generalização: uma abstração de uma abstração mais específica.
Na programação orientada a objetos, criamos objetos (que são abstrações de entidades do mundo real) e usamos classes ou protótipos para criar generalizações desses objetos. As generalizações são criadas por meio de herança. Uma bicicleta é uma generalização de uma bicicleta de lama. Conseqüentemente, as bicicletas de lama são herdadas das bicicletas.
Programação Clássica Orientada a Objetos
Na programação clássica orientada a objetos, temos dois tipos de abstrações: classes e objetos. Um objeto, como mencionado antes, é uma abstração de uma entidade do mundo real. Por outro lado, uma classe é uma abstração de um objeto ou outra classe (ou seja, é uma generalização). Por exemplo, considere:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | Man | Class of object johnDoe. |
| 3 | Human | Superclass of class Man. |
+----------------------+----------------+---------------------------------------+
Como você pode ver em linguagens de programação orientadas a objetos clássicas, os objetos são apenas abstrações (ou seja, todos os objetos têm um nível de abstração 1) e as classes são apenas generalizações (ou seja, todas as classes têm um nível de abstração maior que 1).
Objetos em linguagens de programação orientadas a objetos clássicas só podem ser criados instanciando classes:
class Human {
// ...
}
class Man extends Human {
// ...
}
Man johnDoe = new Man();
Em suma, nas linguagens de programação orientadas a objetos clássicas, os objetos são abstrações de entidades do mundo real e as classes são generalizações (ou seja, abstrações de objetos ou de outras classes).
Portanto, à medida que o nível de abstração aumenta, as entidades se tornam mais gerais e, à medida que o nível de abstração diminui, as entidades se tornam mais específicas. Nesse sentido, o nível de abstração é análogo a uma escala que varia de entidades mais específicas a entidades mais gerais.
Programação Prototípica Orientada a Objetos
Linguagens de programação orientadas a objetos prototípicas são muito mais simples do que as linguagens de programação orientadas a objetos clássicas porque na programação orientada a objetos prototípica temos apenas um tipo de abstração (ou seja, objetos). Por exemplo, considere:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | man | Prototype of object johnDoe. |
| 3 | human | Prototype of object man. |
+----------------------+----------------+---------------------------------------+
Como você pode ver em linguagens de programação orientadas a objetos prototípicas, os objetos são abstrações de entidades do mundo real (nesse caso, são simplesmente chamados de objetos) ou de outros objetos (nesse caso, são chamados de protótipos dos objetos que abstraem). Portanto, um protótipo é uma generalização.
Objetos em linguagens de programação orientadas a objetos prototípicas podem ser criados ex-nihilo (ou seja, do nada) ou a partir de outro objeto (que se torna o protótipo do objeto recém-criado):
var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
Na minha humilde opinião, as linguagens de programação orientadas a objetos prototípicas são mais poderosas do que as linguagens de programação orientadas a objetos clássicas porque:
- Existe apenas um tipo de abstração.
- As generalizações são simplesmente objetos.
Você já deve ter percebido a diferença entre a herança clássica e a herança prototípica. A herança clássica é limitada a classes que herdam de outras classes. No entanto, a herança prototípica inclui não apenas protótipos herdados de outros protótipos, mas também objetos herdados de protótipos.
Isomorfismo de classe de protótipo
Você deve ter notado que protótipos e classes são muito semelhantes. Isso é verdade. Eles são. Na verdade, eles são tão semelhantes que você pode usar protótipos para modelar classes:
function CLASS(base, body) {
if (arguments.length < 2) body = base, base = Object.prototype;
var prototype = Object.create(base, {new: {value: create}});
return body.call(prototype, base), prototype;
function create() {
var self = Object.create(prototype);
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
}
}
Usando a CLASS
função acima , você pode criar protótipos que se parecem com classes:
var Human = CLASS(function () {
var milliseconds = 1
, seconds = 1000 * milliseconds
, minutes = 60 * seconds
, hours = 60 * minutes
, days = 24 * hours
, years = 365.2425 * days;
this.constructor = function (name, sex, dob) {
this.name = name;
this.sex = sex;
this.dob = dob;
};
this.age = function () {
return Math.floor((new Date - this.dob) / years);
};
});
var Man = CLASS(Human, function (Human) {
this.constructor = function (name, dob) {
Human.constructor.call(this, name, "male", dob);
if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
};
});
var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));
No entanto, o inverso não é verdadeiro (ou seja, você não pode usar classes para modelar protótipos). Isso ocorre porque os protótipos são objetos, mas as classes não são objetos. Eles são um tipo totalmente diferente de abstração.
Conclusão
Em suma, aprendemos que uma abstração é um "conceito geral formado pela extração de características comuns de exemplos específicos" e que a generalização é "uma abstração de uma abstração mais específica" . Também aprendemos sobre as diferenças entre herança prototípica e clássica e como ambas são duas faces da mesma moeda.
Em uma nota de despedida, gostaria de observar que existem dois padrões de herança prototípica: o padrão prototípico e o padrão construtor. O padrão prototípico é o padrão canônico de herança prototípica, enquanto o padrão do construtor é usado para fazer a herança prototípica parecer mais com a herança clássica. Pessoalmente, prefiro o padrão prototípico.
PS Sou o cara que escreveu a postagem no blog " Por que a herança prototípica é importante " e respondeu à pergunta " Benefícios da herança prototípica em relação à clássica? ". Minha resposta é a resposta aceita.