3

すでに何かとして実装されている既存の関数をだます以外に、「コンテキスト」を別のパラメーターとして明示的に渡すのではなく、 (またはthisを介して)コンテキストを変更する必要があるようにJavaScript関数を記述したいのはなぜですか?パフォーマンス上の利点はありますか?.call.apply

例:

function tryIncrement(inc, context) {
    context = context || this; // just so we can reuse same fn for the example

    if( typeof context.val!= typeof 1|| typeof inc != typeof 1 ) return false;
    context.val += inc;
    return true;
}

var a = {name: 'A', val: 5}, b = {name: 'B', val: 20};

// reassign internal context
for(var i = 0, n = [1,3,"not a num",5]; i < n.length; i++) {
    if( tryIncrement.call(a, n[i]) ) console.log('incremented', i, n[i], a);
    else console.log('failed to increment', i, n[i], a);
}

// provide explicit context;
// could just as easily declared function so context was first param
// so it looked the same as previous implementation
for(var i = 0, n = [1,3,"not a num",5]; i < n.length; i++) {
    if( tryIncrement(n[i], b) ) console.log('incremented', i, n[i], b);
    else console.log('failed to increment', i, n[i], b);
}
4

4 に答える 4

1

私が知る限り、の使用法はthis他のパラメーターと実際には何の違いもありませんが、変更する方法はもっと複雑です。

あなたの質問に答える最も簡単な方法は、基本のJavascript言語の作成者があなたの慣習に従っていたかどうかを想像することだと思います。

ない世界this

ない世界thisは、過度の重複がたくさんある恐ろしい騒々しい場所です:

var arr = [1,2,3,4];
arr.reverse(arr); //4321

誤解を招く構文または冗長な構文の機会が増える

var str = "stringtobesplit";
"abiglongstringnotbeingsplit".split(str,":");
String.prototype.split(str,":");

そして、少なくとも適用を完全に取り除くわけではありません:

Math.max.apply(arr);  //didn't add the initial `this` since it doesn't exist

事実上、グローバル関数のみを作成するか、受け取った引数のタイプについて仮定を立てたがそれらの仮定を強制しなかったプロトタイプまたはオブジェクトに関数を作成するかを選択できます。たとえば、ファンタジーの世界でtoStringメソッドを想像してみてください。

これまでにすべてのタイプのオブジェクトを取り込んでそれらをすべて機能させるグローバルtoStringメソッドを作成するか、現在機能しているように各タイプのプロトタイプに関数を設定することができます。そのタイプで呼び出されます。誰かが電話することができます

Array.prototype.toString(str)

そして、それを適切に処理する必要があります(applyでこれを行う価値があるため、Object.prototype.toStringに戻り、戻ります[Object String])。したがって、これらの場合に呼び出す正しいプロトタイプメソッドを特定する必要があります。つまり、慣例では次のように呼び出すことになります。

str.toString(str) 

またはそれらの線に沿った何か。

それで、ポイントは何ですか?

thisプロトタイプチェーン上のjavascriptメソッドの一般的なケースを処理するために構築されています。これは、オブジェクトへの呼び出しを複製したり、そのプロトタイプが何であるかを正確に知る必要なしに、オブジェクトがそれ自体に作用できるようにするための省略形を提供します。これがないと、オブジェクトに関数がないか、毎回明示的に関数を呼び出す必要があり、余分な構文と潜在的なエラーが発生します。

callapplyは例外的なケースであり、少なくとも適用することは、なくなったとしても使用できますthis。例外的なケースにAPIを書き込むことは決して良い考えではありません。オブジェクト指向コードを作成thisしている場合は、呼び出しのコンテキストであるオブジェクトを参照する簡単な方法として使用する必要があります。あなたがこれをうまく書くなら、電話して適用することはめったにそして特別な状況で使われるべきではありません。

TL; DR-this理由からJavascriptの一部として設計されました。より明確で理解しやすい構文のために、オブジェクトでメソッドを作成するときに使用してください。

于 2013-03-10T04:29:23.767 に答える
1

this追加のパラメーターを渡す代わりに使用したい場合が多くあります。たとえば、次の関数について考えてみます。

Function.prototype.async = function () {
    setTimeout.bind(null, this, 0).apply(null, arguments);
};

