create - Buen ejemplo de la herencia basada en prototipos de JavaScript
object.create javascript (10)
Como se mencionó, las películas de Douglas Crockford dan una buena explicación sobre el por qué y cubre el cómo. Pero para ponerlo en un par de líneas de JavaScript:
// Declaring our Animal object
var Animal = function () {
this.name = ''unknown'';
this.getName = function () {
return this.name;
}
return this;
};
// Declaring our Dog object
var Dog = function () {
// A private variable here
var private = 42;
// overriding the name
this.name = "Bello";
// Implementing ".bark()"
this.bark = function () {
return ''MEOW'';
}
return this;
};
// Dog extends animal
Dog.prototype = new Animal();
// -- Done declaring --
// Creating an instance of Dog.
var dog = new Dog();
// Proving our case
console.log(
"Is dog an instance of Dog? ", dog instanceof Dog, "/n",
"Is dog an instance of Animal? ", dog instanceof Animal, "/n",
dog.bark() +"/n", // Should be: "MEOW"
dog.getName() +"/n", // Should be: "Bello"
dog.private +"/n" // Should be: ''undefined''
);
El problema con este enfoque, sin embargo, es que volverá a crear el objeto cada vez que crees uno. Otro enfoque es declarar sus objetos en la pila del prototipo, de esta manera:
// Defining test one, prototypal
var testOne = function () {};
testOne.prototype = (function () {
var me = {}, privateVariable = 42;
me.someMethod = function () {
return privateVariable;
};
me.publicVariable = "foo bar";
me.anotherMethod = function () {
return this.publicVariable;
};
return me;
}());
// Defining test two, function
var testTwo = ​function() {
var me = {}, privateVariable = 42;
me.someMethod = function () {
return privateVariable;
};
me.publicVariable = "foo bar";
me.anotherMethod = function () {
return this.publicVariable;
};
return me;
};
// Proving that both techniques are functionally identical
var resultTestOne = new testOne(),
resultTestTwo = new testTwo();
console.log(
resultTestOne.someMethod(), // Should print 42
resultTestOne.publicVariable // Should print "foo bar"
);
console.log(
resultTestTwo.someMethod(), // Should print 42
resultTestTwo.publicVariable // Should print "foo bar"
);
// Performance benchmark start
var stop, start, loopCount = 1000000;
// Running testOne
start = (new Date()).getTime();
for (var i = loopCount; i>0; i--) {
new testOne();
}
stop = (new Date()).getTime();
console.log(''Test one took: ''+ Math.round(((stop/1000) - (start/1000))*1000) +'' milliseconds'');
// Running testTwo
start = (new Date()).getTime();
for (var i = loopCount; i>0; i--) {
new testTwo();
}
stop = (new Date()).getTime();
console.log(''Test two took: ''+ Math.round(((stop/1000) - (start/1000))*1000) +'' milliseconds'');
Hay una ligera desventaja cuando se trata de introspección. El dumping de TestOne dará como resultado información menos útil. También la propiedad privada "privateVariable" en "testOne" se comparte en todas las instancias, como se menciona amablemente en las respuestas de shesek.
He estado programando con lenguajes OOP durante más de 10 años, pero ahora estoy aprendiendo JavaScript y es la primera vez que encuentro herencia basada en prototipos. Tiendo a aprender más rápido estudiando un buen código. ¿Qué es un ejemplo bien escrito de una aplicación de JavaScript (o biblioteca) que utiliza correctamente la herencia prototípica? ¿Y puede describir (brevemente) cómo / dónde se usa la herencia prototípica, entonces sé por dónde empezar a leer?
Douglas Crockford tiene una buena página en JavaScript Prototypal Herencia :
Hace cinco años escribí Herencia clásica en JavaScript. Mostró que JavaScript es un lenguaje prototípico libre de clases, y que tiene suficiente poder expresivo para simular un sistema clásico. Mi estilo de programación ha evolucionado desde entonces, como cualquier buen programador debería. Aprendí a abrazar por completo el prototipalme y me liberé de los confines del modelo clásico.
La Base.js Dean Edward, la Clase de Mootools o las obras de Herencia simple de John Resig son formas de hacer herencia clásica en JavaScript.
Este es el ejemplo más claro que he encontrado, del libro Nodo de Mixu ( http://book.mixu.net/node/ch6.html ):
Yo prefiero la composición sobre la herencia:
Composición: la funcionalidad de un objeto se compone de un agregado de diferentes clases al contener instancias de otros objetos. Herencia: la funcionalidad de un objeto se compone de su propia funcionalidad más funcionalidad de sus clases principales. Si debe tener herencia, use JS antiguo normal
Si debe implementar la herencia, al menos evite usar otra función de implementación / magia no estándar. Aquí se explica cómo puede implementar un facsímil de herencia razonable en ES3 puro (siempre que siga la regla de nunca definir propiedades en prototipos):
function Animal(name) { this.name = name; }; Animal.prototype.move = function(meters) { console.log(this.name+" moved "+meters+"m."); }; function Snake() { Animal.apply(this, Array.prototype.slice.call(arguments)); }; Snake.prototype = new Animal(); Snake.prototype.move = function() { console.log("Slithering..."); Animal.prototype.move.call(this, 5); }; var sam = new Snake("Sammy the Python"); sam.move();
Esto no es lo mismo que la herencia clásica, pero es un Javascript estándar y comprensible, y tiene la funcionalidad que la gente más busca: constructores encadenables y la capacidad de invocar métodos de la superclase.
Hay un fragmento de herencia basada en el prototipo de JavaScript con implementaciones específicas de la versión de ECMAScript. Elegirá automáticamente qué usar entre las implementaciones ES6, ES5 y ES3 de acuerdo con el tiempo de ejecución actual.
Los mejores ejemplos que he visto están en JavaScript de Douglas Crockford : The Good Parts . Definitivamente vale la pena comprar para ayudarlo a obtener una vista equilibrada del idioma.
Douglas Crockford es responsable del formato JSON y trabaja en Yahoo como un gurú de JavaScript.
Me gustaría echar un vistazo a YUI , y en la biblioteca de la Base
Dean Edward: http://dean.edwards.name/weblog/2006/03/base/
Para YUI puedes echar un rápido vistazo al módulo lang , esp. el método YAHOO.lang.extend. Y luego, puede explorar la fuente de algunos widgets o utilidades y ver cómo usan ese método.
Sugiero mirar PrototypeJS ''Class.create:
Línea 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js
También está la biblioteca ASP.NET Ajax de Microsoft, http://www.asp.net/ajax/ .
También hay muchos buenos artículos de MSDN, que incluyen Creación de aplicaciones web avanzadas con técnicas orientadas a objetos .
class
ES6 y se extends
La class
ES6 y las extends
son solo azúcares de sintaxis para la manipulación de cadenas de prototipos previamente posibles, y posiblemente la configuración más canónica.
Primero aprenda más sobre la cadena de prototipos y .
búsqueda de propiedades en: https://.com/a/23877420/895245
Ahora vamos a deconstruir lo que sucede:
class C {
constructor(i) {
this.i = i
}
inc() {
return this.i + 1
}
}
class D extends C {
constructor(i) {
super(i)
}
inc2() {
return this.i + 2
}
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// https://.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
Diagrama simplificado sin todos los objetos predefinidos:
__proto__
(C)<---------------(D) (d)
| | | |
| | | |
| |prototype |prototype |__proto__
| | | |
| | | |
| | | +---------+
| | | |
| | | |
| | v v
|__proto__ (D.prototype)
| | |
| | |
| | |__proto__
| | |
| | |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
function Shape(x, y) {
this.x = x;
this.y = y;
}
// 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver
function Circle(x, y, r) {
Shape.call(this, x, y);
this.r = r;
}
// 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor
Circle.prototype = Object.create(Shape.prototype);