みこむらめもむらむらむら

なんかHTML5とかJS勉強とかやりながらめもを綴るブログ

【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コンストラクタで
関数を定義する場合には
「呼び出し元のコードよりも先に記述する」必要がある

なるほどりょうかいです