【JavaScript 中級講座】コンストラクタ関数
本講座では、JavaScriptのコンストラクタ関数を学習する。 コンストラクタ関数はJavaScriptの本源ではないが、 限りなくそれに近い。 そして今後、JavaScriptの本源(オブジェクトとプロトタイプチェーン)を学習する時、 ここで学習した内容が役に立つ。 では、頑張って学習しよう。
コンストラクタ関数が無いとどうなる?
コンストラクタ関数を学習する前に、 そもそもコンストラクタ関数が無いと何が不便なのか? という事を2段階に分けて学習する。 では、第一段階の不便なプログラムを掲載する。
002//オブジェクトを生成
003var a = { x : 90, y : 10 } ;
004var b = { x : 85, y : 20 } ;
005var c = { x : 80, y : 30 } ;
006
007//配列を宣言
008const array = [ a , b , c ] ;
009</script>
今、変数a、b、cを宣言してオブジェクトを代入した。 3つのそれぞれのオブジェクトは、xプロパティとyプロパティを持っている。 念のためコンピューター上のメモリを見る。
変数 | 参照先 |
a | { x : 90 , y : 10 } |
b | { x : 85 , y : 20 } |
c | { x : 80 , y : 30 } |
3つのオブジェクトが存在する。 実はこの3つのオブジェクトは、「座標」をイメージして作成した。 xプロパティがx座標で、yプロパティがy座標である。 現在、a座標、b座標、c座標が存在して、 それぞれ値が入っているイメージである。 ここで考えたい。
この3つのオブジェクトは、同じ構造を持っている。 同じ構造のオブジェクトを3つ作るのは効率が良くないのでは? あるいは、aに代入したオブジェクトの構造を変更したら、 他のオブジェクトも変更する必要がある。 あるいは、もっと良くない事がある。 座標はこの3点だけではない。利用する座標が増えたら、どうなるのだろうか? 座標が増えるたびに同じ構造を持ったオブジェクトを作るのだろうか? それは大変である。
色々問題のあるプログラムである。 特に、座標がd、e、fと増えていったら、なにやら不便である。 私は不便と感じるが、もっと不便に感じてほしいので、 もう一つプログラムを掲載する。
オブジェクトにinfo()関数を定義した。
002//オブジェクトを生成
003var a = {
004 x : 90,
005 y : 10,
006 info : function ( ) {
007 return x +":"+ y ;
008 }
009 } ;
010var b = {
011 x : 85,
012 y : 20,
013 info : function ( ) {
014 return x+":"+y ;
015 }
016 } ;
017var c = {
018 x : 80,
019 y : 30,
020 info : function ( ) {
021 return x+":"+y ;
022 }
023 } ;
024
025//配列を宣言
026const array = [ a , b , c ] ;
027</script>
このプログラムを見て、私は逃げ出したくなるのだが、 みなさまはどうだろうか? 不便さを感じると思う。座標を使うとき、 同じ構造を持ったオブジェクトを1から作るのは、 不便である。
newでオブジェクトを製造する
ここからコンストラクタ関数を学習する。 まず、とんでもなく簡単なコンストラクタ関数を見て、 それから徐々に難易度を上げていく。
002//コンストラクタ関数
003var User = function ( ) { }
004
005//オブジェクトを生成
006const iori = new User ( ) ;
007
008//[object]
009alert ( iori ) ;
010</script>
プログラムを解説する。 3行目で、関数を定義して変数Userへ代入する。 これは、Userという名前で関数を定義しているに過ぎない。 3行目のイコールの右側を解説する。
まず、引数無し、戻り値無しの関数を宣言している。 関数の処理は記述されていない。 つまり、「空の関数」と言える。 その空の関数を定義したあと、変数Userへ代入する。 これが、3行目の解説である。
002//コンストラクタ関数
003var User = function ( ) { }
004
005//オブジェクトを生成
006const iori = new User ( ) ;
007
008//[object]
009alert ( iori ) ;
010</script>
6行目は超重要である。 newを利用してオブジェクトを作成し、 オブジェクトを変数ioriへ代入する。
new 関数からオブジェクトを作成する時に利用する。
newを利用すると、オブジェクトを生成できる。
ここで一度、立ち止まる必要性を感じざるを得ない。 そもそも、「オブジェクトの生成って何?」と疑問を抱かざるを得ない。 もう一度、オブジェクトの生成を考えざるを得ない。
003var a = { x : 90, y : 10 } ;
004var b = { x : 85, y : 20 } ;
005var c = { x : 80, y : 30 } ;
こちらは、今解説しているプログラムとは別のプログラムである。 3行目から5行目まで、オブジェクトを生成している。 このように中括弧を記述すると、それがオブジェクトとなる。 これが、オブジェクトを生成する方法の一つである。
オブジェクトの生成方法は複数存在する。 今回学習する、newもその一つである。
newを利用すると、 オブジェクトが生成される。 では、別のプログラムをご紹介する。
002//コンストラクタ関数
003var User = function ( ) { }
004
005//オブジェクトを生成
006const iori = new User ( ) ;
007
008//オブジェクトを生成
009const IORI = { } ;
010</script>
6行目でnewを利用して、オブジェクトを生成する。 9行目で、オブジェクトを生成する。 変数ioriが参照しているのもオブジェクト。 変数IORIが参照しているのもオブジェクト。
変数 | 参照先 |
iori | オブジェクト |
IORI | オブジェクト |
2つのオブジェクトがコンピューター上に存在する。 2つのオブジェクトに違いはあるのだろうか? 実は、変数iorが参照するオブジェクトと、 変数IORIが参照するオブジェクトは、 どちらもオブジェクトではあるが、 プロパティの値が異なる。 プロパティの値が異なるが、オブジェクトはオブジェクトである。 今後、どのプロパティの値が異なるか学習する。 しかし、今はプロパティの値を気にする必要は無い。
ここで覚えて頂きたいのは、newを利用すると、 オブジェクトが生成するという事である。
では、もともと学習していたプログラムへ戻る。
002//コンストラクタ関数
003var User = function ( ) { }
004
005//オブジェクトを生成
006const iori = new User ( ) ;
007
008//[object]
009alert ( iori ) ;
010</script>
6行目を解説する。newを利用すると、 特定の関数からオブジェクトを生成できる。 今回は、User関数からオブジェクトを生成した。 User関数はただの関数である。しかし、 「オブジェクトを生成する関数」として定義された関数である。
コンストラクタ関数とは コンストラクタ関数とは、 オブジェクトを生成する事を予定して定義された関数である。
コンストラクタ関数も関数も、定義の方法は同じである。 見た目に違いは無いように感じる。 ただ通常の関数は、関数として実行するために定義する。 一方、コンストラクタ関数は、newによりオブジェクトを生成する事を 予定して定義する。 どちらも関数である。 コンストラクタ関数を定義する時、 「この関数はコンストラクタ関数として定義しました」とコメントに記述する。 また、関数名が「User」というように、 最初の文字が大文字で始まっている関数は、 「コンストラクタ関数として利用される事」を予定して定義されている。 もちろんコンストラクタ関数を通常の関数のように実行する事ができる。 しかし、ここらへんは空気を読む。
コンストラクタ関数の機能 コンストラクタ関数は、 オブジェクトを生成する。 オブジェクト製造機と呼んで良い。
コンストラクタ関数の役割は、オブジェクトを製造する事である。 (今後の事を考えると)ここが一番重要である。 では、少しずつコンストラクタ関数を豊かにしていく。
コンストラクタとプロパティ
コンストラクタ関数に、「コンストラクタ」と「プロパティ」を追加する。
002//コンストラクタ関数
003var User = function ( name ) {
004 this.name = name ;
005 }
006//オブジェクトを生成
007const iori = new User ( "IORI" ) ;
008
009//[IORI]
010alert ( iori.name ) ;
011</script>
プログラムを実行すると「IORI」と表示する。
3行目でコンストラクタ関数Userを定義する。 関数名がUserというように大文字で始まっているので、 「コンストラクタ関数だな」と察する。 関数Userは引数nameを受け取り、4行目で引数のnameを
this.name へ代入する。 thisはオブジェクト自身を表す。今は先へ進む。
7行目でnewを利用してUser関数からオブジェクトを生成し、 そのオブジェクトを変数ioriで受け取る。 User関数を実行する時、引数に文字列「IORI」を渡した。
003var User = function ( "IORI" ) {
004 this.name = "IORI" ;
005 }
006//オブジェクトを生成
007const iori = new User ( "IORI" ) ;
User関数の引数に文字列「IORI」が渡される。 この文字列は、
this.name へ代入される。 thisはオブジェクトを意味する。ここでコンピューター上のメモリを見る。
変数 | 参照先 |
iori | { name : "IORI" } |
変数ioriはオブジェクトを参照する。 変数ioriが参照するオブジェクトには、nameプロパティが存在する。 このnameプロパティは
this.name である。 thisはオブジェクト自身を意味する。 this.name に、 引数で受け取った文字列「IORI」が入る。
002//コンストラクタ関数
003var User = function ( name ) {
004 this.name = name ;
005 }
006//オブジェクトを生成
007const iori = new User ( "IORI" ) ;
008
009//[IORI]
010alert ( iori.name ) ;
011</script>
10行目で、変数ioriが参照するオブジェクトのnameプロパティをダイアログに表示する。 変数ioriが参照するオブジェクトのnameプロパティの値は「IORI」なので、 「IORI」と表示する。
メソッドを定義する
コンストラクタ関数の中に、関数(メソッド)を定義する。
002//コンストラクタ関数
003var User = function ( name ) {
004 this.name = name ;
005 this.run = function (){
006 return "run" + this.name ;
007 }
008 }
009//オブジェクトを生成
010const iori = new User ( "IORI" ) ;
011
012//[runIORI]
013alert ( iori.run() ) ;
014</script>
プログラムを実行すると「runIORI」と表示する。
3行目で、コンストラクタ関数Userを定義する。 User関数は引数にnameを受け取り、 引数で受け取った値をnameプロパティへ代入する。 5行目で、User関数の中にrun()関数を定義する。
メソッド オブジェクト、コンストラクタ関数の中に定義された関数を、 メソッドと言う。
5行目でrun()メソッドを定義している。 メソッドは超重要な用語であるが、 本講座ではメソッドという用語は利用せず、「関数」と記載する(不必要な妙なこだわり)。
User関数内に定義されたrun()関数を見る前に、プログラムを先へ進める。
002//コンストラクタ関数
003var User = function ( name ) {
004 this.name = name ;
005 this.run = function (){
006 return "run" + this.name ;
007 }
008 }
009//オブジェクトを生成
010const iori = new User ( "IORI" ) ;
011
012//[runIORI]
013alert ( iori.run() ) ;
014</script>
10行目でUser関数からオブジェクトを生成して、変数ioriへ代入する。 User関数の引数に文字列「IORI」を渡す。 この文字列はオブジェクトのnameプロパティへ代入される。
変数 | 参照先 |
iori | { name : "IORI" , run : function } |
変数ioriはオブジェクトを参照する。 オブジェクトには、nameプロパティとrun()関数が存在する。
プログラムの13行目で、run()関数を実行する。
004 this.name = name ;
005 this.run = function (){
006 return "run" + this.name ;
007 }
008 }
6行目で、文字列「run」と this.name を文字列結合して、戻す。
this.name の this は、オブジェクト自身を意味する。
つまり、this.name はオブジェクト自身のnameプロパティを意味する。
変数 | 参照先 |
iori | { name : "IORI" , run : function } |
変数ioriが参照するオブジェクトのnameプロパティには、文字列「IORI」が入っている。 これが、this.nameである。 run()関数を実行すると文字列「runIORI」が戻る。
コンストラクタ関数の利便性
最後に、複数のプログラムを見比べて、 コンストラクタ関数の利便性を論じる。
002//コンストラクタ関数
003var Position = function ( x , y ) {
004 this.x = x;
005 this.y = y;
006 this.info = function ( ) {
007 return this.x
008 + ":"
009 + this.y;
010 }
011}
012var a = new Position ( 90 , 30 ) ;
013var b = new Position ( 85 , 20 ) ;
014var c = new Position ( 80 , 10 ) ;
015
016alert ( a.info () ) ;
017alert ( b.info () ) ;
018alert ( c.info () ) ;
019</script>
プログラムを実行すると「90:30」 「85:20」 「80:10」と表示する。
3行目でコンストラクタ関数Positionを定義した。 関数Positionは座標を表現するオブジェクトを生成する。 関数Positionには、xプロパティ、yプロパティが定義されており、 この2つのプロパティが座標を意味する。 また関数内に、info()メソッド(関数)も定義されている。 info()メソッド(関数)を実行すると、 xプロパティとyプロパティの値を文字列にして戻す。
12行目から14行目で、newを利用して、 コンストラクタ関数Positionから、オブジェクトを製造して変数へ代入する。 12行目から14行目を実行すると、3つのオブジェクトが製造される。
変数 | 参照先 |
a | { x : 90 , y : 30 , info : function } |
b | { x : 85 , y : 20 , info : function } |
c | { x : 80 , y : 10 , info : function } |
どうだろう?便利である。 この便利さを強調するために、別の不便プログラムを見る。
002//オブジェクトを生成
003var a = {
004 x : 90,
005 y : 10,
006 info : function ( ) {
007 return x +":"+ y ;
008 }
009 } ;
010var b = {
011 x : 85,
012 y : 20,
013 info : function ( ) {
014 return x+":"+y ;
015 }
016 } ;
017var c = {
018 x : 80,
019 y : 30,
020 info : function ( ) {
021 return x+":"+y ;
022 }
023 } ;
024
025//配列を宣言
026const array = [ a , b , c ] ;
027</script>
変数a、b、cに同じ構造のオブジェクトを代入する。 もし新たなオブジェクトd、e、fを追加する場合・・・、考えただけで寒気がする。 もし、オブジェクトの構造を今後変更する場合・・・、無理です。 では、便利プログラムを見る。
002//コンストラクタ関数
003var Position = function ( x , y ) {
004 this.x = x;
005 this.y = y;
006 this.info = function ( ) {
007 return this.x
008 + ":"
009 + this.y;
010 }
011}
012var a = new Position ( 90 , 30 ) ;
013var b = new Position ( 85 , 20 ) ;
014var c = new Position ( 80 , 10 ) ;
015
016alert ( a.info () ) ;
017alert ( b.info () ) ;
018alert ( c.info () ) ;
019</script>
まず、オブジェクトの構造を定義した関数Positionを宣言する。
そして new を利用して、関数Positionからオブジェクトを作ると、 同じ構造を持ったオブジェクトが次々に製造できる。 コンストラクタ関数を利用すると、 新たなオブジェクトd、e、fを追加する事は至極容易である。
002//コンストラクタ関数
003var Position = function ( x , y ) {
004 this.x = x;
005 this.y = y;
006 this.info = function ( ) {
007 return this.x
008 + ":"
009 + this.y;
010 }
011}
012var a = new Position ( 90 , 30 ) ;
013var b = new Position ( 85 , 20 ) ;
014var c = new Position ( 80 , 10 ) ;
015
016//追加
017var d = new Position ( 10 , 90 ) ;
018var e = new Position ( 20 , 80 ) ;
019var f = new Position ( 30 , 70 ) ;
020
021alert ( a.info () ) ;
022alert ( b.info () ) ;
023alert ( c.info () ) ;
024</script>
17行目から19行目に、オブジェクトd、e、fを追加した。 オブジェクトを生成する場合、コンストラクタ関数をnewするだけである。
本講座は以上である。