objetos objeto ejemplos crear clases array agregar javascript prototype multiple-inheritance

objeto - MĂșltiples herencia/prototipos en JavaScript



javascript prototype constructor (14)

Llegué a un punto en el que necesito tener algún tipo de herencia múltiple rudimentaria en JavaScript. (No estoy aquí para discutir si esta es una buena idea o no, así que tenga la amabilidad de guardar esos comentarios para usted).

Solo quiero saber si alguien ha intentado esto con éxito (o no) y cómo lo hicieron.

Para reducirlo, lo que realmente necesito es poder tener un objeto capaz de heredar una propiedad de más de una cadena de prototipos (es decir, cada prototipo podría tener su propia cadena), pero en un orden de precedencia dado (lo hará). busca las cadenas para la primera definición).

Para demostrar cómo esto es teóricamente posible, se podría lograr uniendo la cadena secundaria al final de la cadena primaria, pero esto afectaría todas las instancias de cualquiera de esos prototipos anteriores y eso no es lo que quiero.

¿Pensamientos?

Editar Aprecio a las personas de las respuestas, pero aunque el consenso parece estar copiando estáticamente sobre las propiedades de ambos árboles, lo que funcionaría en la mayoría de los casos (y probablemente sea lo que termino haciendo), estaba más interesado en una solución dinámica que permite que las cadenas prototipo separadas sean alteradas, y aún así esos cambios son "recogidos" por la instancia.


Aquí hay un ejemplo de encadenamiento prototipo usando funciones de constructor :

