今更聞けないJavaScript講座第5回「オブジェクトと変数アクセス」

どうも、シノだ。

前回、再帰処理かjQuery編に入るかと書いたが…すまない、全然違うものを扱う。

重要なものが抜けていることに気がついた。あぶなかった。

今回は、オブジェクトと、変数アクセスの仕組みについて扱っていく。

前半は変数の拡張、後半は変数の詳細な中身になる。

それぞれ、該当の知識はある前提で進めていくので、心配な方は先に復習しておこう。

スポンサーリンク

前回の内容

前回は、主に関数を扱った。また、HTMLとの値の受け渡しについても一部紹介した。

今回は、第1回で解説した「変数」の考え方をふんだんに使用する。冒頭にも書いた通り、心配な方は先に復習をしておいてほしい。

オブジェクト

オブジェクトは、英語として捉えると「モノ」(Object)という意味になる。

まあ、その名の通り、様々な変数を、モノとして一つにまとめて定義しよう、というイメージで構わない。

例えば、本をプログラム上で表したいとしよう。

本にはどんな情報があるだろうか。タイトル、著者、値段、などなど。上げ始めたらキリがない。

で、これらをわざわざ変数titleを用意して、変数authorを…なんてやってたら、本が増えたときに大変だし、そもそも分かりづらい

そこで、これらの情報全てを持った一つの変数として定義してしまおう、という考えだ。この場合の変数を、オブジェクトと呼ぶという認識で構わないだろう。

オブジェクトの定義方法

では、その本に関するオブジェクトを具体的に定義してみよう。

var book = {
    title: "本のタイトル",
    author: "作者",
    price: 1000,
};

これで、「本のタイトル」というタイトルで、「作者」という作者が書いた、1000円の本を表現することができた。

このように、最初は変数と同じようにvar オブジェクト名オブジェクトを入れる大きな箱を用意する。

そして、中括弧で囲み、中にプロパティ: 値という組で定義する。プロパティが小さな箱の名前、値がその箱に入っているデータだ。

こうすると、オブジェクトという大きな箱に、プロパティという小さな箱を作って、値をまとめて定義できるのだ。

で、表すだけでは無意味なので早速使ってみよう。このアクセス方法にはいくつか種類がある。

連想配列

まず、連想配列と呼ばれる方法から。配列は覚えているだろうか?変数名[添え字]でアクセスした変数の列だ。

この、アクセスするための添え字の代わりに、プロパティ…オブジェクトを定義した際のコロンの左側、こいつを文字リテラルとして大括弧に書いてあげる

そうすると、その中身にアクセスすることができる。例えば、上の例で本のタイトルをコンソールに表示したい場合は、以下のように書けばいい。

console.log(book["title"]);

これで、ページを表示したらコンソールに「本のタイトル」と表示されるはずだ。

ドットを使用したアクセス

さあ、こちらが本命だ。

オブジェクトに定義された中身には、ドットを用いることでもアクセスができる。

上と同じように、本のタイトルを表示してみよう。

console.log(book.title);

これでも、同じように「本のタイトル」と表示されると思う。

このように、オブジェクト名にドットをつけてその後にプロパティを指定すると、その値を持ってこれる。

値以外にも…

このオブジェクトは、値以外にも入れることができるものがある。

オブジェクト

一つは、他のオブジェクト。以下の例を見て欲しい。

var book = {
    title: "本のタイトル",
    author: {
        name: "作者名",
        birth: "誕生日"
    },
    price: 1000,
};

authorプロパティに、更に別のオブジェクトを突っ込んでみた。これでも問題ない。

アクセスの方法もなんとなくお分かりかと思うが、例と詳細な説明を書いていこう。

console.log(book.author.name);

コンソール出力の引数を見て欲しい。二つドットでつながれている。

まず、大元のオブジェクトbookを用意する。で、その中のプロパティauthorにアクセスする。ここまでは上と同じだ。

で、authorに何が入っているかというと…これまたオブジェクト。というわけで、その中のプロパティnameをさらにドットでつないでアクセスしているのだ。

関数(メソッド)

二つ目は、なんと関数

そう、オブジェクトに関数も入れることができてしまうのだ。

先に書き方は以下の通りだ。

