【JavaScript】関数定義における4つの注意点
JavaScriptにおける関数の定義はシンプルだが
実際にコーディングに取り組んでみると
思わぬ不具合に悩まされることもある
よくある誤りを避けるヒントを4つ紹介する
はーいよろしくです
return命令は途中で改行しない
JavaScriptでは「基本的に」セミコロンで文末を認識する
ただしセミコロンを省略した場合にも
適宜、前後の文脈から分の末尾を判断する
つまりJavaScriptでは文末に
セミコロンを付けることが好ましいが「必須ではない」
このような寛容さは
基本的にJavaScriptのハードルを下げる要因になるものだが
時として要らぬ混乱をもたらす原因にもなる
var triangle = function(base, height) { return base * height / 2; } document.writeln('三角形の面積:' + triangle(5, 2)); //5
これは呼び出し元に三角形の面積、式「base * height / 2;」の結果を
戻すことを意図したコードだが
実際に呼び戻してみると意図したような結果は得られない
おそらく実行結果は「三角形の面積:undefined」となるはず
うん、なりました!(・∀・)
上のコードは実際にはセミコロンが自動的に補完されて
下記のような解釈がされている
return; base * height / 2;
結果triangle関数は戻り値として(デフォルトの)undefinedを返し
後続の式「base * height / 2;」は無視されている
意図したように動作させるには途中の改行を削除する必要がある
関数はデータ型の一種
JavaScriptにおいて関数はデータ型の一種である
var triangle = function(base, height) { return base * height / 2; } document.writeln('三角形の面積:' + triangle(5, 2)); //5 triangle = 0; document.writeln(triangle); //0
ほかのプログラミング言語を学んだことがある方ならば
上記のコードが直観的に「間違いである」と感じるはず
うーん‥私ほかの全然学んだことないからなー(・∀・;)
まあいいや続きやる
- 関数と同名の変数が定義されたことに問題があるならば「triangle = 0;」はエラーになるはず
- 関数をあたかも変数のように呼び出していることが問題ならば「document.writeln(triangle);」で問題となるはず
と思うかもしれない
(私は全く思わなかったけど‥ははは)
しかし、これは正しいコードである
JavaScriptにおいて関数は「データ型の一種」
そのため、triangle関数を定義すると
じつは「triangleという変数に関数型のリテラルを格納する」ことと
同じ意味なのである
したがって「triangle = 0;」で変数triangleに
改めて数値型の値をセットしても間違いではない
また、数値型に書き換えられた変数を参照している
「document.writeln(triangle);」のコードも正しい
関数のこの性質を利用して、あからさまに
以下のようなコードを書くこともできる
var triangle = function(base, height) { return base * height / 2; } document.writeln(triangle);
function (base, height) {
return base * height / 2;
}
ここではtriangleを変数として参照しているので
triangleに格納された関数定義がそのまま文字列として出力されている
(厳密にはFunctionオブジェクトのtoStringメソッドが呼び出されて
文字列表現に変化されたものが出力されている)
function命令は静的な構造を宣言する
もっとも、function命令による関数定義は
いわゆる代入演算子(=)による変数への代入とは
異なる点もあるため要注意
document.writeln('三角形の面積:' + triangle(5, 2)); //5 function triangle(base, height) { return base * height / 2; }
「関数定義が変数定義である」という前提に基づけば
1行目の時点でまだtriangle関数(関数定義を格納した変数triangle)は
宣言されていないはずなので
このコードはエラーにならなければならない
しかし、実際にはこのコードを実行してみると
正しくtriangle関数が実行されて結果も表示されることが確認できる
これはfunctionが動的に実行される命令ではなく
静的な構造を宣言するためのキーワードであるためである
「静的な構造」と言ってしまうとわかりにくいかもしれないが
要は
「function命令はコードを解析/コンパイルするタイミングで、関数を登録している」
ということである
したがって実行時にはすでにコード内の構造の一部として
triangle関数をどこからでも呼び出すことができる
関数リテラル/Functionコンストラクタは実行時に評価される
さっきの例を関数リテラル/Functionコンストラクタで
書き換えたらどうなるだろうか
document.writeln('三角形の面積:' + triangle(5, 2)); //5 var triangle = function(base, height) { return base * height / 2; }
結果は実行エラーになる
function命令とは異なり
関数リテラル/Functionコンストラクタ実行時(代入時)に評価される
ということである
したがって関数リテラル/Functionコンストラクタで
関数を定義する場合には
「呼び出し元のコードよりも先に記述する」必要がある
なるほどりょうかいです