2015-07-03 8 views
6

Ho qualche problema a decifrare l'ereditarietà prototipica in JavaScript e ho pensato di pubblicarlo qui. Considerate questo semplice esempio:Qual è la necessità di call() nell'ereditarietà prototipica

function Employee() { 
    this.name = "Rob"; 
    this.dept = "R&D"; 
} 

function Manager() { 
    //Employee.call(this); 
    this.reports = ["Report 1", "Report 2", "Report 3"]; 
} 

Manager.prototype = Object.create(Employee.prototype); 

Employee.prototype.type = "human"; 
m = new Manager(); 
console.log(m.name); //undefined 
console.log(m.type); //human 

Quello che non riesco a capire è l'utilità della linea Employee.call(this). Dal momento che stiamo impostando Employee.protoype come prototipo di Manager, qual è la necessità (come la vedo io) di forzare esplicitamente la creazione di variabili in Employee attraverso call()? In precedenza ho pensato che potesse essere dovuto al fatto che non esiste alcun oggetto di Employee e che l'ereditarietà JS non può funzionare senza oggetti, quindi call() viene qui utilizzato per "completare la creazione dell'oggetto". Tuttavia, la proprietà type viene riflessa in Manager senza la necessità di call(), il che dimostra che non è necessario un oggetto rigido per eseguire l'ereditarietà (ciò che intendo è, solo la definizione della funzione di costruzione simile alla classe farà).

Spero di non averlo reso troppo complicato. In breve: perché è necessario call() qui e perché la proprietà type funziona senza call() (se call() è così importante, cioè).

risposta

2

Lo scopo di Employee.call(this) è di aggiungere gli attributi di nome e reparto alle istanze di Manager.

L'utilizzo di call() è più per convenzione e consente di modificare il chiamante (questo) in posizione.

La proprietà type ha funzionato da quando è stata eseguita l'interfaccia del prototipo.

Se si annulla il valore Employee.call(this), il nome m.name diventa "Rob".

+0

"Lo scopo di Employee.call (this) è di aggiungere gli attributi di nome e reparto alle istanze di Manager." Quindi cos'è 'Manager.prototype = Object.create (Employee.prototype);' doing? Perché non porta ad ereditare quei valori? "L'uso di call() è più per convenzione e consente di modificare il chiamante (questo) sul posto." So cosa, ma perché è necessario? In eredità, stiamo parlando di classi e progetti (spero) non di oggetti.Forse quello che sto chiedendo è più sulle linee della filosofia del design del linguaggio, ma alcune risposte approfondite sarebbero più utili. – dotslash

+1

Object.create() copia solo sui prototipi. Le proprietà name e dept non sono nel prototipo ma nel costruttore, quindi non vengono copiate automaticamente. Se andassi Manager.prototype = Object.create (Employee), anche 'name' e 'dept' verrebbero copiati, ma l'uso della chiamata per fare ciò è più diffuso. – Shilly

+1

@dotslash in effetti, non ci sono classi in JavaScript (anche se EcmaScript 6 potrebbe farcelo credere). Esistono solo oggetti che delegano ad altri oggetti. Lavorare con l'operatore 'new' non fa che falsificare un'eredità pseudo-classica. Ma non è così che javascript funziona all'interno. Il seguente post di Eric Elliott potrebbe aiutare a chiarire le cose: https://medium.com/javascript-scene/common-misconceptions-about-inheritance-in-javascript-d5d9bab29b0a – nils

1

Anche se il prototipo è ereditato, che è il tipo "umano" definito, senza la chiamata al Dipendente non si "inizializza la classe base" e si esegue il codice all'interno del costruttore Employee(). Solo perché Employee è il prototipo di Manager non garantisce di voler eseguire il costruttore Employee quando si crea un gestore.

Diversamente da alcune lingue in cui è possibile chiamare super(), è necessario chiamare l'inizializzatore per la classe base per nome. Questo è simile a C++:

class Manager : public Employee { 
public: 
    Manager() : Employee() {} 
}; 

Potete anche arrivare a decidere quando chiamare il genitore ed eseguire logica aggiuntiva, in cui una chiamata automatica non avrebbe dato questa possibilità:

function Employee(name) { 
    this.name = name; 
    this.dept = "R&D"; 
} 

function Manager(name) { 
    // Add the title to the name, first 
    var mgrName = name + ' (Manager)'; 

    Employee.call(this, mgrName); 
    this.reports = ["Report 1", "Report 2", "Report 3"]; 
} 

Manager.prototype = Object.create(Employee.prototype); 
m = new Manager('Bill'); 
0

Il .type (e altre proprietà prototipicamente ereditate) funzionano, sì.
Ma .name e .dept no, non sono stati creati su m! Avevi perfettamente ragione, è necessario il per l'inizializzazione completa . È la chiamata super del costruttore, che non dovresti omettere.