JavaScriptを学習する最初の段階では、初心者は通常、JavaScriptの機能がC#の場合とほぼ同じように機能すると考えています。 ただし、JavaScriptで関数を呼び出すためのメカニズムには多くの重要な違いがあり、それらを無視するとエラーを見つけやすくなります。
3つの要素の配列を返す単純な関数を書きましょう-これの現在の値と関数に渡される2つの引数。
function makeArray(arg1, arg2){ return [ this, arg1, arg2 ]; }
最も一般的な方法:グローバルコール
多くの場合、初心者は上記の例に示すように関数を宣言します。 この関数の呼び出しは簡単です。
makeArray('one', 'two'); // => [ window, 'one', 'two' ]
ちょっと待って
window
オブジェクトはどこから来たのですか? なぜ
this
が
window
と等しいのですか?
JavaScriptでは、スクリプトがブラウザで実行されようと他の環境で実行されようと、 グローバルオブジェクトは常に定義されます。 何かに「付加」されていない(つまり、オブジェクト宣言の外側にある)スクリプト内のコードは、実際にはグローバルオブジェクトのコンテキスト内にあります。 この場合、
makeArray
はそれ自体が「歩く」関数ではありません。 実際、
makeArray
はグローバルオブジェクト(ブラウザでのコード実行の場合)
window
メソッドです。 これは簡単に証明できます:
alert( typeof window.methodThatDoesntExist ); // => undefined alert( typeof window.makeArray ); // => function
つまり、
makeArray('one', 'two');
への呼び出し
makeArray('one', 'two');
window.makeArray('one', 'two');
を呼び出すのと
window.makeArray('one', 'two');
。
この関数呼び出し方法が最も一般的であるという事実に悲しんでいます。なぜなら、それはグローバル関数の存在を暗示しているからです。 そして、私たちは皆、グローバル関数とグローバル変数がプログラミングの最高のトーンではないことを知っています。 これは特にJavaScriptに当てはまります。 グローバルな定義は避けてください。後悔することはありません。
関数を呼び出すためのルール1。オブジェクトを指定せずに関数を直接呼び出す場合(たとえば、
myFunction()
)、この値はグローバルオブジェクト(コードがブラウザーで実行される場合は
window
)になります。
メソッド呼び出し
単純なオブジェクトを作成し、
makeArray
をメソッドにしましょう。 リテラル表記を使用してオブジェクトを宣言し、メソッドを呼び出します。
// var arrayMaker = { someProperty: '- ', make: makeArray }; // make() arrayMaker.make('one', 'two'); // => [ arrayMaker, 'one', 'two' ] // , arrayMaker['make']('one', 'two'); // => [ arrayMaker, 'one', 'two' ]
違いがわかりますか? この場合、
this
値はオブジェクト自体です。 関数宣言が変更されていないため、前の例のように
window
を使用しないのはなぜですか? 完全な秘密は、関数がJavaScriptで渡される方法です。
Function
は、実際にはオブジェクトであるJavaScriptの標準タイプであり、他のオブジェクトと同様に、関数を渡してコピーできます。 この場合、引数リストと本文を含む関数全体をコピーし、結果のオブジェクトを
arrayMaker
オブジェクトの
make
プロパティに割り当てました。 これはそのような宣言と同等です:
var arrayMaker = { someProperty: '- '; make: function (arg1, arg2) { return [ this, arg1, arg2]; } };
関数呼び出し規則#2:メソッド呼び出し構文を使用して呼び出される関数、たとえば
obj.myFunction()
または
obj['myFunction']()
では、値は
obj
ます。
この単純で一般的な原則を理解しないと、多くの場合、イベント処理でエラーが発生します。
<input type="button" value="Button 1" id="btn1" /> <input type="button" value="Button 2" id="btn2" /> <input type="button" value="Button 3" id="btn3" onclick="buttonClicked();" /> <script type="text/javascript"> function buttonClicked(){ var text = (this === window) ? 'window' : this.id; alert( text ); } var button1 = document.getElementById('btn1'); var button2 = document.getElementById('btn2'); button1.onclick = buttonClicked; button2.onclick = function(){ buttonClicked(); }; </script>
最初のボタンをクリックすると、メッセージ「btn1」が表示されます。この場合、関数をメソッドとして呼び出し、関数内でこのメソッドが属するオブジェクトの値を取得するためです。 2番目のボタンをクリックすると、 「window」が表示されます。この場合、
buttonClicked
直接呼び出すためです(つまり、
obj.buttonClicked()
ます)。 3番目のボタンの場合のように、要素タグでイベントハンドラーを割り当てると、同じことが起こります。 3番目のボタンをクリックすると、2番目のボタンと同じメッセージが表示されます。
jQueryのようなライブラリを使用する場合、考える必要はありません。 jQueryは、イベントハンドラーで
this
値を書き換えて、
this
値がイベントを発生させた要素になるように注意します。
// jQuery $('#btn1').click( function() { alert( this.id ); // jQuery , 'this' });
jQueryは
this
値をどのように変更
this
ますか? 以下をお読みください。
さらに2つの方法: apply()
およびcall()
関数を使用する頻度が高くなるほど、関数を渡して異なるコンテキストで呼び出す必要が頻繁に発生するのは論理的です。 多くの場合、
this
の値をオーバーライドする必要があります。 覚えているなら、JavaScriptの関数はオブジェクトです。 実際には、これは、関数に事前定義されたメソッドがあることを意味します。
apply()
と
call()
はそのうちの2つです。 これらの値をオーバーライドできます:
var car = { year: 2008, model: 'Dodge Bailout' }; makeArray.apply( car, [ 'one', 'two' ] ); // => [ car, 'one', 'two' ] makeArray.call( car, 'one', 'two' ); // => [ car, 'one', 'two' ]
これら2つの方法は非常に似ています。 最初のパラメーターは
this
オーバーライドします。 これらの違いは次の引数にあります
Function.apply()
は
Function.apply()
に渡される値の配列を受け取り、
Function.call()
は引数を個別に受け取ります。 実際には、私の意見では、
apply()
を
apply()
方が便利です。
関数を呼び出すためのルールNo. 3:関数を別のオブジェクトにコピーせずに
this
値をオーバーライドする場合は、
myFunction.apply( obj )
または
myFunction.call( obj )
使用できます。
コンストラクター
JavaScriptでネイティブ型を宣言することは避けますが、JavaScriptにはクラスがなく、カスタム型にはコンストラクターが必要であることを思い出す必要があると思います。 さらに、コンストラクター関数のプロパティである
prototype
使用してユーザー定義メソッドを宣言することをお勧めします。 タイプを作成しましょう:
// function ArrayMaker(arg1, arg2) { this.someProperty = ''; this.theArray = [ this, arg1, arg2 ]; } // ArrayMaker.prototype = { someMethod: function () { alert(' someMethod'); }, getArray: function () { return this.theArray; } }; var am = new ArrayMaker( 'one', 'two' ); var other = new ArrayMaker( 'first', 'second' ); am.getArray(); // => [ am, 'one', 'two' ]
この例で重要なのは、関数を呼び出す前に
new
演算子が存在することです。 彼のためではなかった場合、それはグローバルコールになり、コンストラクターで作成されたプロパティはグローバルオブジェクトを参照します。 必要ありません。 さらに、コンストラクターは通常、明示的に値を返しません。
new
演算子がないと、コンストラクターは
undefined
返し、それを使用して
this
返します。 良いスタイルは、大文字のデザイナーの名前です。 これにより、
new
オペレーターの必要性を思い出すことができます。
そうでない場合、コンストラクター内のコードは、別の言語で記述するコードのように見える可能性が高くなります。 この場合の値は、作成している新しいオブジェクトです。
関数を呼び出すためのルールNo. 4:
new
演算子で関数を呼び出すとき、
this
値はJavaScriptランタイムによって作成された新しいオブジェクトになります。 この関数がオブジェクトを明示的に返さない場合、これは暗黙的に返されます。
おわりに
関数呼び出しのさまざまな方法の違いを理解することで、JavaScriptコードを改善できることを願っています。
this
の値に関連するエラーをキャッチするのは簡単ではない場合があります。そのため、エラーの発生を事前に防ぐのが理にかなっています。