2010-11-11 11 views
73

Di recente ho letto circa l'utilizzo delle chiamate JavaScript MDCereditarietà Javascript: chiamare il super-costruttore o utilizzare la catena del prototipo?

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

uno Linke dell'esempio mostrato di seguito, io ancora non capisco.

Perché stanno usando eredità qui in questo modo

Prod_dept.prototype = new Product(); 

è necessario? Perché c'è una chiamata al super-costruttore in

Prod_dept() 

in ogni caso, come questo

Product.call 

si tratta solo di un comportamento comune? Quando è meglio usare call per il super-costruttore o usare la catena del prototipo?

function Product(name, value){ 
    this.name = name; 
    if(value >= 1000) 
    this.value = 999; 
    else 
    this.value = value; 
} 

function Prod_dept(name, value, dept){ 
    this.dept = dept; 
    Product.call(this, name, value); 
} 

Prod_dept.prototype = new Product(); 

// since 5 is less than 1000, value is set 
cheese = new Prod_dept("feta", 5, "food"); 

// since 5000 is above 1000, value will be 999 
car = new Prod_dept("honda", 5000, "auto"); 

Grazie per rendere le cose più chiare

+0

Il modo in cui l'hai usato è quasi corretto, ma potresti voler usare Object.create() invece di creare un'istanza della base usando la nuova parola chiave (potrebbe causare problemi se il costruttore base ha bisogno di argomenti). Ho dettagli sul mio blog: http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon

+1

Nota anche che Product() viene effettivamente chiamato due volte. –

risposta

103

La risposta alla domanda reale è che avete bisogno di fare entrambe le cose:

  • Impostare il prototipo a un'istanza del genitore inizializza la catena di prototipi (eredità), questo viene fatto solo una volta (da quando il prototipo oggetto è condiviso).
  • Chiamare il costruttore del genitore inizializza l'oggetto stesso, questo viene fatto con ogni istanza (è possibile passare diversi parametri ogni volta che lo si costruisce).

Pertanto, non è necessario chiamare il costruttore del genitore quando si imposta l'ereditarietà. Solo quando si istanzia un oggetto che eredita da un altro.

La risposta di Chris Morgan è quasi completa, manca un piccolo dettaglio (proprietà del costruttore). Lasciatemi suggerire un metodo per impostare l'ereditarietà.

function extend(base, sub) { 
    // Avoid instantiating the base class just to setup inheritance 
    // Also, do a recursive merge of two prototypes, so we don't overwrite 
    // the existing prototype, but still maintain the inheritance chain 
    // Thanks to @ccnokes 
    var origProto = sub.prototype; 
    sub.prototype = Object.create(base.prototype); 
    for (var key in origProto) { 
    sub.prototype[key] = origProto[key]; 
    } 
    // The constructor property was set wrong, let's fix it 
    Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
    }); 
} 

// Let's try this 
function Animal(name) { 
    this.name = name; 
} 

Animal.prototype = { 
    sayMyName: function() { 
    console.log(this.getWordsToSay() + " " + this.name); 
    }, 
    getWordsToSay: function() { 
    // Abstract 
    } 
} 

function Dog(name) { 
    // Call the parent's constructor 
    Animal.call(this, name); 
} 

Dog.prototype = { 
    getWordsToSay: function(){ 
     return "Ruff Ruff"; 
    } 
}  

// Setup the prototype chain the right way 
extend(Animal, Dog); 

// Here is where the Dog (and Animal) constructors are called 
var dog = new Dog("Lassie"); 
dog.sayMyName(); // Outputs Ruff Ruff Lassie 
console.log(dog instanceof Animal); // true 
console.log(dog.constructor); // Dog 

Vedere il post del mio blog per un ulteriore zucchero sintattico durante la creazione delle classi.http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Tecnica copiato da Ext-JS e http://www.uselesspickles.com/class_library/ e un commento da https://stackoverflow.com/users/1397311/ccnokes

+6

In EcmaScript5 + (tutti i browser moderni), è possibile renderlo non enumerabile se lo si definisce come questo 'Object.defineProperty (sub.protoype, 'constructor', {enumerable: false, value: sub});' Questo in questo modo otterrai esattamente lo stesso "comportamento" di quando javascript crea una nuova istanza di una funzione (il costruttore è impostato come enumerabile = falso automaticamente) – Adaptabi

+2

Non potresti semplicemente semplificare il metodo di estensione a due linee? Vale a dire: sub.prototype = Object.create (base.prototype); sub.prototype.constructor = sub; – Appetere

+0

@Steve Sì, è possibile, quando ho scritto per la prima volta, 'Object.create' non è stato altrettanto supportato ... aggiornandolo. Nota che la maggior parte dei polyfill 'Object.create' sono implementati usando la tecnica che ho mostrato in origine. –

28

Il modo ideale per farlo è quello di non fare Prod_dept.prototype = new Product();, perché questo chiama il costruttore Product. Quindi il modo ideale è quello di clonarlo tranne che per il costruttore, qualcosa di simile:

function Product(...) { 
    ... 
} 
var tmp = function(){}; 
tmp.prototype = Product.prototype; 

function Prod_dept(...) { 
    Product.call(this, ...); 
} 
Prod_dept.prototype = new tmp(); 
Prod_dept.prototype.constructor = Prod_dept; 

