Javascript prototipale eredità?
-
23-08-2019 - |
Domanda
I'been fare un po 'eredità in js al fine di comprendere meglio, e ho trovato qualcosa che mi confonde.
So che quando si chiama un 'funzione di costruzione' con la nuova parola chiave, si ottiene un nuovo oggetto con un riferimento al prototipo di quella funzione.
So anche che al fine di rendere l'ereditarietà prototipale è necessario sostituire il prototipo della funzione di costruzione con un'istanza dell'oggetto che si desidera essere il 'superclasse'.
Così ho fatto questo stupido esempio per provare questi concetti:
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!
(Questo è stato fatto in console di Firebug)
1) Perché se tutti i nuovi oggetti contengono un riferimento alla prototipo della funzione creatore, fido.prototype è indefinito?
2) è la catena di ereditarietà [obj] -> [costruttore] -> [prototype] invece di [obj] -> [prototype]
3) è la proprietà 'prototipo' del nostro oggetto (fido) mai verificato? se è così ... perché è 'deathBite' indefinito (nella parte finale)?
Soluzione
1) Perché se tutti i nuovi oggetti contengono una il riferimento al Creatore la funzione di prototipo, fido.prototype è undefined?
Tutti i nuovi oggetti fanno mantenere un riferimento al prototipo che era presente sul loro costruttore al momento della costruzione. Tuttavia il nome proprietà utilizzata per memorizzare questo riferimento non è prototype
in quanto è la funzione di costruzione stessa. Alcune implementazioni Javascript Impedire l'accesso a questa proprietà 'nascosto' con un nome proprietà come __proto__
dove gli altri non (per esempio Microsofts).
2) è la catena di ereditarietà [obj] -> [Costruttore] -> [prototype] invece di [obj] -> [prototype]
No. Date un'occhiata a questo: -
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();
È il segnale per "First" non Seconda. Se la catena di ereditarietà è andato tramite il costruttore vorremmo vedere "Secondo". Quando un oggetto viene costruito il riferimento corrente intrattenuto nella proprietà Funzioni prototipo è trasferito al di oggetti nascosti riferimento al suo prototipo.
3) è il 'prototipo' di proprietà della nostra oggetto (fido) mai verificato? se è così... perché è 'deathBite' indefinito (nel ultima parte)?
L'assegnazione ad un oggetto (diverso da una funzione) una proprietà denominata prototype
non ha alcun significato particolare, come affermato in precedenza un oggetto non mantiene un riferimento al suo prototipo tramite tale nome della proprietà.
Altri suggerimenti
Non è possibile modificare il prototipo di un oggetto una volta che è stato istanziato con new
.
Nel tuo esempio sopra, le linee come
fido.prototype = new KillerDog();
semplicemente crea un nuovo attributo denominato prototype
sul fido
oggetto, e imposta tale attributo a un nuovo oggetto KillerDog
. Non è diverso rispetto
fido.foo = new KillerDog();
Per quanto il codice si trova ...
// 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();
Il comportamento speciale prototype
si applica solo ai costruttori in JavaScript, in cui i costruttori sono function
s che verranno chiamati con new
.
Risposta dai numeri alle tue domande:
- proprietà prototype di oggetto non si chiama
prototype
. Lo standard utilizza[[prototype]]
per designare esso. Firefox rende questa proprietà pubblica con il nome di __proto __. - La catena di ereditarietà è
[obj]
[prototype object]
⇒. La tua ipotesi originale ([obj]
⇒[constructor]
⇒[prototype]
) non è corretto e si può facilmente confutarla, modificandoconstructor
e / oconstructor.prototype
, e controllando quali metodi possono essere chiamati sul[obj]
- si scopre che queste modifiche non cambiano nulla - proprietà
prototype
su oggetti non vengono controllati e non utilizzati. È possibile impostare per quello che vuoi. JavaScript utilizza su oggetti funzione solo durante la costruzione di un oggetto.
Per dimostrare la # 3 ecco il codice da 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
}
})();
Come si può vedere si avvantaggia del fatto che prototype
viene utilizzato solo in un luogo riutilizzando la stessa funzione TMP
per tutti gli oggetti delegati con differenti prototipi. Infatti prototype
viene assegnato direttamente prima di richiamare la funzione con new
, e sarà cambiato dopo che non interessano tutti gli oggetti creati.
È possibile trovare la sequenza creata oggetto nella mia risposta a tra [[Prototype]] e prototipi in JavaScript .
Lo so che è già stato risposto, ma, c'è un modo migliore per farlo eredità. Chiamare un costruttore solo per lo scopo di eredità non è auspicabile. Uno degli effetti indesiderati è.
function Base() {this.a = "A"}
function Child() {this.b = "B"};
Child.prototype = new Base();
proprietà ora hai aggiunto "a" al prototipo del bambino che non hai intenzione di.
Ecco la strada giusta (non ho inventato questo, Ext-JS e le altre librerie usano questo)
// 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();
Un modo ancora più semplice è quello di aggiungere un terzo parametro per estendere in cui si specifica il metodo della classe derivata in modo che non c'è bisogno di chiamare estendere e quindi aggiungere i metodi al prototipo
extend(BaseCtor, DerivedCtor, {
getB: function() {return this.b}
});
Poi ci sono molte altre cose che si potrebbe fare per lo zucchero sintattico.
bloggato su esso: http: // js -bits.blogspot.com/2010/08/javascript-inheritance-done-right.html
Da segnalare che in ECMAScript 5 (vale a dire la versione più recente del linguaggio JavaScript) è possibile ottenere l'accesso al interno [[Prototype]] di proprietà di un'istanza tramite Object.getPrototypeOf
:
Object.getPrototypeOf(fido) === Dog.prototype