最近JSのことばかり言っているね。
ウェブ系とかだとJavaScriptを正しく扱っている場合が多いけれど、Java技術者だけで構築された社内システムなんかだと
あまりJavaScriptのことを知らないで関数を呼ぶだけの、単純なモノばかりだったりしている気がする。
Java研修やSQL研修などはやるけれどJavaScript研修なんかはやらなかったし、様々な現場でいろんな会社の人をみてきたけれど、やっぱりJavaScriptは簡易言語みたいな認識だった。
この手の人がjQueryを始めとするライブラリが全然違ってよくわからないと思ってしまいがちなのだけれども、
どうやらJavaScriptではない別の言語の文法のように思えるのかも知れないけれども、
JavaScriptの文法を知っていたらJavaScriptと違うモノと思うこともない。
Java風の書き方ではなく、JavaScriptは全然違う文法で面白いことがいっぱいできるんだよということを
サンプルコード付きで今回は説明したいと思う。
よくある書き方
function hogefunc() { document.write("よくある書き方"); } hogefunc(); /* 『よくある書き方』と表示される */
ごくごく普通の関数の宣言。
これしか知らない人は要注意。
変数に代入する書式で書く方法
var hogefunc2 = function() { document.write("変数に代入する場合と同じ書式でかく"); } hogefunc2();/* 『変数に代入する場合と同じ書式でかく』と表示される */
先ほどと効果は変わらないけれど、これも関数の宣言とおなじ。
JavaScriptでは関数は変数と変わらない。
Javaでオブジェクトを変数に入れられるのと同じように、JavaScriptでは関数を変数に入れられる。
言い換えればfunction hogeという宣言はhogeという関数入りの変数を作ったということ。
関数を戻り値で返す関数
var sotofunc = function() { return function() { document.write("関数を返す関数"); } } var func = sotofunc(); /* return の横にある関数が、func変数に代入される */ func(); /* 代入された関数が実行され『関数を返す関数』が表示される */
関数が変数と同じならば、他の数値や文字列と同じように扱うことができる。
たとえば関数の戻り値を関数にする方法などが可能。
関数で関数を返す使いドコロとしては、JavaにおけるFactoryメソッドなど処理を動的に切り替えるパターンなどに重宝できる。
関数を引数で関数に渡す
var hogefunc3 = function(func2) { func2();/* 引数で渡された何かしらの変数を関数として実行する */ } /* 関数を引数で渡す。『関数を引数で受け取る関数』と表示される */ hogefunc3(function(){ document.write("関数を引数で受け取る関数"); });
関数も変数だから、引数として関数を渡すことができる。
jQueryでも$(funciton(){});のような書き方があるけれども、正にこれ。
$はただそういう名前がついた関数で演算子ではない。
$の引数に関数を渡しているだけ。
これの使いドコロは、あらかじめ共通部分はつくっておいて、処理部分を後から作るとかができる。
Javaでは通常インターフェースを使われる。
関数内で生き残る変数(クロージャ)
var hogefunc4 = function() { var num = 0; /* 0を持つ変数を宣言 */ return function() { document.write(num); num++; } } var clfunc = hogefunc4(); /* returnの横にある無名関数を変数に代入する */ /* numはスコープから外れているので、clfuncに代入された関数が実行後も変数は生きている */ clfunc(); /* 0と表示される */ clfunc(); /* 1と表示される */ clfunc(); /* 2と表示される */
JavaScriptでは関数を関数内に定義することができる。
ある関数が関数の外にある変数にアクセスできるが、関数内にネストされた関数でもそれはできる。
関数を返す関数hogefunc4内にnumという変数があるが、
通常numの寿命はhogefunc4が実行されている間だが、
戻り値で返される関数がnumを使っている場合numの寿命が伸びる。
戻り値で返される関数が生きている間はnumは生き続けるし、またnumは戻り値関数のスコープの外にあるから
状態が保持される。
この場合、関数を実行するたびインクリメントされる。
状態を持った関数を作ることができる。
こういうものをクロージャと呼ぶ。
無名関数の実行
(function(){ document.write("無名関数を実行"); })();
無名関数という変数名がない関数があるが、それを無名関数のまま実行することができる。
文法的には単純なもので
functionで定義された関数を()で囲み一つの纏まりにする。
そのあとに()を付けて関数として実行をする。
ちなみにfunction hoge(){}と宣言された関数があるとして、
hoge();は関数の実行だけれども、hogeとだけ書いた場合はhoge関数の変数としてつかわれる。
だから、var hoge2 = hoge;のようにかけばhoge2に代入しているということになる。
関数が入ったモノに()をつけると実行するのだ。
new演算子を使ってインスタンスを生成
/* コンストラクタとなる関数を作り */ var Doubutsu = function(name) { this.name = name; } /* 関数を継承する。オブジェクトに対する操作になるのでこれは関数ではなくメソッドになる */ Doubutsu.prototype.writeName = function() { document.write(this.name); } var cat = new Doubutsu("猫"); cat.writeName();/* 猫と表示される */ var dog = new Doubutsu("犬"); dog.writeName(); /* 犬と表示される */
JavaScriptはオブジェクト指向だから、インスタンスを生成することができる。
でもJavaのようにクラスがあるわけではない。
関数のプロトタイプに関数を入れていくという形で継承していく。
だからプロトタイプベースのオブジェクト指向と言われる。
例ではthisとnewという2つのキーワードがでてきている。
thisはその関数が含まれるオブジェクトをさす。
このthisが活かされるにはnewが必要だ。
newはオブジェクトを新たに生成して、newの横にある関数を入れるという動きをしてくれるらしい。
だからthis.nameはnewで作られたオブジェクト内に居場所が作られることになる。
クラスをnewするように、一つの関数で別の状態を持つ別のインスタンスを作ることができるようになる。
スコープについて
スコープというのは変数の生き残る範囲のこと。
Javaだとメソッド内で宣言したものやforやifのブロック内で宣言されたものはその中でしか生きられない。
だが、JavaScriptは基本的に関数内のみのスコープしか無い(letとかあるけれどそれは別の時に)
言語によってはファイルごとにスコープが存在するものがあるが、JavaScriptにはファイルスコープはない。
別のjsファイルにまたがっていても同じ関数や変数が存在すれば後から来たものが上書きをしてしまう。
関数の外に書かれた変数はグローバル領域の関数となる。
恐らく数々のウェブアプリは多くのjsファイルを使うことだろう。
だが、あまり考えずにグローバルに関数や変数を作ってしまえば何処に影響が出るのかわかならい。
隠れたバグの温床に成ったりする。
だから、JavaScriptではクロージャや無名関数などを使って後々までにグローバルに残らないような書き方をする人が多い。
あとがき
オブジェクトについては今回かけなかったけれども、
オブジェクトはJavaScriptについては基本的なデータ構造で、Javaの世界観とは全く違うモノ。
これが奥が深くて面白い。
僕もまだ初心者だけれども、オブジェクトがわかればJavaScriptがわかるんじゃないかとおもう。
機会があればthisとかと絡めてオブジェクトについて書いていきたい。