この関数を使用すると、次のように関数呼び出しを延期できます。

alert.async("This will display later.");
alert("This will display first.");

ここでデモを見ることができます:http://jsfiddle.net/AjwQu/

関数をバインドする代わりにthis、パラメーターとして渡すこともできます。

function async(funct) {
    setTimeout.bind(null, funct, 0).apply(null, [].slice.call(arguments, 1));
}

今は次のように使用します。

async(alert, "This will display later.");
alert("This will display first.");

結果は同じです:http://jsfiddle.net/63dBF/

ただし、引数を取得するには、[].slice.call(arguments, 1)代わりに使用する必要があります。arguments最初の例では、関数が引数リストの一部ではなかったため、単純に使用できました。

すべてに長所と短所があります。いつ何を使うかを知る必要があります。これが少し役立つことを願っています。

ボーナス:this使用する関数を追加のパラメーターを受け入れる関数に、またはその逆に変換するのは本当に簡単です。まず、いくつかの効用関数を定義しましょう。

var functProto = Function.prototype;

var bind = functProto.bind;

var bindable = bind.bind(bind);
var callable = bindable(functProto.call);
var appliable = bindable(functProto.apply);

このbindable関数を使用すると、既存の関数のバインド可能なバージョンを作成できます。この関数を呼び出すと、指定された引数にバインドされた新しい関数が返されます。

このcallable関数を使用すると、既存の関数の呼び出し可能なバージョンを作成できます。この関数を呼び出すと、指定された引数とthisポインターを使用して既存の関数が呼び出されます。

このappliable関数を使用すると、既存の関数の適用可能なバージョンを作成できます。この関数を呼び出すと、指定された引数とthisポインターが既存の関数に適用されます。

次に、最初の例の関数を指定すると、次のように2番目の例の関数を作成できます。

var async = callable(functProto.async);

こちらのデモをご覧ください:http://jsfiddle.net/3dSBS/

同様に、次のように、2番目の例の関数を最初の例の関数に変換できます。

Function.prototype.async = function () {
    return async.apply(null, [this].concat([].slice.call(arguments)));
};

こちらのデモをご覧ください:http://jsfiddle.net/rJQyS/

ご覧のとおり、を使用して関数を記述しthis、その逆の方法よりも、コンテキストをパラメーターとして受け入れる関数を作成する方がはるかに簡単です。

于 2013-03-10T13:15:39.733 に答える
0

オブジェクト指向プログラミングを行う場合、関数はコンテキストに依存し、それをパラメーターとして提供しても意味がありません。これはオブジェクト指向プログラミングの目的を無効にするためです。

コールバックに暗黙的なコンテキストを提供することも理にかなっています。コンテキストのみが必要な場合は、パラメーターの正しい順序を覚えておく必要はありません。その場合、パラメーターを使用する必要はまったくありません。だから代わりに

function mayCallback(param1, param2, context)

あなたはただ書くことができます

function myCallback()

thisparam1 と param2 が必要ない場合は、を使用します。

于 2013-03-10T09:20:23.403 に答える
-1

私の主な目的に対処するために-関数パラメーターよりも使用することでパフォーマンス上の利点はありますか?this-答えはノーのようです:

http://jsperf.com/function-context-vs-parameter

オブジェクト内でインスタンス()変数の代わりにパラメーター値を使用することには、わずかな利点があるように見えますが(ただし、重要ではない場合があります)。this

(自分でテストして、違う場合はコメントしてください)

他の回答によって対処される目的に関して:@Aaditによって指摘されたいくつかのきちんとしたユースケースがありますが、保守性は議論の余地なく個人的な好みですが、@ ben336が言ったように、オブジェクト(したがってOOP)を操作している場合thisはより有用。

ECMAScriptの第5版のネイティブ関数bindは、2つの世界の間の興味深い架け橋になるか、少なくとも探索するのに時間がかかる接線になる可能性があります。

上で参照したインスタンスとパラメータ値のテストも私のポイントの良い例かもしれません。静的な機能ライブラリを構築している場合は、別のコンテキストobj.callback2にスコープすることで「ハイジャック」するか、別のコンテキストを直接this呼び出すことができます。obj.callback

于 2013-03-19T18:03:49.550 に答える