問題は、関数を別の関数への参照として渡しているため、渡された関数がスコープを失っているということです。問題のある行は次のとおりです。
Circle.prototype.increaseRadiusBy = function(number) {
this.r = sumWithFunction(this.getRadius, number);
}
JavaScriptオブジェクトは、いくつかの点で見た目よりも単純です。getRadius
メソッドをプロトタイプに追加したとき、従来のCircle
OOのようにクラスメソッドを定義していませんでした。プロトタイプの名前付きプロパティを定義し、そのプロパティの値に関数を割り当てるだけでした。this.getRadius
のような静的関数に引数として渡すとsumWithFunction
、のコンテキストthis
が失われます。this
にバインドされたキーワードで実行されwindow
、プロパティwindow
がないr
ため、ブラウザは未定義のエラーをスローします。
言い換えると、ステートメントthis.getRadius()
は実際には「のgetRadius
プロパティに割り当てられた関数this
を実行し、のコンテキストで実行しますthis
。そのステートメントを介して関数を明示的に呼び出さないと、コンテキストは割り当てられません。
これに対する一般的な解決策は、コンテキストのために、別の関数を受け取る関数に期待される引数を追加することです。
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);
より単純ですが、堅牢性の低い解決策は、ローカルクロージャのコンテキスト参照にアクセスできる関数をインラインで宣言することです。
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);
});
しかし、はるかに簡単な解決策は、ECMAScriptの新しい機能である。と呼ばれる関数メソッドを使用することbind
です。すべてのブラウザでサポートされているわけではないという事実を含め、ここでよく説明されています。そのため、jQuery、Prototypeなどの多くのライブラリには、のようなクロスブラウザ関数バインディングユーティリティメソッドがあり$.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)