Poi il costruttore eccellente è chiamato in tempi di costruzione, che è ciò che si vuole, perché allora si può passare i parametri, anche.

Se guardi cose come la Google Closure Library vedrai che è così che lo fanno.

+0

Io chiamo quel costruttore usato per impostare l'ereditarietà del costruttore surrogato. Il tuo esempio dimentica ancora di reimpostare la proprietà del costruttore dopo aver impostato l'ereditarietà in modo da poter rilevare correttamente il costruttore –

+1

@Juan: OK, aggiornato per aggiungere 'Prod_dept.prototype.constructor = Prod_dept;'. –

+0

@ChrisMorgan Ho problemi a capire l'ultima riga del tuo esempio: 'Prod_dept.prototype.constructor = Prod_dept;'. Prima di tutto, perché è necessario e perché fa riferimento a "Prod_dept' anziché a' Product'? –

6

Se avete fatto Object Oriented Programming in JavaScript, si sa che si può creare una classe come segue:

Person = function(id, name, age){ 
    this.id = id; 
    this.name = name; 
    this.age = age; 
    alert('A new person has been accepted'); 
} 

Finora la nostra persona di classe ha solo due proprietà e abbiamo intenzione di dargli alcuni metodi. Un modo pulito per farlo è per usare il suo oggetto "prototipo". A partire da JavaScript 1.1, l'oggetto prototipo è stato introdotto in JavaScript. Questo è un oggetto incorporato che semplifica il processo di aggiunta di proprietà e metodi personalizzati a tutte le istanze di un oggetto. Aggiungiamo 2 metodi per la nostra classe utilizzando il suo oggetto 'prototipo' come segue:

Person.prototype = { 
    /** wake person up */ 
    wake_up: function() { 
     alert('I am awake'); 
    }, 

    /** retrieve person's age */ 
    get_age: function() { 
     return this.age; 
    } 
} 

ora abbiamo definito la nostra classe Person. Cosa succede se volessimo definire un'altra classe chiamata Manager che eredita alcune proprietà da Persona. Non ha senso ridefinire tutte queste proprietà quando definiamo la nostra classe Manager, possiamo semplicemente impostarla per ereditare dalla classe Person. JavaScript non hanno costruito in eredità ma possiamo usare una tecnica per implementare l'ereditarietà come segue:

Inheritance_Manager = {}; // creiamo una classe di eredità manager (il nome è arbitrario)

Ora diamo la nostra classe eredità un metodo chiamato extend che prende gli argomenti baseClass e sottoclassici. All'interno del metodo extend, creeremo una classe interna chiamata inheritance function inheritance() {}. Il motivo per cui stiamo usando questa classe interna è per evitare confusione tra i prototipi di baseClass e di sottoclasse. Quindi il prototipo della nostra classe di ereditarietà punta al prototipo di baseClass con il seguente codice: inheritance.prototype = baseClass. prototipo; Quindi copiamo il prototipo di ereditarietà nel prototipo di sottoclasse come segue: subClass.prototype = new inheritance(); La prossima cosa è specificare il costruttore per la nostra sottoclassella come segue: subClass.prototype.constructor = subClass; Una volta terminato con la nostra prototipazione di sottoclasse, possiamo specificare le prossime due righe di codice per impostare alcuni puntatori di classe base.

subClass.baseConstructor = baseClass; 
subClass.superClass = baseClass.prototype; 

Ecco il codice completo per la nostra funzione di estensione:

Inheritance_Manager.extend = function(subClass, baseClass) { 
    function inheritance() { } 
    inheritance.prototype = baseClass.prototype; 
    subClass.prototype = new inheritance(); 
    subClass.prototype.constructor = subClass; 
    subClass.baseConstructor = baseClass; 
    subClass.superClass = baseClass.prototype; 
} 

Ora che abbiamo implementato la nostra eredità, siamo in grado di iniziare ad usarlo per estendere i nostri corsi. In questo caso ci accingiamo a estendere la nostra classe Persona in una classe Manager come segue:

definiamo la classe Manager

Manager = function(id, name, age, salary) { 
    Person.baseConstructor.call(this, id, name, age); 
    this.salary = salary; 
    alert('A manager has been registered.'); 
} 

rendiamo ereditare forma persona

Inheritance_Manager.extend(Manager, Person); 

Se notato, abbiamo appena chiamato il metodo di estensione della nostra classe Inheritance_Manager e passato la sottoclass Manager nel nostro caso e poi la persona baseClass. Si noti che l'ordine è molto importante qui. Se li si scambia, l'ereditarietà non funzionerà come previsto. Si noti inoltre che è necessario specificare questa eredità prima di poter effettivamente definire la nostra sottoclasse. Ora definiamo la nostra sottoclasse:

Possiamo aggiungere più metodi come quello qui sotto. La nostra classe Manager avrà sempre i metodi e le proprietà definiti nella classe Person perché eredita da essa.

Manager.prototype.lead = function(){ 
    alert('I am a good leader'); 
} 

Ora per testarlo ci consentono di creare due oggetti, uno dalla persona di classe e uno dalla ereditato classe Manager:

var p = new Person(1, 'Joe Tester', 26); 
var pm = new Manager(1, 'Joe Tester', 26, '20.000'); 

Sentitevi liberi di ottenere la piena codice e altri commenti a: http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

+2

http://blog.codinghorror.com/i-shall-call-it-somethingmanager/ –

Problemi correlati