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)
Underscore ha anche un paio di strumenti vincolanti: [ '_.bind'] (http://underscorejs.org/#bind) e [' _.bindAll'] (http://underscorejs.org/ #bindAll). –
Wow, deatailed! Grazie! Non mi sarei mai aspettato così difficile bisogno di tempo per digerire ... – user1510539
@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