herencia - prototype javascript ejemplos
Javascript Prototypal Herencia Duda II (5)
No puede cambiar el prototipo de un objeto una vez que ha sido instanciado con new
.
En su ejemplo anterior, líneas como
fido.prototype = new KillerDog();
simplemente crea un nuevo atributo llamado prototype
en el objeto fido
, y establece ese atributo en un nuevo objeto KillerDog
. No es diferente de
fido.foo = new KillerDog();
Como su código está parado ...
// 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 del prototype
especial se aplica solo a los constructores en javascript, donde los constructores son function
que se llamarán con new
.
He estado haciendo algo de herencia en js para entenderlo mejor, y encontré algo que me confunde.
Sé que cuando llamas una ''función de constructor'' con la palabra clave nueva, obtienes un objeto nuevo con una referencia al prototipo de esa función.
También sé que para hacer una herencia prototípica debes reemplazar el prototipo de la función constructora con una instancia del objeto que quieres que sea la ''superclase''.
Así que hice este ejemplo tonto para probar 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 fue hecho en la consola de Firebug)
1) ¿Por qué si todos los objetos nuevos contienen una referencia al prototipo de la función del creador, fido.prototype no está definido?
2) ¿Es la cadena de herencia [obj] -> [constructor] -> [prototipo] en lugar de [obj] -> [prototipo]?
3) ¿se ha comprobado alguna vez la propiedad ''prototipo'' de nuestro objeto (fido)? si es así ... ¿por qué ''deathBite'' no está definido (en la última parte)?
¡Gracias!
Sé que ya ha sido respondida, pero hay una mejor manera de hacer herencia. Llamar a un constructor solo con el propósito de heredar 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 ha agregado la propiedad "a" al prototipo de Niño que no pretendía.
Esta es la manera correcta (no inventé esto, Ext-JS y otras bibliotecas usan esto)
// 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 extenderlo donde especifique el método de la clase derivada para que no tenga que llamar a extender y luego agregar métodos al prototipo
extend(BaseCtor, DerivedCtor, {
getB: function() {return this.b}
});
Luego hay muchas otras cosas que podrías hacer por el azúcar sintáctico.
Blogueó al respecto: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
Cabe destacar que en ECMAScript 5 (es decir, la última versión del lenguaje de JavaScript) puede obtener acceso a la propiedad interna [[Prototype]] de una instancia a través de Object.getPrototypeOf
:
Object.getPrototypeOf(fido) === Dog.prototype
1) ¿Por qué si todos los objetos nuevos contienen una referencia al prototipo de la función del creador, fido.prototype no está definido?
Todos los objetos nuevos tienen una referencia al prototipo que estaba presente en su constructor en el momento de la construcción. Sin embargo, el nombre de propiedad utilizado para almacenar esta referencia no es un prototype
ya que está en la función constructora. Algunas implementaciones de JavaScript permiten el acceso a esta propiedad ''oculta'' a través de un nombre de propiedad como __proto__
donde otros no lo hacen (por ejemplo, Microsofts).
2) ¿Es la cadena de herencia [obj] -> [constructor] -> [prototipo] en lugar de [obj] -> [prototipo]?
No. Eche 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 fuera a través del constructor, veríamos "Segundo". Cuando se construye un objeto, la referencia actual mantenida en la propiedad del prototipo Funciones se transfiere a la referencia oculta del objeto a su prototipo.
3) ¿se ha comprobado alguna vez la propiedad ''prototipo'' de nuestro objeto (fido)? si es así ... ¿por qué ''deathBite'' no está definido (en la última parte)?
Asignar a un objeto (que no sea una función) una propiedad llamada prototype
no tiene ningún significado especial, como se dijo anteriormente, un objeto no mantiene una referencia a su prototipo a través de dicho nombre de propiedad.
Responda por números a sus preguntas:
- La propiedad del prototipo del objeto no se llama
prototype
. El estándar usa[[prototype]]
para designarlo. Firefox hace que esta propiedad sea pública bajo el nombre de __proto__. - La cadena de herencia es
[obj]
⇒[prototype object]
. Tu suposición original ([obj]
⇒[constructor]
⇒[prototype]
) es incorrecta y puedes refutarla fácilmente modificandoconstructor
y / oconstructor.prototype
, y verificando qué métodos se pueden[obj]
en tu[obj]
: descubrirás que estas modificaciones no cambian nada. -
prototype
propiedad deprototype
en los objetos no se verifica y no se usa. Puedes configurarlo como quieras. JavaScript lo usa en objetos de función solo durante la construcción del objeto.
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 puede ver, se aprovecha el hecho de que el prototype
se usa solo en un lugar al reutilizar la misma función TMP
para todos los objetos delegados con diferentes prototipos. De hecho, el prototype
se asigna directamente antes de invocar la función con new
, y se modificará después de que no afecte a ningún objeto creado.
Puede encontrar la secuencia objeto creado en mi respuesta a la relación entre [[Prototype]] y el prototipo en JavaScript .