var book = {
    title: "本のタイトル",
    author: {
        name: "作者名",
        birth: "誕生日"
    },
    price: 1000,
    showAllData: function(){
        console.log("タイトル : " + this.title);
        console.log("作者名 : " + this.author.name);
        console.log("値段 : " + this.price + "円");
    }
};

showAllDataというプロパティを追加した。値を見ると、先頭にfunction(){とある。

このように、関数名を省略した書き方で関数を書く。そうすると、そのプロパティ名で呼び出すことができる関数を定義できるのだ。

言葉のお話だが、こういったオブジェクト内に定義された関数のことを、特別にメソッドと呼ぶ。

で、このメソッドの中身。なんかthisというやつが何回も出てきている。

これは、この関数が呼び出されているオブジェクト自身を指している。まさに、「これ」だ。

これまでの内容で…

さあ、これで今まで解説していたものが色々と分かってくるのではないかと思う。

例えば、HTMLのID名「test」というテキスト欄に入力された文字列を持ってきたいとき、以下のように書いたのを覚えているだろうか。

document.getElementById("test").value

これ、今なら全部理解できると思う。

まず、先頭のdocument。これは、元々用意されたオブジェクトだ。HTML文章全体を表している。

次に、ドットでつないでgetElementById("test")。これは、documentオブジェクトに定義されているメソッドだ。意味は、引数に渡した文字列をIDに持つHTMLタグ情報を、オブジェクトとして持ってくる

で、ここまででHTMLタグのオブジェクトだ。さらに、その中にvalueというプロパティがあり、その中に入力された値が入っている、というわけだ。

ここまで、大丈夫だろうか?次の内容はさらに複雑になっていくので、いったんここまでで理解しておいて欲しい。

変数アクセス

突然だが、謝罪を一個。

今まで、変数には値が入っているというような解説をしてきた。しかし、厳密には異なる。申し訳ない。

なんでそう解説したかだが、今まではそういう理解で問題なかったからだ。

しかし、上で解説したオブジェクトが絡んでくると、そのままの理解では色々と不可解なことが発生してしまう

そのため、ここからは厳密な変数の動き方を見ていこう。

クイズ

いきなりだが、クイズを出そう。

以下のJavaScriptを実行すると、コンソールにはどう表示されるだろうか。

var x = 10;
var y = x;
console.log(y);
x += 10;
console.log(y);

まあ、これは簡単だ。10が二つ表示される。

では、次の内容はどうだろうか。

var obj1 = {
    x: 10,
    y: 20,
    showNum: function(){
        console.log("x = " + this.x + ", y = " + this.y);
    }
};
var obj2 = obj1;
obj2.showNum();
obj1.x = 30;
obj2.showNum();

やってることはそんなに難しくない。

まずオブジェクトを1個用意している。

プロパティは3つ、値が二つと、その中身を表示するためのメソッド一つ

で、もう一個オブジェクトを用意して、それに一つ目の内容を代入している。

その後、いったん中身を表示して、代入している元の値を一つ書き換えてみた

これで代入している先の内容を表示するとどうなるか、という問題だ。

…こう書いている時点で、x = 10, y = 20」が2行表示されるわけではないのはなんとなく察してもらえると思う。

この表示結果は、以下の通りだ。

x = 10, y = 20
x = 30, y = 20

そう、代入している元の値を書き換えたのに、代入している先の値まで変わってしまっている

なぜか、解説していこう。

参照渡し

実は、JavaScriptの変数は、本当にその値が格納されているわけではない

これまで、変数は箱を用意して、その中に値を入れている。その箱に変数名という名前をつける、というような解説をしてきた。

だが、実際は…

箱を用意し、その中に値を入れる。変数には、その箱の住所が入っている。

という動作なのだ。

この考え方を基に、上の2つのクイズを具体例として見ていこう。

一つ目の例

一つ目のソースをもう一度書いておく。

var x = 10;
var y = x;
console.log(y);
x += 10;
console.log(y);

まず、2行目までの内容を実行した時点でどうなっているか、表にしてみる。

変数名 変数の中身
x 100番地
y 100番地
住所 住所の中身
100番地 10

こんな感じだ。

で、3行目で表示すると、yの中身…100番地内にある10という数字が表示される。

で、4行目でxの値を変えた。そうすると、実際の中身は以下のようになる。

変数名 変数の中身
x 200番地
y 100番地
住所 住所の中身
100番地 10
200番地 20

このように、計算結果の20という数字を新たな箱に入れ、xでそこを指すようにする。これで、yは影響を受けることなく10と表示される

ここまでは、直感的に動いているので問題ないと思う。問題はここから。

二つ目の例

こっちも、もう一度ソースを用意しておこう。

var obj1 = {
    x: 10,
    y: 20,
    showNum: function(){
        console.log("x = " + this.x + ", y = " + this.y);
    }
};
var obj2 = obj1;
obj2.showNum();
obj1.x = 30;
obj2.showNum();

同じように、二つの変数定義の部分までの詳細をまとめてみる。

変数名 変数の中身
obj1 100番地
obj2 100番地
住所 住所の中身
100番地 110番地にx、120番地にy、130番地にshowNum
110番地 10
120番地 20
130番地 関数

こんな感じだ。この状態で表示すれば、もちろん100番地のx…110番地と100番地のy…120番地の中身が出力される。よって、x = 10, y = 20となる。

では、ここでobj1xの値を書き換えてみよう。すると、以下のようになる。

変数名 変数の中身
obj1 100番地
obj2 100番地
住所 住所の中身
100番地 140番地にx、120番地にy、130番地にshowNum
110番地 10
120番地 20
130番地 関数
140番地 30

…お分かりだろうか。100番地の中にあったxが入っている場所が変わり、その先に代入した30が入っている

今書き換えたのは、obj1xの値だ。

では、この状態でobj2xの値を見てみよう。

まず、obj2は100番地に入っている

100番地を見ると、どうやらxは140番地に入っているらしい。

で、その140番地を見ると…上で代入した30が入っている

これが、結果としてx = 30, y = 20と表示されてしまった理由だ。

このように、オブジェクト内の変数を操作すると、直感とずれたことが起きる

原因不明の誤動作はこれが原因であることがわりと多い。心にとどめておいて欲しい。

なお、これは配列でも起きる。注意しておこう。

オマケ:関数の戻り値を複数受け取る

さて、ちょっとテクニックを一つ紹介する。

例えば、関数から二つの戻り値を受け取りたいとしよう。以前解説したように、関数に指定できる戻り値は一つだけだ。

しかし、今回紹介したオブジェクト、あるいは参照渡しを利用することで、複数の値を受け取ることができる

処理としては、両方とも同じにしておこう。二つの数を受け取り、それぞれの1からその数までの合計を同時に返したいとする。…二回呼び出せよってのは突っ込まないでくれ。

で、これを二通りの方法で書いてみよう。まずはオブジェクトとして持ってくる方法。

function sumTwo(num1, num2){
    var resObj = {
        x: 0,
        y: 0
    };
    for(var i = 1; i <= num1; i++){
        resObj.x += i;
    }
    for(var i = 1; i <= num2; i++){
        resObj.y += i;
    }
    return resObj;
}

こうすることで、複数の値を一つにまとめて返すことができる。

そして、二つ目…オブジェクトを渡してそれに値を入れてしまおう。例は以下の通り。

function sumTwo(num1, num2, resObj){
    for(var i = 1; i <= num1; i++){
        resObj.x += i;
    }
    for(var i = 1; i <= num2; i++){
        resObj.y += i;
    }
}

今度は、引数にオブジェクトを渡している。

で、実際にはそのオブジェクトがどこに入っているかが渡されているので、その中身を書き換えることで、元のオブジェクトも書き換わる

これを利用し、引数に渡した中身を変更しているのだ。

状況によってどちらが使いやすいか分かれるだろう。その時々で考えて欲しい。

おわりに

今回は、まずオブジェクトという複数の変数をまとめるものを解説した。

そして、変数は実際にはこう動いてるんだよ、という仕組みも紹介した。

特に、変数の動作は知らないと誤動作の元になる。気を付けておきたい。

さて、次回だが…今度こそjQueryに入ろうかなと思っている。

更新情報はTwitterで垂れ流している。よかったらページ下部のTwitterアイコンから覗いていってほしい。

それでは。

コメント

  1. […] 前回は、オブジェクトという複数の値を持つ大きな箱と、変数は実際どんな動き方をしているかを解説した。 […]

タイトルとURLをコピーしました