javascript - primera - jquery orientado a objetos
¿Cómo heredar de una clase en javascript? (14)
Para aquellos que llegan a esta página en 2015 o después
Con la última versión del estándar ECMAScript (ES6) , puede usar de keywork extends
Tenga en cuenta que la definición de clase no es un object
regular, por lo tanto, no hay comas entre los miembros de la clase. Para crear una instancia de una clase, debe usar la palabra clave new
. Para heredar de una clase base, use extends
:
class Vehicle {
constructor(name) {
this.name = name;
this.kind = ''vehicle'';
}
getName() {
return this.name;
}
}
// Create an instance
var myVehicle = new Vehicle(''rocky'');
myVehicle.getName(); // => ''rocky''
Para heredar de una clase base, use extends
:
class Car extends Vehicle {
constructor(name) {
super(name);
this.kind = ''car''
}
}
var myCar = new Car(''bumpy'');
myCar.getName(); // => ''bumpy''
myCar instanceof Car; // => true
myCar instanceof Vehicle; // => true
Desde la clase derivada, puede usar super de cualquier constructor o método para acceder a su clase base:
- Para llamar al constructor padre, use
super().
- Para llamar a otro miembro, use, por ejemplo,
super.getName()
.
Hay más para usar clases. Si desea profundizar en el tema, recomiendo " Clases en ECMAScript 6 " por el Dr. Axel Rauschmayer. *
En PHP / Java uno puede hacer:
class Sub extends Base
{
}
Y automáticamente todos los métodos / propiedades públicos / protegidos, propiedades, campos, etc. de la clase Súper se convierten en parte de la clase Sub, que puede anularse si es necesario.
¿Cuál es el equivalente para eso en Javascript?
Bueno, en JavaScript no hay "herencia de clase", solo hay "herencia de prototipo". Entonces no haces un "camión" de clase y luego lo marcas como una subclase de "automóvil". En cambio, haces un objeto "Jack" y dices que usa "John" como prototipo. Si John sabe cuánto es "4 + 4", entonces Jack también lo sabe.
Le sugiero que lea el artículo de Douglas Crockford sobre la herencia prototípica aquí: Crockford También muestra cómo puede hacer que JavaScript tenga una herencia "similar" como en otros lenguajes de OO y luego explica que esto en realidad significa romper javaScript de una manera que no debe ser utilizada.
Desde ES2015, así es exactamente como haces herencia en JavaScript
class Sub extends Base {
}
Después de leer muchas publicaciones, se me ocurrió esta solución ( jsfiddle aquí ). La mayoría de las veces no necesito algo más sofisticado
var Class = function(definition) {
var base = definition.extend || null;
var construct = definition.construct || definition.extend || function() {};
var newClass = function() {
this._base_ = base;
construct.apply(this, arguments);
}
if (definition.name)
newClass._name_ = definition.name;
if (definition.extend) {
var f = function() {}
f.prototype = definition.extend.prototype;
newClass.prototype = new f();
newClass.prototype.constructor = newClass;
newClass._extend_ = definition.extend;
newClass._base_ = definition.extend.prototype;
}
if (definition.statics)
for (var n in definition.statics) newClass[n] = definition.statics[n];
if (definition.members)
for (var n in definition.members) newClass.prototype[n] = definition.members[n];
return newClass;
}
var Animal = Class({
construct: function() {
},
members: {
speak: function() {
console.log("nuf said");
},
isA: function() {
return "animal";
}
}
});
var Dog = Class({ extend: Animal,
construct: function(name) {
this._base_();
this.name = name;
},
statics: {
Home: "House",
Food: "Meat",
Speak: "Barks"
},
members: {
name: "",
speak: function() {
console.log( "ouaf !");
},
isA: function(advice) {
return advice + " dog -> " + Dog._base_.isA.call(this);
}
}
});
var Yorkshire = Class({ extend: Dog,
construct: function(name,gender) {
this._base_(name);
this.gender = gender;
},
members: {
speak: function() {
console.log( "ouin !");
},
isA: function(advice) {
return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice);
}
}
});
var Bulldog = function() { return _class_ = Class({ extend: Dog,
construct: function(name) {
this._base_(name);
},
members: {
speak: function() {
console.log( "OUAF !");
},
isA: function(advice) {
return "bulldog -> " + _class_._base_.isA.call(this,advice);
}
}
})}();
var animal = new Animal("Maciste");
console.log(animal.isA());
animal.speak();
var dog = new Dog("Sultan");
console.log(dog.isA("good"));
dog.speak();
var yorkshire = new Yorkshire("Golgoth","Male");
console.log(yorkshire.isA("bad"));
yorkshire.speak();
var bulldog = new Bulldog("Mike");
console.log(bulldog.isA("nice"));
bulldog.speak();
En JavaScript no tiene clases, pero puede obtener la herencia y la reutilización del comportamiento de muchas maneras:
Herencia pseudo-clásica (mediante creación de prototipos):
function Super () {
this.member1 = ''superMember1'';
}
Super.prototype.member2 = ''superMember2'';
function Sub() {
this.member3 = ''subMember3'';
//...
}
Sub.prototype = new Super();
Debería usarse con el new
operador:
var subInstance = new Sub();
Aplicación de función o "encadenamiento de constructor":
function Super () {
this.member1 = ''superMember1'';
this.member2 = ''superMember2'';
}
function Sub() {
Super.apply(this, arguments);
this.member3 = ''subMember3'';
}
Este enfoque también se debe usar con el new
operador:
var subInstance = new Sub();
La diferencia con el primer ejemplo es que cuando apply
el constructor Super
a this
objeto dentro de Sub
, agrega las propiedades asignadas a this
en Super
, directamente en la nueva instancia, por ejemplo, subInstance
contiene las propiedades member1
y member2
directamente ( subInstance.hasOwnProperty(''member1'') == true;
).
En el primer ejemplo, esas propiedades se alcanzan a través de la cadena de prototipos , existen en un objeto interno [[Prototype]]
.
Herencia parasitaria o constructores de potencia:
function createSuper() {
var obj = {
member1: ''superMember1'',
member2: ''superMember2''
};
return obj;
}
function createSub() {
var obj = createSuper();
obj.member3 = ''subMember3'';
return obj;
}
Este enfoque se basa básicamente en el "aumento de objetos", no necesita usar el new
operador, y como puede ver, this
palabra clave no está involucrada.
var subInstance = createSub();
ECMAScript 5th Ed. Método Object.create
:
// Check if native implementation available
if (typeof Object.create !== ''function'') {
Object.create = function (o) {
function F() {} // empty constructor
F.prototype = o; // set base object as prototype
return new F(); // return empty object with right [[Prototype]]
};
}
var superInstance = {
member1: ''superMember1'',
member2: ''superMember2''
};
var subInstance = Object.create(superInstance);
subInstance.member3 = ''subMember3'';
El método anterior es una técnica de herencia prototípica propuesta por Crockford .
Las instancias de objeto heredan de otras instancias de objetos, eso es todo.
Esta técnica puede ser mejor que el simple "aumento de objetos" porque las propiedades heredadas no se copian en todas las instancias de objetos nuevos, ya que el objeto base se establece como [[Prototype]]
del objeto extendido , en el ejemplo anterior subInstance
contiene físicamente solo la propiedad de member3
.
Encuentro que esta cita es la más esclarecedora:
En esencia, una "clase" de JavaScript es solo un objeto Function que sirve como un constructor más un objeto prototipo adjunto. ( Fuente: Guru Katz )
Me gusta usar constructores en lugar de objetos, por lo que soy parcial al método de "herencia pseudo-clásica" descrito aquí por CMS . Aquí hay un ejemplo de herencia múltiple con una cadena de prototipos :
// Lifeform "Class" (Constructor function, No prototype)
function Lifeform () {
this.isLifeform = true;
}
// Animal "Class" (Constructor function + prototype for inheritance)
function Animal () {
this.isAnimal = true;
}
Animal.prototype = new Lifeform();
// Mammal "Class" (Constructor function + prototype for inheritance)
function Mammal () {
this.isMammal = true;
}
Mammal.prototype = new Animal();
// Cat "Class" (Constructor function + prototype for inheritance)
function Cat (species) {
this.isCat = true;
this.species = species
}
Cat.prototype = new Mammal();
// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");
console.log(tiger);
// The console outputs a Cat object with all the properties from all "classes"
console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true
// You can see that all of these "is" properties are available in this object
// We can check to see which properties are really part of the instance object
console.log( "tiger hasOwnProperty: "
,tiger.hasOwnProperty("isLifeform") // false
,tiger.hasOwnProperty("isAnimal") // false
,tiger.hasOwnProperty("isMammal") // false
,tiger.hasOwnProperty("isCat") // true
);
// New properties can be added to the prototypes of any
// of the "classes" above and they will be usable by the instance
Lifeform.prototype.A = 1;
Animal.prototype.B = 2;
Mammal.prototype.C = 3;
Cat.prototype.D = 4;
console.log(tiger.A, tiger.B, tiger.C, tiger.D);
// Console outputs: 1 2 3 4
// Look at the instance object again
console.log(tiger);
// You''ll see it now has the "D" property
// The others are accessible but not visible (console issue?)
// In the Chrome console you should be able to drill down the __proto__ chain
// You can also look down the proto chain with Object.getPrototypeOf
// (Equivalent to tiger.__proto__)
console.log( Object.getPrototypeOf(tiger) ); // Mammal
console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal
// Etc. to get to Lifeform
Aquí hay otro buen recurso de MDN , y aquí hay un jsfiddle para que lo pruebes .
Gracias a la respuesta de CMS y después de jugar un rato con el prototipo y Object.create y lo que no, pude encontrar una solución ordenada para mi herencia usando apply como se muestra aquí:
var myNamespace = myNamespace || (function() {
return {
BaseClass: function(){
this.someBaseProperty = "someBaseProperty";
this.someProperty = "BaseClass";
this.someFunc = null;
},
DerivedClass:function(someFunc){
myNamespace.BaseClass.apply(this, arguments);
this.someFunc = someFunc;
this.someProperty = "DerivedClass";
},
MoreDerivedClass:function(someFunc){
myNamespace.DerivedClass.apply(this, arguments);
this.someFunc = someFunc;
this.someProperty = "MoreDerivedClass";
}
};
})();
He cambiado la forma en que hago esto ahora, trato de evitar el uso de funciones de constructor y su propiedad de prototype
, pero mi respuesta anterior de 2010 todavía está en la parte inferior. Ahora prefiero Object.create()
. Object.create
está disponible en todos los navegadores modernos.
Debo señalar que Object.create
suele ser mucho más lento que usar new
con un constructor de funciones.
//The prototype is just an object when you use `Object.create()`
var Base = {};
//This is how you create an instance:
var baseInstance = Object.create(Base);
//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));
//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True
Uno de los grandes beneficios de usar Object.create es poder pasar un argumento defineProperties , que le da un control significativo sobre cómo se puede acceder y enumerar las propiedades de la clase, y también uso funciones para crear instancias, estas sirven como constructores en cierto modo, ya que puede hacer la inicialización al final en lugar de simplemente devolver la instancia.
var Base = {};
function createBase() {
return Object.create(Base, {
doSomething: {
value: function () {
console.log("Doing something");
},
},
});
}
var Sub = createBase();
function createSub() {
return Object.create(Sub, {
doSomethingElse: {
value: function () {
console.log("Doing something else");
},
},
});
}
var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true
Esta es mi respuesta original de 2010:
function Base ( ) {
this.color = "blue";
}
function Sub ( ) {
}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
console.log( this.color );
}
var instance = new Sub ( );
instance.showColor( ); //"blue"
La herencia de Javascript es un poco diferente de Java y PHP, porque realmente no tiene clases. En cambio, tiene prototipos de objetos que proporcionan métodos y variables miembro. Puede encadenar esos prototipos para proporcionar herencia de objeto. El patrón más común que encontré al investigar esta pregunta se describe en Mozilla Developer Network . He actualizado su ejemplo para incluir una llamada a un método de superclase y para mostrar el registro en un mensaje de alerta:
// Shape - superclass
function Shape() {
this.x = 0;
this.y = 0;
}
// superclass method
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
log += ''Shape moved./n'';
};
// Rectangle - subclass
function Rectangle() {
Shape.call(this); // call super constructor.
}
// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;
// Override method
Rectangle.prototype.move = function(x, y) {
Shape.prototype.move.call(this, x, y); // call superclass method
log += ''Rectangle moved./n'';
}
var log = "";
var rect = new Rectangle();
log += (''Is rect an instance of Rectangle? '' + (rect instanceof Rectangle) + ''/n''); // true
log += (''Is rect an instance of Shape? '' + (rect instanceof Shape) + ''/n''); // true
rect.move(1, 1); // Outputs, ''Shape moved.''
alert(log);
Personalmente, creo que la herencia en Javascript es incómoda, pero esta es la mejor versión que he encontrado.
No puede heredar de una clase en JavaScript, porque no hay clases en JavaScript.
Puede usar la github.com/dotnetwise/Javascript-FastClass .inheritWith
y .fastClass
. Es más rápido que la mayoría de las bibliotecas populares y, a veces incluso más rápido que la versión nativa.
Muy fácil de usar:
function Super() {
this.member1 = "superMember";//instance member
}.define({ //define methods on Super''s prototype
method1: function() { console.log(''super''); } //prototype member
}.defineStatic({ //define static methods directly on Super function
staticMethod1: function() { console.log(''static method on Super''); }
});
var Sub = Super.inheritWith(function(base, baseCtor) {
return {
constructor: function() {//the Sub constructor that will be returned to variable Sub
this.member3 = ''subMember3''; //instance member on Sub
baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments
},
method1: function() {
console.log(''sub'');
base.method1.apply(this, arguments); //call the base class'' method1 function
}
}
Uso
var s = new Sub();
s.method1(); //prints:
//sub
//super
no puedes (en el sentido clásico). Javascript es un lenguaje prototípico. Observará que nunca declara una "clase" en Javascript; simplemente defines el estado y los métodos de un objeto. Para producir herencia, toma algún objeto y prototipo. El prototipo se amplía con nuevas funcionalidades.
function Base() {
this.doSomething = function () {
}
}
function Sub() {
Base.call(this); // inherit Base''s method(s) to this instance of Sub
}
var sub = new Sub();
sub.doSomething();
function Person(attr){
this.name = (attr && attr.name)? attr.name : undefined;
this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined;
this.printName = function(){
console.log(this.name);
}
this.printBirthYear = function(){
console.log(this.birthYear);
}
this.print = function(){
console.log(this.name + ''('' +this.birthYear+ '')'');
}
}
function PersonExt(attr){
Person.call(this, attr);
this.print = function(){
console.log(this.name+ ''-'' + this.birthYear);
}
this.newPrint = function(){
console.log(''New method'');
}
}
PersonExt.prototype = new Person();
// Init object and call methods
var p = new Person({name: ''Mr. A'', birthYear: 2007});
// Parent method
p.print() // Mr. A(2007)
p.printName() // Mr. A
var pExt = new PersonExt({name: ''Mr. A'', birthYear: 2007});
// Overwriten method
pExt.print() // Mr. A-2007
// Extended method
pExt.newPrint() // New method
// Parent method
pExt.printName() // Mr. A