this
オブジェクトは関数のコンテキストです。それはあなたがあなたのために何かを作る機械を作るようなものであり、this
オブジェクトはあなたの家のように機械が働く場所になるでしょう。好きなように動かすことができます。
オブジェクトを設定する方法は4つありthis
ます。
メソッドではない関数の呼び出し:
fn(someArguments)
このようにして、this
オブジェクトはnullまたはおそらくウィンドウオブジェクトに設定されます。
関数をメソッドとして呼び出す:
someObject.fn(someArguments)
この場合、this
オブジェクトはポイントしsomeObject
、変更可能です。
call
関数のまたはapply
メソッドを呼び出します。
fn.call(anotherObject, someArguments)
someObject.call(anotherObject, someArguments)
someObject.apply(anotherObject, [someArguments])
この場合、this
オブジェクトはここを指しsomeObject
ます。それを呼び出すとき、あなたはそれを別のコンテキストを持つように強制しています。
関数のバインド
var fn2 = fn.bind(anotherObject, someArguments)
this
これにより、指定したオブジェクトにバインドされる別の関数が作成されます( anotherObject
)。どのように呼んでも、this
オブジェクトは同じになります。
ユースケース
今、あなたはこれを知っているいくつかのトリッキーなことをすることができます。ここにある理由(最初はC ++から来たと思います)は、オブジェクトのメソッドがその親にアクセスする必要があるためです。this
オブジェクトがアクセスを提供します。
var coolObject = {
points : ['People are amazing'],
addPoint : function (p) { this.points.push(p) }
}
したがって、次のことを行うと機能しません。
var addPoint = coolObject.addPoint;
addPoint('This will result in an error');
このオブジェクトはcoolObject
もう私たちのものではなく、pointsプロパティを持っていないため、エラーがスローされます。したがって、このような場合は、次のようにすることができます。
var addPoint = coolObject.addPoint;
addPoint.call({points : []}, 'This is pointless');
これは無意味ですが、関数は機能this
します。オブジェクトでさえ、本来あるべきものではありません。
var anotherCoolObject = {
points : ['Im a thief!'],
addPoint : coolObject.addPoint
}
anotherCoolObject.addPoint('THIS IS CALL STEALING');
それでも、このように呼び出すと、関数は機能します。これは、this
オブジェクトがプロパティを持つanotherCoolObjectを指すためですpoints
。
私が見た中で最も一般的なユースケースは、argumentsオブジェクトをスライスすることです。
function returnHalf() {
return [].slice.call(arguments, 0, arguments.length / 2);
}
returnHalf('Half', 'is', 'not', 'awesome');
// >> [Half', 'is']
ご覧のとおり、argumentsオブジェクトはinstanceof配列ではありません。そうするとarguments.slice(...)
、コンパイラに殺されてしまいます。ただし、ここでは、配列のようなものであるため、argumentsオブジェクトで配列のメソッドを使用します。
関数コンテキストを変更したくない場合や、独自の引数を追加したい場合は、bindを使用します。
たとえば、jqueryを使用してイベントのリスナーを追加する場合、jqueryが関数を呼び出すと、thisオブジェクトが要素になります。しかし、時々あなたはトリッキーなことをしてそれを変更したいです:
var myElement = {
init : function () {
$(this.element).click(this.listener.bind(this));
},
view : "<li>${Name}</li>",
name : 'ed',
element : $('#myelement'),
listener : function () {
this.element.append($.tmpl( this.view, this ));
}
}
myElement.init();
したがって、ここでは、それをmyElementにバインドして、オブジェクトのプロパティにアクセスしてビューをレンダリングできるようにします。別の例は次のとおりです。
for (var i = 0; i < 10; i++) {
setTimeout(function () {console.log(i)}, 10)
}
// All of them will be 10.
for (var i = 0; i < 10; i++) {
setTimeout((function () {console.log(this.i)}).bind({ i : i }, 10)
}
非同期関数呼び出しをループに入れた場合、コールバックが呼び出され、ループが終了し、カウンターが終了するまでに、bindを使用して、現在のカウンターをコールバックにクリーンにバインドできます。
async
私がよく使用するもう1つの良いユースケースは、クロージャを作成せずに、引数を使用して関数をモジュールに渡す場合です。
async.parallel({
writeFile : function (cb) {
fs.writeFile('lolz.txt', someData, cb);
},
writeFile2 : function (cb) {
fs.writeFile('lolz2.txt', someData, cb);
}
}, function (err){
console.log('finished')
});
async.parallel({
writeFile : fs.writeFile.bind(fs, 'lolz.txt', someData),
writeFile2 : fs.writeFile.bind(fs, 'lol2z.txt', someData),
}, function (err){
console.log('finished')
});
これらの2つの実装は同じです。
パフォーマンス
これらをチェックしてください:
http://jsperf.com/bind-vs-call2
http://jsperf.com/js-bind-vs-closure/2
http://jsperf.com/call-vs-closure-to-pass-scope/10
bind
他のタイプの呼び出しと比較してパフォーマンスのオーバーヘッドが大きくなりますが、時期尚早の最適化による保守性でパフォーマンスを犠牲にしないようにしてください。
また、この記事を見ることができます。