ejemplos clases javascript oop prototype prototypal-inheritance

clases - ¿Herencia de JavaScript con prototipos-propiedad ''constructor''?



javascript prototype constructor (4)

Esta es mi solución personal, que he desarrollado a partir de los nuggets de sabiduría combinados de @thefourtheye, @FelixKling, @SeanKinsey e incluso las travesuras de @ helly0d:


La solución más simple:

/** Food Class -- You can bite all foods **/ function Food(){ this.bites = 0 }; Food.prototype.bite = function(){ console.log("Yum!"); return this.bites += 1 }; /** All Foods inherit from basicFood **/ var basicFood = new Food(); /** Bread inherits from basicFood, and can go stale **/ function Bread(){ Food.apply(this); // running food''s constructor (defines bites) this.stale = false; }; Bread.prototype = Object.create( basicFood ); Bread.prototype.constructor = Bread; // just conventional Bread.prototype.goStale = function(){ return this.stale = true }; /** Sushi inherits from basicFood, and can be cooked **/ function Sushi(){ Food.apply(this); this.raw = true; }; Sushi.prototype = Object.create( basicFood ); Sushi.prototype.constructor = Sushi; Sushi.prototype.cook = function(){ return this.raw = false };

Metodología avanzada:

Es mejor porque hace que la propiedad prototipo del constructor no sea enumerable.

/** My handy-dandy extend().to() function **/ function extend(source){ return {to:function(Constructor){ Constructor.prototype = Object.create(source); Object.defineProperty(Constructor.prototype, ''constructor'', { enumerable: false, configurable: false, writable: false, value: Constructor }); return Constructor; }} }; function Food(){ this.bites = 0 }; Food.prototype.bite = function(){ console.log("Yum!"); return this.bites += 1 }; var basicFood = new Food(); var Bread = extend(basicFood).to(function Bread(){ Food.apply(this); this.stale = false; }); Bread.prototype.goStale = function(){ return this.stale = true }; var Sushi = extend(basicFood).to(function Sushi(){ Food.apply(this); this.raw = true; }); Sushi.prototype.cook = function(){ return this.raw = false };

Las dos metodologías anteriores dan los mismos resultados de prueba:

var food = new Food(); var bread = new Bread(); var sushi = new Sushi(); console.log( food instanceof Food ); // true console.log( food instanceof Bread ); // false console.log( food instanceof Sushi ); // false console.log( bread instanceof Food ); // true console.log( bread instanceof Bread ); // true console.log( bread instanceof Sushi ); // false console.log( sushi instanceof Food ); // true console.log( sushi instanceof Bread ); // false console.log( sushi instanceof Sushi ); // true console.log( food.constructor ); // Food console.log( bread.constructor ); // Bread console.log( sushi.constructor ); // Sushi

Un agradecimiento muy especial a @FelixKling, cuya experiencia me ayudó a comprender mejor el chat fuera de este hilo, también a @thefourtheye, quien fue el primero en mostrarme la forma correcta, y también a @SeanKinsey, quien destacó la utilidad de poder ejecutar el constructor padre en el contexto de los hijos.

Esta comunidad ha respondido esta respuesta. Por favor, avíseme o edíteme si encuentra algo en el código de esta respuesta que sea sospechoso :)

He visto muchas cosas como esta y estoy buscando la solución adecuada para la herencia básica de JavaScript:

function Food(){} // Food constructor (class) function Bread(){} // Bread constructor (class) var basicFood = new Food(); // Food classes will inherit from this basicFood instance. Bread.prototype = basicFood; // Bread now inherits from Food. var bread = new Bread(); // We create some bread! bread.constructor == Food; // Though, now we feel very uneasy about how // the constructor is wrong, Bread.prototype.constructor = Bread; // So we explicitly set the prototype''s constructor bread = new Bread(); // and when we create our new bread, bread.constructor == Bread; // we feel much better as the constructor appears correct. // The issue? Suppose we have another food item, // like in a real inheritance situation: function Sushi(){}; // We might be Sushi.prototype = basicFood; // tempted to do Sushi.prototype.constructor = Sushi; // the same thing var sushi = new Sushi(); // again sushi.constructor == Sushi; // Before we realize bread.constructor == Sushi; // that we''ve ruined our bread. basicFood.constructor != Food; // More importantly, we''ve really ruined all our basicFood, // because while it''s a prototype, // it''s also an object in its own right, // and deserves an accurate constructor property.

¿Quién se supone que es realmente el constructor ?

¿Y el constructor tiene algo que ver con los resultados de instanceof ?

Me pregunto, ¿qué es lo correcto? Entiendo que muchos optarían por darle a cada clase de alimentos (pan, sushi, etc.) una nueva instancia de Alimentos, en lugar de darles a todos la misma instancia de BasicFood. Quiero esta solución más óptima (no hacer instancias innecesarias).

Teniendo en cuenta nuestra comida, pan, sushi y alimentos básicos:

function Food(){} function Bread(){} function Sushi(){} var basicFood = new Food();

Pensé que podría crear un ayudante de creación de instancias, que definiría un "constructor" de propiedad no configurable y no enumerable no enumerable en la nueva instancia:

Bread.prototype = basicFood; // We still simply inherit from basicFood Sushi.prototype = basicFood; // But we use this helper function when we make instances function reconstructify(target, Constructor){ return Object.defineProperty(target, ''constructor'', { enumerable: false, configurable: false, writable: false, value: Constructor }); } var bread = reconstructify(new Bread(), Bread); // Like so var sushi = reconstructify(new Sushi(), Sushi);

Al probar esto, me di cuenta de que instanceof no se está comportando de la manera que pensé:

// True expressions for testing -- all good basicFood.constructor == Food; bread.constructor == Bread; sushi.constructor == Sushi; basicFood instanceof Food; // good also bread instanceof Food; sushi instanceof Food; sushi instanceof Bread; // uh oh, not so good that this is true bread instanceof Sushi; // why is this?

Si lo analizo más, parece que no puedo ponerme a trabajar de la manera que supongo:

function Food(){} function Bread(){} function Sushi(){} var basicFood = new Food(); Bread.prototype = basicFood; Sushi.prototype = basicFood; var bread = new Bread(); var sushi = new Sushi(); sushi instanceof Bread; // why true? bread instanceof Sushi; // why true?

Quiero que el bread y el sushi sean ejemplos de comida, no uno del otro.

¿Cómo puedo obtener la herencia de JavaScript al mismo tiempo que mantengo el comportamiento esperado para la propiedad del constructor , así como para el operador instanceof ?


Lo que está haciendo mal es reutilizar el objeto basicFood para varias "clases" secundarias. En su lugar, una nueva nueva. De esa manera, a medida que agrega miembros al prototipo (nueva instancia del padre), lo agrega a una instancia que no se comparte entre otras clases hereditarias.

Ahora, hay una cosa que le falta a su código, y es a los constructores sin efectos secundarios. Muchos constructores requieren argumentos y se lanzarán sin ellos, pero ¿cómo se puede construir un prototipo para una nueva clase descendente sin crear un nuevo padre? Bueno, en realidad no estamos interesados ​​en la función principal, solo en el prototipo de los padres. Entonces lo que puedes hacer es

function Parent() { /*some side effect or invariant */ } Parent.prototype.foo = ... function Child() { Parent.call(this); } // the next few lines typically go into a utility function function F() {} // a throw-away constructor F.prototype = Parent.prototype; // borrow the real parent prototype F.prototype.constructor = Parent; // yep, we''re faking it Child.prototype = new F(); // no side effects, but we have a valid prototype chain Child.prototype.bar = ... // now continue adding to the new prototype


Su único problema en su lógica fue establecer el mismo objeto basicFood tanto para Bread.prototype como para Sushi.prototype . Intenta hacer algo como esto:

Bread.prototype = new Food(); Bread.prototype.constructor = Bread; Sushi.prototype = new Food(); Sushi.prototype.constructor = Sushi;

Ahora la instanceof bread y el sushi será la Food pero los constructores serán el Bread y el Sushi para cada uno en particular;


Vamos a examinar tu código un poco.

function Food(){} function Bread(){} function Sushi(){} var basicFood = new Food(); Bread.prototype = basicFood; Sushi.prototype = basicFood;

Nota: Cuando configura el mismo objeto que el prototipo de dos objetos, el aumento en un prototipo también se reflejará en el otro prototipo. Por ejemplo,

Bread.prototype = basicFood; Sushi.prototype = basicFood; Bread.prototype.testFunction = function() { return true; } console.log(Sushi.prototype.testFunction()); // true

Regresemos a sus preguntas.

var bread = reconstructify(new Bread(), Bread); var sushi = reconstructify(new Sushi(), Sushi); console.log(sushi instanceof Bread); // Why true? console.log(bread instanceof Sushi); // Why true?

Según la instanceof documentos de MDN ,

El operador instanceof prueba si un objeto tiene en su cadena de prototipo la propiedad prototipo de un constructor.

Entonces cuando hacemos algo como

object1 instanceof object2

JavaScript intentará encontrar si el prototipo del object2 está en la cadena de prototipo del object1 .

En este caso, se devolverá true solo cuando el Bread.prototype encuentre en la cadena de prototipos de sushi . Sabemos que el sushi está hecho de Sushi . Entonces, tomará el prototipo de Sushi y verificará si es igual al prototipo de Bread . Desde entonces, ambos apuntan al mismo objeto de basicFood , que devuelve true . El mismo caso para el bread instanceof Sushi también.

Entonces, la forma correcta de heredar sería, así

function Food() {} function Bread() {} function Sushi() {} Bread.prototype = Object.create(Food.prototype); Bread.prototype.constructor = Bread; Sushi.prototype = Object.create(Food.prototype); Sushi.prototype.constructor = Sushi; var bread = new Bread(); var sushi = new Sushi(); console.log(sushi instanceof Bread); // false console.log(bread instanceof Sushi); // false console.log(sushi.constructor); // [Function: Sushi] console.log(bread.constructor); // [Function: Bread] console.log(sushi instanceof Food); // true console.log(bread instanceof Food); // true console.log(sushi instanceof Sushi); // true console.log(bread instanceof Bread); // true