function Lifeform () { // 1st Constructor function this.isLifeform = true; } function Animal () { // 2nd Constructor function this.isAnimal = true; } Animal.prototype = new Lifeform(); // Animal is a lifeform function Mammal () { // 3rd Constructor function this.isMammal = true; } Mammal.prototype = new Animal(); // Mammal is an animal function Cat (species) { // 4th Constructor function this.isCat = true; this.species = species } Cat.prototype = new Mammal(); // Cat is a mammal

Este concepto usa la definición de Yehuda Katz de una "clase" para JavaScript:

... una "clase" JavaScript es solo un objeto Function que sirve como un constructor más un prototipo adjunto. ( Fuente: Guru Katz )

A diferencia del enfoque Object.create , cuando las clases se crean de esta manera y queremos crear instancias de una "clase", no necesitamos saber de qué está heredando cada "clase". Solo usamos new .

// Make an instance object of the Cat "Class" var tiger = new Cat("tiger"); console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform); // Outputs: true true true true

El orden de la precedencia debe tener sentido. Primero mira en el objeto instancia, luego es prototipo, luego el siguiente prototipo, etc.

// Let''s say we have another instance, a special alien cat var alienCat = new Cat("alien"); // We can define a property for the instance object and that will take // precendence over the value in the Mammal class (down the chain) alienCat.isMammal = false; // OR maybe all cats are mutated to be non-mammals Cat.prototype.isMammal = false; console.log(alienCat);

También podemos modificar los prototipos que afectarán a todos los objetos creados en la clase.

// All cats are mutated to be non-mammals Cat.prototype.isMammal = false; console.log(tiger, alienCat);

Originalmente escribí algo de esto con esta respuesta .


Creo que es ridículamente simple. El problema aquí es que la clase infantil solo se referirá a instanceof para la primera clase que llame

https://jsfiddle.net/1033xzyt/19/

function Foo() { this.bar = ''bar''; return this; } Foo.prototype.test = function(){return 1;} function Bar() { this.bro = ''bro''; return this; } Bar.prototype.test2 = function(){return 2;} function Cool() { Foo.call(this); Bar.call(this); return this; } var combine = Object.create(Foo.prototype); $.extend(combine, Object.create(Bar.prototype)); Cool.prototype = Object.create(combine); Cool.prototype.constructor = Cool; var cool = new Cool(); console.log(cool.test()); // 1 console.log(cool.test2()); //2 console.log(cool.bro) //bro console.log(cool.bar) //bar console.log(cool instanceof Foo); //true console.log(cool instanceof Bar); //false


Eche un vistazo al paquete IeUnit .

La asimilación de conceptos implementada en IeUnit parece ofrecer lo que está buscando de una manera bastante dinámica.


Es posible implementar herencia múltiple en JavaScript, aunque muy pocas bibliotecas lo hacen.

Podría señalar a Ring.js , el único ejemplo que conozco.


Este usa Object.create para hacer una verdadera cadena de prototipos:

function makeChain(chains) { var c = Object.prototype; while(chains.length) { c = Object.create(c); $.extend(c, chains.pop()); // some function that does mixin } return c; }

Por ejemplo:

var obj = makeChain([{a:1}, {a: 2, b: 3}, {c: 4}]);

volverá:

a: 1 a: 2 b: 3 c: 4 <Object.prototype stuff>

de modo que obj.a === 1 , obj.b === 3 , etc.


Estuve trabajando mucho en esto hoy e intento lograrlo en ES6. La forma en que lo hice fue usando Browserify, Babel y luego lo probé con Wallaby y pareció funcionar. Mi objetivo es ampliar la matriz actual, incluir ES6, ES7 y agregar algunas características personalizadas adicionales que necesito en el prototipo para manejar datos de audio.

Wallaby pasa 4 de mis pruebas. El archivo example.js se puede pegar en la consola y se puede ver que la propiedad ''includes'' está en el prototipo de la clase. Todavía quiero probar esto más mañana.

Este es mi método: (lo más probable es que refactorice y vuelva a empaquetar como un módulo después de dormir un poco).

var includes = require(''./polyfills/includes''); var keys = Object.getOwnPropertyNames(includes.prototype); keys.shift(); class ArrayIncludesPollyfills extends Array {} function inherit (...keys) { keys.map(function(key){ ArrayIncludesPollyfills.prototype[key]= includes.prototype[key]; }); } inherit(keys); module.exports = ArrayIncludesPollyfills

Github Repo: https://github.com/danieldram/array-includes-polyfill


Herencia múltiple [editar, no herencia propia de tipo, sino de propiedades; mixins] en Javascript es bastante sencillo si utiliza prototipos construidos en lugar de objetos genéricos. Aquí hay dos clases principales para heredar de:

function FoodPrototype() { this.eat = function () { console.log("Eating", this.name); }; } function Food(name) { this.name = name; } Food.prototype = new FoodPrototype(); function PlantPrototype() { this.grow = function () { console.log("Growing", this.name); }; } function Plant(name) { this.name = name; } Plant.prototype = new PlantPrototype();

Tenga en cuenta que he usado el mismo miembro de "nombre" en cada caso, lo que podría ser un problema si los padres no estaban de acuerdo sobre cómo se debe manejar el "nombre". Pero son compatibles (redundante, realmente) en este caso.

Ahora solo necesitamos una clase que herede de ambos. La herencia se realiza llamando a la función de constructor (sin usar la nueva palabra clave) para los prototipos y los constructores de objetos. Primero, el prototipo debe heredar de los prototipos originales

function FoodPlantPrototype() { FoodPrototype.call(this); PlantPrototype.call(this); // plus a function of its own this.harvest = function () { console.log("harvest at", this.maturity); }; }

Y el constructor tiene que heredar de los constructores padres:

function FoodPlant(name, maturity) { Food.call(this, name); Plant.call(this, name); // plus a property of its own this.maturity = maturity; } FoodPlant.prototype = new FoodPlantPrototype();

Ahora puedes cultivar, comer y cosechar diferentes instancias:

var fp1 = new FoodPlant(''Radish'', 28); var fp2 = new FoodPlant(''Corn'', 90); fp1.grow(); fp2.grow(); fp1.harvest(); fp1.eat(); fp2.harvest(); fp2.eat();


Me gusta la implementación de la estructura de clases de John Resig: http://ejohn.org/blog/simple-javascript-inheritance/

Esto puede simplemente extenderse a algo como:

Class.extend = function(prop /*, prop, prop, prop */) { for( var i=1, l=arguments.length; i<l; i++ ){ prop = $.extend( prop, arguments[i] ); } // same code }

que le permitirá pasar múltiples objetos de los cuales heredar. Vas a perder la capacidad de instanceOf aquí, pero eso es un hecho si quieres herencia múltiple.

mi ejemplo bastante intrincado de lo anterior está disponible en https://github.com/cwolves/Fetch/blob/master/support/plugins/klass/klass.js

Tenga en cuenta que hay algún código muerto en ese archivo, pero permite la herencia múltiple si desea echar un vistazo.

Si desea herencia encadenada (NO herencia múltiple, pero para la mayoría de las personas es lo mismo), se puede lograr con Class como:

var newClass = Class.extend( cls1 ).extend( cls2 ).extend( cls3 )

que preservará la cadena de prototipos original, pero también tendrá un montón de código sin sentido en ejecución.


No se confunda con las implementaciones de frameworks de JavaScript de herencia múltiple.

Todo lo que necesita hacer es usar Object.create() para crear un nuevo objeto cada vez con el objeto y las propiedades prototipo especificados, luego asegúrese de cambiar Object.prototype.constructor cada paso del camino si planea crear una instancia de B en el futuro.

Para heredar las propiedades de instancia thisA y thisB utilizamos Function.prototype.call() al final de cada función de objeto. Esto es opcional si solo te importa heredar el prototipo.

Ejecute el siguiente código en alguna parte y observe objC :

function A() { this.thisA = 4; // objC will contain this property } A.prototype.a = 2; // objC will contain this property B.prototype = Object.create(A.prototype); B.prototype.constructor = B; function B() { this.thisB = 55; // objC will contain this property A.call(this); } B.prototype.b = 3; // objC will contain this property C.prototype = Object.create(B.prototype); C.prototype.constructor = C; function C() { this.thisC = 123; // objC will contain this property B.call(this); } C.prototype.c = 2; // objC will contain this property var objC = new C();

  • B hereda el prototipo de A
  • C hereda el prototipo de B
  • objC es una instancia de C

Esta es una buena explicación de los pasos anteriores:

OOP en JavaScript: lo que NECESITA saber


No soy de ninguna manera un experto en javascript OOP, pero si te entiendo correctamente quieres algo como (pseudo-código):

Earth.shape = ''round''; Animal.shape = ''random''; Cat inherit from (Earth, Animal); Cat.shape = ''random'' or ''round'' depending on inheritance order;

En ese caso, probaría algo como:

var Earth = function(){}; Earth.prototype.shape = ''round''; var Animal = function(){}; Animal.prototype.shape = ''random''; Animal.prototype.head = true; var Cat = function(){}; MultiInherit(Cat, Earth, Animal); console.log(new Cat().shape); // yields "round", since I reversed the inheritance order console.log(new Cat().head); // true function MultiInherit() { var c = [].shift.call(arguments), len = arguments.length while(len--) { $.extend(c.prototype, new arguments[len]()); } }


Se puede lograr la herencia múltiple en ECMAScript 6 mediante el uso de objetos Proxy .

Implementación

function getDesc (obj, prop) { var desc = Object.getOwnPropertyDescriptor(obj, prop); return desc || (obj=Object.getPrototypeOf(obj) ? getDesc(obj, prop) : void 0); } function multiInherit (...protos) { return Object.create(new Proxy(Object.create(null), { has: (target, prop) => protos.some(obj => prop in obj), get (target, prop, receiver) { var obj = protos.find(obj => prop in obj); return obj ? Reflect.get(obj, prop, receiver) : void 0; }, set (target, prop, value, receiver) { var obj = protos.find(obj => prop in obj); return Reflect.set(obj || Object.create(null), prop, value, receiver); }, *enumerate (target) { yield* this.ownKeys(target); }, ownKeys(target) { var hash = Object.create(null); for(var obj of protos) for(var p in obj) if(!hash[p]) hash[p] = true; return Object.getOwnPropertyNames(hash); }, getOwnPropertyDescriptor(target, prop) { var obj = protos.find(obj => prop in obj); var desc = obj ? getDesc(obj, prop) : void 0; if(desc) desc.configurable = true; return desc; }, preventExtensions: (target) => false, defineProperty: (target, prop, desc) => false, })); }

Explicación

Un objeto proxy consiste en un objeto objetivo y algunas trampas, que definen el comportamiento personalizado para las operaciones fundamentales.

Al crear un objeto que hereda de otro, usamos Object.create(obj) . Pero en este caso queremos herencia múltiple, así que en lugar de obj uso un proxy que redireccionará las operaciones fundamentales al objeto apropiado.

Yo uso estas trampas:

  • The has trap es una trampa para el operador in . Uso some para verificar si al menos un prototipo contiene la propiedad.
  • La trampa de get es una trampa para obtener valores de propiedad. Utilizo find para encontrar el primer prototipo que contiene esa propiedad, y devuelvo el valor, o llamo al getter en el receptor apropiado. Esto es manejado por Reflect.get . Si ningún prototipo contiene la propiedad, devuelvo undefined .
  • La trampa set es una trampa para establecer valores de propiedad. Utilizo find para encontrar el primer prototipo que contiene esa propiedad, y llamo a su setter en el receptor apropiado. Si no hay incubadora o ningún prototipo contiene la propiedad, el valor se define en el receptor apropiado. Esto es manejado por Reflect.set .
  • La trampa de enumerate es una trampa for...in loops . Intento iterar las propiedades enumerables del primer prototipo, luego del segundo, y así sucesivamente. Una vez que se ha iterado una propiedad, la almaceno en una tabla hash para evitar repetirla nuevamente.
    Advertencia : esta trampa se ha eliminado en el borrador de ES7 y está en desuso en los navegadores.
  • La trampa ownKeys es una trampa para Object.getOwnPropertyNames() . Desde ES7, for...in loops sigue llamando a [[GetPrototypeOf]] y obteniendo las propiedades propias de cada uno. Entonces, para hacer que itere las propiedades de todos los prototipos, uso esta trampa para hacer que todas las propiedades heredadas enumerables aparezcan como propiedades propias.
  • La trampa getOwnPropertyDescriptor es una trampa para Object.getOwnPropertyDescriptor() . Hacer que todas las propiedades enumerables aparezcan como propiedades propias en la trampa de ownKeys no es suficiente, for...in bucles se obtendrá el descriptor para verificar si son enumerables. Así que uso find para encontrar el primer prototipo que contiene esa propiedad, e itero su cadena prototípica hasta que encuentre el dueño de la propiedad, y devuelvo su descriptor. Si ningún prototipo contiene la propiedad, devuelvo undefined . El descriptor se modifica para hacerlo configurable, de lo contrario podríamos romper algunas invariantes de proxy.
  • Las preventExtensions y defineProperty solo se incluyen para evitar que estas operaciones modifiquen el destino del proxy. De lo contrario, podríamos terminar rompiendo algunas invariantes proxy.

Hay más trampas disponibles, que no uso

  • Se podría agregar la trampa getPrototypeOf , pero no hay una forma adecuada de devolver los múltiples prototipos. Esto implica que instanceof no funcionará. Por lo tanto, dejo que obtenga el prototipo del objetivo, que inicialmente es nulo.
  • La trampa setPrototypeOf podría agregarse y aceptar una matriz de objetos, que reemplazaría a los prototipos. Esto se deja como un ejercicio para el lector. Aquí solo dejo que modifique el prototipo del objetivo, que no es muy útil porque ninguna trampa usa el objetivo.
  • La trampa deleteProperty es una trampa para eliminar propiedades propias. El proxy representa la herencia, por lo que no tendría mucho sentido. Dejo que intente la eliminación en el objetivo, que no debería tener ninguna propiedad de todos modos.
  • La trampa isExtensible es una trampa para obtener la extensibilidad. No es muy útil, dado que un invariante lo obliga a devolver la misma extensibilidad que el objetivo. Así que simplemente dejo que redirija la operación al objetivo, que será extensible.
  • Las trampas de apply y construct son trampas para invocar o crear instancias. Solo son útiles cuando el objetivo es una función o un constructor.

Ejemplo

// Creating objects var o1, o2, o3, obj = multiInherit(o1={a:1}, o2={b:2}, o3={a:3, b:3}); // Checking property existences ''a'' in obj; // true (inherited from o1) ''b'' in obj; // true (inherited from o2) ''c'' in obj; // false (not found) // Setting properties obj.c = 3; // Reading properties obj.a; // 1 (inherited from o1) obj.b; // 2 (inherited from o2) obj.c; // 3 (own property) obj.d; // undefined (not found) // The inheritance is "live" obj.a; // 1 (inherited from o1) delete o1.a; obj.a; // 3 (inherited from o3) // Property enumeration for(var p in obj) p; // "c", "b", "a"


Un recién llegado en la escena es SimpleDeclare . Sin embargo, cuando se trata de herencia múltiple, aún se obtendrán copias de los constructores originales. Esa es una necesidad en Javascript ...

Merc.


Yo usaría ds.oop . Es similar a prototype.js y otros. hace que la herencia múltiple sea muy fácil y minimalista. (solo 2 o 3 kb) También es compatible con algunas características interesantes, como interfaces e inyección de dependencias

/*** multiple inheritance example ***********************************/ var Runner = ds.class({ run: function() { console.log(''I am running...''); } }); var Walker = ds.class({ walk: function() { console.log(''I am walking...''); } }); var Person = ds.class({ inherits: [Runner, Walker], eat: function() { console.log(''I am eating...''); } }); var person = new Person(); person.run(); person.walk(); person.eat();


Mixins se puede utilizar en javascript para lograr el mismo objetivo que probablemente desee resolver a través de herencia múltiple en este momento.