9

私はJavaScriptの初心者で、助けが必要です。関数ごとに半径を合計しようとしましたが、未定義のエラーが発生しました:(

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);
4

2 に答える 2

27

問題は、関数を別の関数への参照として渡しているため、渡された関数がスコープを失っているということです。問題のある行は次のとおりです。

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

JavaScriptオブジェクトは、いくつかの点で見た目よりも単純です。getRadiusメソッドをプロトタイプに追加したとき、従来のCircleOOのようにクラスメソッドを定義していませんでした。プロトタイプの名前付きプロパティを定義し、そのプロパティの値に関数を割り当てるだけでした。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)
于 2012-07-08T22:26:27.170 に答える
0

JavaScriptで注意が必要なのは、関数がデフォルトで呼び出されたときにthisプロパティであったオブジェクトが含まれていることです。MyCircle.increaseRadiusByしたがって、パラメータとして渡すときは、それをとして呼び出すfunc()ので、関数はどのオブジェクトのプロパティでもありません。設定する最も簡単な方法は、次の関数thisを使用することです。call()

function addFivetoIt(func, context) {
    // The first parameter to `call()` is the value of `this` in the function
    func.call(context, 5);
}

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

func呼び出す前にプロパティとして設定する以下のアプローチも機能します。に不要なプロパティが追加されるため、実際にはこれを行うことはありませんが、contextどのように機能するかを示す良い教訓的な例thisです。

function addFivetoIt(func, context) {
    context.func = func;
    context.func(5);
}

var MyCircle = new Circle(0, 0, 10);
addFivetoIt(MyCircle.increaseRadiusBy, MyCircle);
于 2020-01-03T19:03:01.353 に答える