2012-07-08 18 views
7

Sono un novizio di javascript e ho bisogno di aiuto. Stavo cercando di riassumere raggio per funzione, ma ha ottenuto un errore non definito :(Come passare la funzione prototipo?

function sumWithFunction(func, number) { 
    return func() + number; 
} 

function Circle(X, Y, R) { 
    this.x = X; 
    this.y = Y; 
    this.r = R; 
} 
Circle.prototype.getRadius = function() { 
    return this.r; 
} 
Circle.prototype.increaseRadiusBy = function(number) { 
    this.r = sumWithFunction(this.getRadius, number); 
} 

function addFivetoIt(func) { 
    func(5); 
} 

var MyCircle = new Circle(0, 0, 10); 
addFivetoIt(MyCircle.increaseRadiusBy); 

risposta

17

Il problema è che si sta passando una funzione un riferimento a un'altra funzione, e la funzione passata è quindi perdere portata City! la linea incriminata:.. oggetti

Circle.prototype.increaseRadiusBy = function(number) { 
    this.r = sumWithFunction(this.getRadius, number); 
} 

JavaScript sono in qualche modo più semplice di quanto non appaiano quando si è aggiunto il metodo getRadius al prototipo Circle, non eri definendo un metodo di classe come si farebbe in OO classica eri semplicemente definire una proprietà con nome del prototipo e assegnare af unzione al valore di quella proprietà. Quando si passa this.getRadius come argomento a una funzione statica, ad esempio sumWithFunction, si perde il contesto di this. Esegue con la parola chiave this associata a window e dal momento che window non ha la proprietà r, il browser genera un errore non definito.

In altre parole, la dichiarazione this.getRadius() in realtà sta dicendo "eseguire la funzione assegnata alla getRadius proprietà di this, ed eseguirlo nel contesto di this. senza chiamare la funzione in modo esplicito attraverso quella dichiarazione, il contesto non è assegnato.

una soluzione comune a questo è di aggiungere un argomento previsto su qualsiasi funzione che riceve un'altra funzione, per il contesto.

function sumWithFunction(func, context, number) { 
    return func.apply(context) + number; 
} 

function Circle(X, Y, R) { 
    this.x = X; 
    this.y = Y; 
    this.r = R; 
} 
Circle.prototype.getRadius = function() { 
    return this.r; 
} 
Circle.prototype.increaseRadiusBy = function(number) { 
    this.r = sumWithFunction(this.getRadius, this, number); 
} 

function addFivetoIt(func, context) { 
    func.apply(context,[5]); 
} 

var MyCircle = new Circle(0, 0, 10); 
addFivetoIt(MyCircle.increaseRadiusBy, myCircle); 

Una soluzione più semplice, ma meno efficace sarebbe dichiarare una funzione in linea che può accedere a un riferimento di contesto nella chiusura locale.

function sumWithFunction(func, number) { 
    return func() + number; 
} 

function Circle(X, Y, R) { 
    this.x = X; 
    this.y = Y; 
    this.r = R; 
} 
Circle.prototype.getRadius = function() { 
    return this.r; 
} 
Circle.prototype.increaseRadiusBy = function(number) { 
    var me = this; 
    this.r = sumWithFunction(function() { 
     return me.getRadius() 
    }, number); 
} 

function addFivetoIt(func) { 
    func(5); 
} 

var MyCircle = new Circle(0, 0, 10); 
addFivetoIt(function(number) { 
    return MyCircle.increaseRadiusBy(number); 
}); 

Ma di gran lunga la soluzione più semplice è quella di utilizzare una funzionalità più recente di ECMAScript, un metodo di funzione chiamata bind. It is explained well here, incluso il fatto che non è supportato da tutti i browser. Ecco perché molte librerie, come jQuery, Prototype, ecc., Hanno metodi di utilità per il collegamento di funzioni cross-browser come $.proxy.

function sumWithFunction(func, number) { 
    return func() + number; 
} 

function Circle(X, Y, R) { 
    this.x = X; 
    this.y = Y; 
    this.r = R; 
} 
Circle.prototype.getRadius = function() { 
    return this.r; 
} 
Circle.prototype.increaseRadiusBy = function(number) { 
    this.r = sumWithFunction(this.getRadius.bind(this), number); // or $.proxy(this.getRadius,this) 
} 

function addFivetoIt(func) { 
    func(5); 
} 

var MyCircle = new Circle(0, 0, 10); 
addFivetoIt(MyCircle.increaseRadiusBy.bind(MyCircle)); // or $.proxy(MyCircle.increaseRadiusBy,MyCircle) 
+1

Underscore ha anche un paio di strumenti vincolanti: [ '_.bind'] (http://underscorejs.org/#bind) e [' _.bindAll'] (http://underscorejs.org/ #bindAll). –

+0

Wow, deatailed! Grazie! Non mi sarei mai aspettato così difficile bisogno di tempo per digerire ... – user1510539

+0

@muistooshort Vero! Quasi tutte le librerie, incluso [Ext] (http://docs.sencha.com/ext-js/4-1/#!/api/Ext-method-bind), [MooTools] (http: // mootools. net/docs/core/Types/Function # Funzione: bind), [YUI] (http://yuilibrary.com/yui/docs/api/classes/YUI.html#method_bind) ... hai un'idea. – zetlen

Problemi correlati