Javascript prototípico herencia?
-
23-08-2019 - |
Pregunta
I'been haciendo un poco de herencia en js con el fin de comprenderlo mejor, y me encontré con algo que me confunde.
Sé que cuando se llama a un 'función constructora' con la nueva palabra clave, se obtiene un nuevo objeto con una referencia al prototipo de esa función.
también sé que con el fin de hacer herencia de prototipos debe reemplazar el prototipo de la función constructora con una instancia del objeto que desea ser el 'superclase'.
Así que hice este ejemplo tonto para tratar estos conceptos:
function Animal(){}
function Dog(){}
Animal.prototype.run = function(){alert("running...")};
Dog.prototype = new Animal();
Dog.prototype.bark = function(){alert("arf!")};
var fido = new Dog();
fido.bark() //ok
fido.run() //ok
console.log(Dog.prototype) // its an 'Object'
console.log(fido.prototype) // UNDEFINED
console.log(fido.constructor.prototype == Dog.prototype) //this is true
function KillerDog(){};
KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")}
fido.prototype = new KillerDog();
console.log(fido.prototype) // no longer UNDEFINED
fido.deathBite(); // but this doesn't work!
(Esto se hizo en la consola de Firebug)
1) ¿Por qué si todos los nuevos objetos contienen una referencia al prototipo de la función de creador, fido.prototype es indefinido?
2) es la cadena de herencia [obj] -> [constructor] -> [Prototype] en lugar de [obj] -> [Prototype]
3) es la propiedad 'prototipo' de nuestro objeto (Fido) cada vez verificado? Si es así ... ¿por qué es 'deathBite' no definido (en la última parte)?
Solución
1) ¿Por qué si todos los nuevos objetos contienen una referencia a la función de creador prototipo, es fido.prototype indefinido?
Todos los nuevos objetos estén en posesión de una referencia al prototipo que estuvo presente en su constructor en el momento de la construcción. Sin embargo, el nombre de la propiedad se utiliza para almacenar esta referencia no se prototype
como lo es en la propia función constructora. Algunas implementaciones JavaScript Es importante permitir el acceso a esta propiedad 'oculta' a través de algún nombre de la propiedad como __proto__
donde otros no lo hacen (por ejemplo Microsoft de).
2) es la cadena de herencia [obj] -> [Constructor] -> [prototipo] en lugar de [obj] -> [Prototype]
No. Echar un vistazo a esto: -
function Base() {}
Base.prototype.doThis = function() { alert("First"); }
function Base2() {}
Base2.prototype.doThis = function() { alert("Second"); }
function Derived() {}
Derived.prototype = new Base()
var x = new Derived()
Derived.prototype = new Base2()
x.doThis();
Esto alerta "primero" no segundo. Si la cadena de herencia pasó a través del constructor veríamos "Segundo". Cuando se construye un objeto de la referencia de corriente que tuvo lugar en la propiedad Funciones prototipo se transfiere al objeto de referencia oculta a su prototipo.
3) es la propiedad 'prototipo' de nuestra objeto (Fido) cada vez verificado? si es así... ¿por qué es 'deathBite' no definido (en el última parte)?
Asignar a un objeto (que no sea una función) una propiedad llamada prototype
tiene ningún significado especial, como se dijo anteriormente un objeto no mantiene una referencia a su prototipo a través de un nombre de dicha propiedad.
Otros consejos
No se puede cambiar el prototipo de un objeto, una vez ha sido instanciado con new
.
En el ejemplo anterior, como líneas
fido.prototype = new KillerDog();
simplemente crea un nuevo atributo llamado prototype
en el fido
objeto, y establece que el atributo a un nuevo objeto KillerDog
. No es diferente que
fido.foo = new KillerDog();
A medida que su código se encuentra ...
// Doesn't work because objects can't be changed via their constructors
fido.deathBite();
// Does work, because objects can be changed dynamically,
// and Javascript won't complain when you use prototype
//as an object attribute name
fido.prototype.deathBite();
El comportamiento especial prototype
se aplica sólo a los constructores en JavaScript, donde los constructores están function
s que serán llamados con new
.
respuesta por números a sus preguntas:
- propiedad prototype del objeto no se llama
prototype
. El estándar utiliza[[prototype]]
para designarlo. Firefox hace que esta propiedad pública bajo el nombre de __proto __. - La cadena de herencia es
[obj]
⇒[prototype object]
. Su hipótesis inicial ([obj]
⇒[constructor]
⇒[prototype]
) es incorrecta y se puede refutar fácilmente modificandoconstructor
y / oconstructor.prototype
, y comprobar qué métodos pueden ser llamados en su[obj]
- usted descubrirá que estas modificaciones no cambian nada -
prototype
propiedad sobre los objetos no son controlados y no se utiliza. Usted puede configurarlo para lo que quiera. JavaScript utiliza en los objetos junto función sólo durante la construcción de objetos.
Para demostrar el # 3 aquí está el código de Dojo :
dojo.delegate = dojo._delegate = (function(){
// boodman/crockford delegation w/ cornford optimization
function TMP(){}
return function(obj, props){
TMP.prototype = obj;
var tmp = new TMP();
if(props){
dojo._mixin(tmp, props);
}
return tmp; // Object
}
})();
Como se puede ver que se aprovecha del hecho de que prototype
se utiliza sólo en un lugar mediante la reutilización de la misma TMP
función para todos los objetos delegados con diferentes prototipos. De hecho prototype
se asigna directamente antes de invocar la función con new
, y se cambió después de que no afecte a los objetos creados.
Puede encontrar la secuencia creada objeto en mi respuesta a [[Prototype]] y el prototipo en JavaScript .
Sé que ya ha sido contestada, pero, hay una mejor manera de hacerlo herencia. Llamar a un constructor sólo con el propósito de herencia no es deseable. Uno de los efectos no deseados es.
function Base() {this.a = "A"}
function Child() {this.b = "B"};
Child.prototype = new Base();
Ahora propiedad Ha añadido "a" para el prototipo de niño que no tenía intención de.
Esta es la manera correcta (Yo no inventé esto, Ext-JS y otras librerías utilizan este)
// This is used to avoid calling a base class's constructor just to setup inheritance.
function SurrogateCtor() {}
/**
* Sets a contructor to inherit from another constructor
*/
function extend(BaseCtor, DerivedCtor) {
// Copy the prototype to the surrogate constructor
SurrogateCtor.prototype = BaseCtor.prototype;
// this sets up the inheritance chain
DerivedCtor.prototype = new SurrogateCtor();
// Fix the constructor property, otherwise it would point to the BaseCtor
DerivedCtor.prototype.constructor = DerivedCtor;
// Might as well add a property to the constructor to
// allow for simpler calling of base class's method
DerivedCtor.superclass = BaseCtor;
}
function Base() {
this.a = "A";
}
Base.prototype.getA = function() {return this.a}
function Derived() {
Derived.superclass.call(this); // No need to reference the base class by name
this.b = "B";
}
extend(Base, Derived);
// Have to set methods on the prototype after the call to extend
// otherwise the prototype is overridden;
Derived.prototype.getB = function(){return this.b};
var obj = new Derived();
Una forma aún más fácil es agregar un tercer parámetro para extender donde se especifica el método de la clase derivada de modo que usted no tiene que llamar a extender y luego añadir métodos al prototipo
extend(BaseCtor, DerivedCtor, {
getB: function() {return this.b}
});
A continuación, hay muchas otras cosas que podría hacer para el azúcar sintáctica.
escribió en su blog sobre él: http: // JS -bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
A tener en cuenta que, en ECMAScript 5 (es decir, la última versión del lenguaje JavaScript) se puede obtener acceso a la interna [[Prototype]] propiedad de una instancia a través de Object.getPrototypeOf
:
Object.getPrototypeOf(fido) === Dog.prototype