第十一回: 同じ事のいろいろな書き方
JavaScriptは、いろいろな事情から同じ事を複数の書き方で書ける言語です。 自分が書く時は一通りのやり方だけ知っていれば十分ですし、 実際同じやり方で統一されている方が良いコードでもあります。
ですが、他人のコードを読む時には、他人の書き方を知っている必要があります。 また、JavaScriptの入門書や入門サイトを見る時も、それらで使われている書き方を知っている方が話は早い。
そこで第十一回は、これまでやった事と同じ事を別の書き方でやる、という回になります。
細かい理屈とか「何故?」とか考えるよりも、「ここまでやってきたこの書き方と同じ意味なのか」とこれまでの内容に対応づける事を意識して読んでください。
なお、今回紹介する書き方は、書けるようになる必要はありません。あくまで見かけたらここに戻ってきて解読出来ればいいのです。
そういう訳で目標は理解する事では無く、今回紹介するのと同じようなコードを見つけたらこのページの存在を思い出せる程度のレベルでOKです。
という事で課題とかも無しで軽く説明するくらいに留めます。軽く頭に通しておく位の気持ちで行きましょう。
関数の作り方
これまで、以下のような関数の作り方を見てきました。
var awa = function() {
MessageBox.show("むぇー");
};
JavaScriptでは、これと全く同じ事を以下のようにも書けます。
function awa() {
MessageBox.show("むぇー");
};
function
の後に関数を入れる変数名を書く、というものです。
違いは一行目ですね。 抜き出して比べてみましょう。
var awa = function() {
function awa() {
この二つは同じ意味になります。
なんで?という理由は無く、そういう決まりなのです。
実際に動かしてみましょう。
このようにfunction awa(){
とやると、awaという名前の変数を作って関数を入れます。
関数の文字や数字の受け取り方
お嬢様はセバスチャンに何かを言いつける時に、arguments
というのを使う事で数字や文字を渡せる、という話をしました。
これの関数を作る所は、以下のように間に変数を使っても同じ意味になりますね。
var awa = function() {
// 一旦変数に受け取る
var nakigoe = arguments[0];
MessageBox.show(nakigoe);
};
これと同じ事を、以下のように書けます。
var awa = function(nakigoe) {
MessageBox.show(nakigoe);
};
違うのは最初の二行ですね。一応抜き出して並べてみましょう。
これと、
var awa = function() {
var nakigoe = arguments[0];
この下のは、
var awa = function(nakigoe) {
同じ意味となります。
function(nakigoe)
のように、functionのあとのカッコの中に変数の名前を書いておくと、その変数でarguments[0]を受け取ったかのように振る舞います。
やってみましょう。
なお、複数何かを渡す場合、例えば以下みたいな場合は、
var lucy = function() {
// 3つ受け取る
var kochinkochin_ni = arguments[0];
var hieta = arguments[1];
var mugicya = arguments[2];
MessageBox.show(kochinkochin_ni);
MessageBox.show(hieta);
MessageBox.show(mugicya);
};
// 3つ渡す
lucy("こちんこちんに", "ひえた", "麦茶!");
こう書く事も出来ます。
var lucy = function(kochinkochin_ni, hieta, mugicya) {
MessageBox.show(kochinkochin_ni);
MessageBox.show(hieta);
MessageBox.show(mugicya);
};
// 3つ渡す
lucy("こちんこちんに", "ひえた", "麦茶!");
つまり、以下のコードが、
var lucy = function() {
// 3つ受け取る
var kochinkochin_ni = arguments[0];
var hieta = arguments[1];
var mugicya = arguments[2];
こうなるんですね。
var lucy = function(kochinkochin_ni, hieta, mugicya) {
実際に動かしてみましょう。
わけわからん、と思うかもしれませんが、これは決まりなので、理屈ではありません。
何も考えずに、これまでの書き方に翻訳して読むしかありません。
幸い、ツクールMVのプラグインはいろいろな事情によりarguments
を使う事の方が多いので、今回紹介した書き方はあまり出てこないと思います。
どちらかというとwebとか入門書で良く出てくる書き方ですね。
なお、覚えなくて良いですが、このfunction(nakigoe)
とかのようにfunction
の後のカッコの中に書いた変数を、「仮引数」と呼びます。「かりひきすう」って読みます。ひきすうってなんやねん。
C言語とかJavaにはarguments
が無いので、こういう用語があるのですが、我々は気にする必要はありません。
さらに覚えなくて良いですが、こういう風に同じ事を短く書けるヘンテコなルールを、「シンタックスシュガー」と呼びます。 長渕を怒らせない方針の我々は、横文字は使いません。
2つのやり方を合わせる
先に説明した2つの書き方は、両方同時に使う事が出来ます。 つまり、以下のようなコードは、
var awa = function() {
MessageBox.show(arguments[0]);
};
こう書いても同じ意味になります。
function awa(nakigoe) {
MessageBox.show(nakigoe);
};
関数用の変数をfunctionのすぐ後に書いて、arguments[0]用の変数をかっこの中に書きます。
つまりfunction awa(nakigoe)
ですね。
ツクールのプラグインではあまり見る事は無いこの書き方ですが、 それ以外の分野では良く使われます。
webの入門書などではこの書き方をしている事が多いと思いますし、 またUnityScriptやSecond Lifeのスクリプトなど、 JavaScriptに似せた独自の言語はこの書き方と似たような書き方とかしか許していない事が多いです。
現時点では慣れる必要はありませんが、ある程度ツクールMVなどで経験を積んだ後にこの辺に軽く慣れておくと、 よそのプログラミング言語を覚える時などには便利です。 たださすがにそんなのはその時やればいいと思うので、 このJavaScript入門シリーズでは課題とかはやりません。
関数を作ってすぐ実行する
関数は以下のようなコードで作って使える訳ですが、
これを両方同時にやる事が出来ます。以下のように書きます。
なんじゃこりゃ、って感じですね。最初のカッコをawaという変数に置き換えると、最後のカッコが関数を使う時のカッコである事が分かるのですが、あんまり細かい理屈を気にする必要はありません。
それよりは、
(function() {
で始めて、間にコードを何行か書き、次に
})();
で終わると、間の所がその場で実行される、という風に覚えてしまう方が良い。
つまり(function() {
全体でbegin
とかそういう感じの物だと思って、
})();
全体でendとかそういう物だと思う。
雰囲気としては、以下のコードを
(function() {
MessageBox.show("むぇー");
})();
以下のコードみたいな感じの何かだ、と思う訳です。
begin
MessageBox.show("むぇー");
end
例えば「そんな事は朝飯前だ」という時に、「朝」の「ご飯」の「前」、と覚えるのでは無く、「朝飯前」で簡単だぜって感じの意味だ、と覚えてしまうようなものです。
こういう、ごちゃっとした書き方で良くあるパターンをイディオムといいますが、 長渕準拠の我々はそんな言葉は使いません。
実際に間に何行かコードを書いた物を見てみましょう。こんな感じです。
最初と最後の行はあんまり気にしないのが読むコツです。
なんでこんな事するのか?(ここは分からなくて良いです)
さて、この関数を作ってすぐ実行する、というテクニックは、 一見使いみちがわかりません。
中に書いた事がその場で実行されるのだから、こんなテクニック使わずに書いたのと同じ結果になる訳です。 つまり、
(function() {
MessageBox.show("むぇー");
})();
と
MessageBox.show("むぇー");
は、全く同じ結果になります。念の為に実行してみましょう。
ほら、一緒でしょ?
では何故こんな事をするか、というと、変数がぶつからないようにする為です。
偶然プラグインAが作った変数awaに、プラグインBが別の目的でawaという変数を作ると、 この変数が上書きされてしまってプラグインAが壊れてしまう、という問題があります。
ですが、JavaScriptでは、関数の中の変数と外の変数は別の物、と扱われるという性質があります。 例えば以下のようなコードを実行してみましょう。
このように、関数の中の変数は関数の外の変数には影響を与えません。
このトリックを使う事で、ある範囲の外には影響を与えずに変数が使える、というのが、先程紹介したテクニックの目的になります。
例えば以下のようになります。
// 外のnakigoe
var nakigoe = "むぇー";
(function(){
// 関数の中のnakigoe
var nakigoe = "ダネー";
// この行のnakigoeは関数の中のnakigoe
MessageBox.show(nakigoe);
})();
// 関数の中のnakigoeは、外のnakigoeには影響を与えない
MessageBox.show(nakigoe);
そういう訳で、理屈はおいといて、(function(){
で始めて、最後に})();
と書いておくと、間違って同じ名前の変数を作っちゃう、という事を避ける事が出来ます。
この話は見た目ほど簡単じゃない
なんか難しそうな事言ってるがどうって事無い、と思うかもしれませんが、これはいろいろ変な事が起こる、JavaScriptのハマりポイントな事が知られています。 例えば、先程のコードで、daniki関数の先頭でvarをつけ忘れると結果が変わります。
これはJavaScriptには、変数を「作らずに使う」と、外の変数だと思う性質があるからです。
これにさらに、varがブロックスコープじゃないせいで、もっと変な例がいろいろと作れる事が知られています。
中級者にも意味が分からないような例も作れてしまう。
あまりにも意味不明なので、次のJavaScriptのバージョンではvarを使うのは禁止にして、 もっと分かりやすく振る舞うletという物にしよう、という事が決まっているくらいです。 実際ツクールMVでも、いろいろ詳しければこのletを使う事は出来るようです。
ですが皆使っていないようなので、本シリーズではletの解説しません。
2018年現在は、このvarとletのちょうど過渡期で、だいたいはletに移り終わったがツクールMVなどのように一部varも生き残っている、という状態です。 この感じだと、もう二年くらいすればたぶんみんなletになりそうです。
という事で、varのこの辺の事を現在頑張って理解してもすぐ使えなくなるので、このシリーズではあまり真面目に解説してません。 ただ一応用語を軽くは説明しておきます。
この、関数の中と外の変数がぶつからない現象はスコープ
と言われています。
そしてこの関数を作ってすぐ実行する事でスコープを作るトリックを、IIFE(Immediately-Invoked Function Expression)と呼びます。
こんな言葉は知らなくていいです。
続:理解しなくてもいい「関数を作ってすぐ実行する」の理屈
理解しなくていいので解説しなくていいか、と思ったのだけど、ツクールMVのプラグインの入門サイトは意外とこの辺の解説が無いので、ここに書いておく事にします。 理解しなくて良いのですが、いつか興味が湧いた時の為に。
ここまでで、awa
という変数に関数が入っている場合、後に()
をつけて、awa();
とすると関数が使える、という話をしました。
次に、関数は
function() {
MessageBox.show("むぇー");
}
という風にすると作れる、という話をしました。 今回の話では関数の中身は重要では無いため、見やすくする為に便宜上、関数の中身の所を「…」と書く事にします。
そうすると以下のように一行で書けます。
function(){...}
さて、関数を使うには()
をつければよかったのだから、この作った関数の後ろに()
をつければ関数が実行されるはず、つまり、こうすれば良さそうですが、
function(){...}()
実際はこれはエラーになってしまいます。
これがエラーになる理由は、functionというのには厳密にはfunction expressionという物とfunction declarationという物があって、 何もない所でfunctionで始まると、これはdeclarationだとみなす、という決まりになっているからです。
そしてdeclarationは終わりに()
をつけたり出来ない、という決まりになっています。
(なお、functionをイコールの右側に置くと、それはexpressionになります。)
さて、declarationとみなされてしまうとかっこを後ろに付けても関数が実行出来ない訳ですが、 JavaScriptでは、かっこで囲まれた部分はexpressionとみなす、という決まりがあります。
そこでこの関数を作っている所はfunction expressionだぞ、と処理系に教えてやる為に、 かっこで関数を作っている所をかくくってやると、function expressionに出来ます。
つまり、
(function() {...})
という風に書くと、ここはfunction expressionだと理解してくれます。
そこでfunction expressionに無事出来たので、その後ろに()
をつけてやれば、この今作った関数を実行する事が出来ます。
(function() {...})();
これがIIFEの仕組みです。
なんでdeclarationとexpressionが違うのか、とかそういう話をすると、hoistedとはなんぞや、とか、そういう話が出てくるのですが、 突き詰めていくと結局は「そう決まっているから」になるので、この位で終わらせておいても納得感は大差無いです。
まぁどうでもいいです。
「関数を作ってすぐ実行する」で知っておいて欲しい事
知らなくていい事ばかり書いたので、知っておいて欲しい事をまとめておきます。
知っておいて欲しい事としては、
- 複数のプラグインでたまたま同じ名前の変数を作ろうとするとぶつかる事がある
- ぶつかったらどっちかの名前を変えればいいだけだし、現実的にはほとんどぶつかる事は無い
- 現実的にはほとんどぶつからない事だけど、神経質な人たちは変なトリックでぶつからないようにしている
- 読む時はそんなトリック無視して読んでOK(だいたいぶつからないから!)
という位です。
第十一回まとめ
- これまでやった関数の作り方と同じ事を、違う書き方で書ける
function awa()
とかfunction (nakigoe)
とか
- 理解はしないで良いので、遭遇したらこのページに戻ってきて解読しよう
- 関数を作ってすぐ実行する、という書き方もある
(function(){
で始める- 間にいろいろコード書く
})();
で終わる
- 3はかっこつけてるだけなので気にしないでOK
今回の内容は基本的には理解する必要は無いのですよね。 何故解説したかといえば「見かけた時に拒否反応が出ないように」という程度のものです。
出てきた時に、「あー、これ見たわー、昔見たわー。忘れたけど。」と思えたら第十一回の目的は達成出来たと言えます。