前回の記事では、 慣れないことをしたため、文体がやや硬くなってしまい、あとから見ると読みにくく感じた。あと、こういう文章を書くときは最後にまとめを書くべきだなとも思ったので、ここで書いておこう。
箇条書きでまとめると、JavaScriptのthisとは、
- 関数呼び出しがobj.method() という形式の場合、objである。
- そうでなく、単にfunc() という呼び出しである場合、グローバルオブジェクトである。
という非常にシンプルなものであった。基本的には、このルールがクロージャなどの他の概念とともに表れ、理解を妨げているのだと思う。
実はそれだけじゃないよJavaScriptのthis
さて、今回記事をわざ新しく書いたのは、実は前回説明していないthisが存在するからだ。
前回、obj.method()という形式での関数呼び出しと、func()という形式での関数本体でのthisのルールは説明した。これでほとんど十分なのだけど、実はJavaScriptにはもう一つ、関数本体が実行される構文が存在する。それは、
new Class();
こんな構文。オブジェクトの初期化というやつだ。
え?newがついてるだけで関数呼び出しと同じじゃないの?と思う人がいるかもしれないが、違う。JavaScriptではnew演算子とか呼ばれていたりする、関数呼び出しとは別の演算だと思っていい。
さて、new演算子とは新しいオブジェクトを作る演算子である。new演算子で呼び出される関数(コンストラクタとよぶ)は、新しいオブジェクトを作り出し、初期化するために使われる。例えばこんなふうに。
var Class = function() { this.x = 10; }; var foo = new Class(); foo.x; //10
このコンストラクタについてよく知らない人のために、この記事の下の方にコンストラクタについても簡単に説明したので、そういう人はそっちも読むといいかもしれない。
さて、もうお分かりだと思うが、new演算子を伴い関数をコンストラクタとして呼び出した場合、その関数中でのthisはコンストラクタが作り出す新しいオブジェクトなのである。まあ、当然だよね。というわけで、JavaScriptのthisのルールは、冒頭のルールに加えて
- 関数呼び出しがobj.method() という形式の場合、objである。
- そうでなく、単にfunc() という呼び出しである場合、グローバルオブジェクトである。
- そうでなく、new Class() という呼び出しである場合、コンストラクタClassが作る新しいオブジェクトである。
という形になる。これでほぼ完璧だ!
ほぼといったのには、実はまだthisが変わるルールがひとつあるからだ。でもこのルールはある意味、「プログラマが自分の意志でthisを変える」とも言えるとても特殊な、ルール変更とも言えるルールだ。そのルールはFunction.prototype.applyという関数によって適応される。この関数についてはthisを変えるというだけあって、thisの勉強をしているこの記事の読者とは当分縁がないものだと思うので、今回は説明しない。
コンストラクタってなに?
ところで、コンストラクタによってオブジェクトを作るのはなぜだろう。すべてオブジェクトリテラル{}で作ってしまえばいいのに。
例えば、我々人間は必ず名前と年齢を持つ。
var one = { name:'Alice', age:18 }; var someOne = { name:'Bob', age:20 }; one.name; //Alice someOne.name;//Bob
例えばこんなふうにだ。
さて、ここには少し不便な点がある。oneもsomeOneも、nameとageという共通の性質を持つのに、それぞれ別個にわざわざnameとageを指定してあげなければならない(例えば、うっかり片方をnamaとしてしまったらどうなるだろうか!)。また、同じ人間である二人なのに、そのことはnameとageという同じ性質を持ってるということでしか確認できない。nameやageを見なくても彼らはたしかに人間なのだ。
このような不便な点は、JavaScriptではnew演算子により起動されたコンストラクタによって解決する。同じコンストラクタによって作られたオブジェクトを、同じ種類(オブジェクトの種類のことを、クラスとか呼んだりもする)である、と考えるのだ。さらに、その種類のオブジェクトのもつ性質はコンストラクタが設定してあげる。こんなふうに。
var Human = function(name, age) { this.age = age; this.name = name; }; var one = new Human('Alice', 18); var someOne = new Human('Bob', 20); one.name; //Alice someOne.name;//Bob
Humanを用いて作られたオブジェクトは必ず、nameとageを持つ。また、オブジェクトが人間であることはHumanを用いて作らてたという事実(JavaScriptでは、constrnctorプロパティやinstanceof演算子によって判別できる)によってわかる。 これが、オブジェクトのコンストラクタだ。
本当は、prototypeによる高度な特徴つけの話もあるけど、それまた今度。