8

call()JavaScriptとapply()メソッドの力を組み合わせる必要があります。私が抱えている問題はcall()、への適切な参照を保持してthisいますが、関数の引数として送信する必要があるときに、配列として持っている引数配列を送信することです。配列を使用する場合、メソッドは引数を関数に問題なく送信しますが、メソッドが自然にアクセスできるように見えるapply()適切な参照を送信する方法がわかりません。thiscall()

以下は、私が持っているコードの簡略化されたバージョンです。おそらくかなり役に立たないように見えますが、要点を理解するには良い方法です。

// AN OBJECT THAT HOLDS SOME FUNCTIONS
var main = {};
main.the_number = 15;
main.some_function = function(arg1, arg2, arg3){
    // WOULD VERY MUCH LIKE THIS TO PRINT '15' TO THE SCREEN
    alert(this.the_number);
    // DO SOME STUFF WITH THE ARGUMENTS
    ... 
};
// THIS STORES FUNCTIONS FOR LATER.
//  'hub' has no direct knowledge of 'main'
var hub = {};
hub.methods = [];
hub.methods.push(main.some_function);
hub.do_methods = function(arguments_array){
    for(var i=0; i<this.methods.length; i++){
        // With this one, '15' is printed just fine, but an array holding 'i' is 
        //  just passed instead if 'i' itself
        this.methods[i].call(arguments_array);   
        // With this one, 'i' is passed as a function argument, but now the
        //  'this' reference to main is lost when calling the function 
        this.methods[i].apply(--need a reference to 'main' here--, arguments_array); 
    }
}
4

6 に答える 6

9

何?適用もスコープを通過します...

method.apply(this, [args]);

編集:

あなたのコードでは、包含スコープで定義されたメインオブジェクトがあるので、簡単に行うことができます。

this.methods[i].call(main);

また

this.methods[i].apply(main, [args]);
于 2012-05-22T18:52:06.437 に答える
2

使用する場合applycall第 1 パラメーターは設定したいものですthis

call引数のリストを取ります:

.call(main, i, j)

apply配列または引数を取ります。

.apply(main, [i, j])

したがって、この行で:

this.methods[i].call([i]); 

これはinsideとして渡さ[i]れます。thisthis.methods[i]

あなたはおそらくやりたい:

this.methods[i].call(main, i);

これは を呼び出しthis.methods[i]、 に設定thismain、それに渡しiます。

または:

this.methods[i].call(main, arguments_array);

これは を呼び出しthis.methods[i]、 に設定thismain、 の要素をarguments_arrayパラメーターとして渡します。

于 2012-05-22T18:54:12.267 に答える
1

親オブジェクトへの参照を関数オブジェクトにアタッチするだけです。

main.some_function = function () {
    //...
}
main.some_function.parent = main;

// now some_function.parent holds a ref to main

または、必要に応じて、レスキューのクロージャ:main定義するときにへの参照を含めますmain.some_function

// self-execution function the returns a function with `that` (i.e., main) in scope
main.some_function = (function(that) {
    return function() {
        alert(that.the_number);
    }
})(main);
于 2012-05-22T19:10:06.117 に答える
1

前の回答のコメントを読んだので、何らかの拘束力が必要だと思います。jQueryまたは同様のライブラリを使用するか、自分で実装することができます。

var bound_some_function = function(){
    return main.some_function.apply(main, arguments);
}
// ...
hub.methods.push(bound_some_function);
// ...
this.methods[i].apply(null /*whathever, has no effect*/, arguments_array);

バインド関数のユニバーサル定義は次のようになります。

function bind(fun, scope){
    return function(){
        return fun.apply(scope, arguments);
    };
}

バインディングの詳細については、Googleの「Javascript関数バインディング」を参照してください。それについての記事がたくさんあります。

于 2012-05-22T19:16:32.233 に答える
0

apply はバインドされた関数に影響を与えないため(上記の回答のように)、以前に(削除された回答で)誤った情報がありました。

証明する簡単なコードを追加するだけです:

/*Lets suppose we have a class "MessageReader" that has a method printMessage
 * that for some reason needs to receive the message to be printed from a function,
 * not directly a value.*/
function MessageReader() {
    this.value = "This is NOT a message and is not supposed to be readed!";
}
/*getMessage needs to return a string that will be printed!*/
MessageReader.prototype.printMessage = function(getMessage) {
    console.log(getMessage.apply(this, []));
}

function Message(value) {
    this.value = value;
}
Message.prototype.getMessage = function() {
    return this.value;
}


var myMessageReader = new MessageReader();
var myMessage1 = new Message("This is a simple message");
var myMessage2 = new Message("This is another simple message");

/*This will print the wrong message:*/
myMessageReader.printMessage(myMessage1.getMessage);

/*But this works!*/
myMessageReader.printMessage(myMessage2.getMessage.bind(myMessage2));
于 2015-08-26T14:50:56.567 に答える
0

.call()メソッドとメソッドは、 での.apply()処理に関して違いはありませんthis。MDN ドキュメントから:

この関数の構文は apply() の構文とほぼ同じですが、根本的な違いは、call() が引数リストを受け入れるのに対し、apply() は単一の引数配列を受け入れることです。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call

残念ながら、call / bind / apply を使用して関数を呼び出し、元のthis参照を維持することはできませんが、これは醜いように見えるかもしれませんが、渡すことができる回避策があります

function applyOriginalThis(fn, parametersArray)
{
    var p = parametersArray;

    switch (p.length)
    {
        case 0: fn(); break;
        case 1: fn(p[0]); break;
        case 2: fn(p[0], p[1]); break;
        case 3: fn(p[0], p[1], p[2]); break;
        case 4: fn(p[0], p[1], p[2], p[3]); break;
        case 5: fn(p[0], p[1], p[2], p[3], p[4]); break;
        case 6: fn(p[0], p[1], p[2], p[3], p[4], p[5]); break;
        case 7: fn(p[0], p[1], p[2], p[3], p[4], p[5], p[6]); break;
        case 8: fn(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); break;
        case 9: fn(p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); break;
        default: throw "Too many parameters.";
    }
}

もちろん、これはサポートしたいパラメータの数に対してのみ機能します。明るい面では、過剰な数のパラメーターを取る関数はコードの匂いです。また、私が間違っていなければ、このタイプのコード パターンは AngularJS 1.x でパフォーマンスの最適化として使用されていました.apply()(現在は参照が見つかりません)。

于 2016-07-20T16:22:18.570 に答える