プログラミング

JavaScript超入門⑥ オブジェクトとJSONでデータを整理しよう

オブジェクト、プロパティ、メソッド、JSON.stringify、JSON.parseを学びます。

名前、点数、合格したかどうか。この3つが同じ人の情報なら、まとめて持ちたいですよね。

JavaScriptではオブジェクトを使います。さらに、そのデータを文字列として保存や通信に使う形式がJSONです。見た目は似ていますが、同じものではありません。

今回は「まとめる → 読み書きする → 文字列へ変える → 戻す」の順に進めます。

1. オブジェクトは、名前付きの項目の集まり

const student = {
  name: "Aoi",
  score: 80,
  passed: true
};

console.log(student.name);
console.log(student.score);

出力はAoi、80です。

name、score、passedのような項目をプロパティと呼びます。正確には、名前とその値の組を持っています。

配列が位置の番号で選ぶのに対し、オブジェクトはstudent.scoreのような名前で選べます。「何番目のデータだっけ?」と覚えなくても、意味を読み取りやすくなるわけです。

2. ドットと角かっこで値を選ぶ

const student = { name: "Aoi", score: 80 };

console.log(student.score);
console.log(student["score"]);

const key = "score";
console.log(student[key]);

すべて80です。

student.scoreはscoreという固定の名前、student[key]は変数keyの値をプロパティ名として使います。

student.keyと書くと、keyという名前の項目を探します。key変数の中身を使うわけではありません。取り出す項目名を動的に変えたいときは、角かっこが必要です。

存在しない項目を読むと、通常はundefinedになります。student.socreのような打ち間違いでも、すぐに説明付きのエラーが出るとは限りません。

3. プロパティを変更・追加する

const student = { name: "Aoi", score: 80 };

student.score = 90;
student.className = "A";

console.log(student.score);
console.log(student.className);

既存のscoreを90へ変更し、新しいclassNameを追加しています。

constが禁止するのはstudentへ別の値を再代入することです。今のオブジェクトのプロパティ変更はできます。中身まで固定する宣言ではありません。

同じオブジェクトを別の名前から見る場合

const original = { score: 80 };
const alias = original;

alias.score = 90;
console.log(original.score);

結果は90です。aliasへオブジェクト全体をコピーしたのではなく、同じオブジェクトを指す値を渡しているためです。

数値の変数を別の変数へ入れたときと違い、中の変更が双方から見えます。関数へオブジェクトを渡した場合も、プロパティを変更すれば元のオブジェクトへ影響します。

中身が似ていても、===で同じとはならない

console.log({ score: 80 } === { score: 80 });

結果はfalseです。別々に作ったオブジェクトだからです。===は項目を1つずつ比較する処理ではありません。

4. 関数を持つプロパティがメソッド

const player = {
  name: "Aoi",
  hp: 100,
  showStatus() {
    console.log(`${this.name}: HP ${this.hp}`);
  }
};

player.showStatus();

Aoi: HP 100と表示します。オブジェクトにある関数をメソッドと呼びます。

ここでのthisは、player.showStatus()という呼び出し方によってplayerになります。thisは必ず「定義された場所のオブジェクト」になるわけではありません。

メソッドだけを別変数へ取り出してshow()のように呼ぶと、thisが同じになるとは限らず、エラーや意図しない値の原因になります。まずはplayer.showStatus()のように相手を明示して呼びましょう。

アロー関数は外側のthisを使うため、このメソッドを単純にアロー関数へ変えると意味が変わる場合があります。MDNのthisの説明で確認できます。

5. オブジェクトを配列に入れる

const students = [
  { name: "Aoi", score: 80 },
  { name: "Ren", score: 65 }
];

for (const student of students) {
  console.log(`${student.name}: ${student.score}点`);
}

studentsが複数人の並び、各オブジェクトが1人分です。for…ofで1人ずつ取り出し、nameとscoreへアクセスしています。

今回のstudentはconstですが、student.score = 90とすればそのオブジェクトの点数を変更できます。数値の配列から取り出した数を変更する場合とは区別してください。

また、passedとscoreの両方を保存すると、scoreだけ直してpassedを直し忘れる場合があります。score >= 60から毎回求められるなら、合否は必要なときに計算する設計も選べます。

6. JSONは、データを表す文字列の形式

const student = { name: "Aoi", score: 80 };
const text = JSON.stringify(student);

console.log(text);
console.log(typeof text);

const restored = JSON.parse(text);
console.log(restored.name);
{"name":"Aoi","score":80}
string
Aoi

JSON.stringifyは値をJSON形式の文字列へ変換し、JSON.parseはJSON文字列をJavaScriptの値へ戻します。

シリアライズという言葉を見かけたら、保存や通信のために表現を変える作業、と考えると今回のstringifyへつながります。

JSONとオブジェクトの書き方は同じではない

項目 JavaScriptのオブジェクトの例 JSON
プロパティ名の引用符 省ける場合がある ダブルクォートが必要
文字列の引用符 シングルも使える ダブルクォート
関数 持てる 表現できない
コメント 書ける場所がある 書けない
末尾の余分なカンマ 使える 使えない

JSONはオブジェクトだけでなく、配列、文字列、数値、true、false、nullも表せます。JSON.parseの結果が必ずオブジェクトになるわけではありません。MDNのJSONの説明に形式の違いがまとまっています。

7. parseは「中身が安全で正しい」まで保証しない

文法が壊れたJSONをparseすると、SyntaxErrorという例外になります。

const text = '{"name":"Aoi","score":80}';

try {
  const data = JSON.parse(text);

  if (
    data !== null &&
    typeof data === "object" &&
    !Array.isArray(data) &&
    typeof data.name === "string" &&
    Number.isInteger(data.score) &&
    data.score >= 0 &&
    data.score <= 100
  ) {
    console.log(`${data.name}: ${data.score}点`);
  } else {
    console.log("データの項目を確認してください");
  }
} catch {
  console.log("JSONの書き方を確認してください");
}

try / catchは、例外が起きる可能性のある処理を囲み、起きたときの対応を分ける仕組みです。

ここでは、JSONとして読めるかに加えて、名前が文字列か、点数が0〜100の整数かを確認しています。{"score":999}もJSONとしては正しいので、業務上の正しさは別途検査します。

JSONを読むためにevalを使う必要はありません。データをプログラムとして実行せず、JSON.parseを使います。

stringifyは万能のコピーではない

関数やundefinedなどは、オブジェクトのプロパティとしてJSONへ出ない場合があります。循環した参照があるオブジェクトも、そのままstringifyできません。

このため、stringifyとparseを通せばどんなオブジェクトでも完全に複製できる、とは覚えないようにしましょう。オブジェクトの基本と、扱いたいデータの種類を確認します。

8. 練習:商品を文章で表示しよう

商品名、価格、在庫数をproductへまとめ、1つの文章で表示してください。

模範解答

const product = {
  name: "キーボード",
  price: 4800,
  stock: 6
};

console.log(
  `${product.name}は${product.price}円で、在庫は${product.stock}個です`
);

解説

1つの商品に関する情報をまとめ、ドットでそれぞれのプロパティを取り出しています。表示は「キーボードは4800円で、在庫は6個です」です。

JSONへ変えてから戻す追加例の答えは、const restored = JSON.parse(JSON.stringify(product));です。今回のような文字列と小さな整数だけなら、値を表現して戻せます。

ただし、戻したrestoredは元のproductと別のオブジェクトです。restored === productはfalse、restored.price === product.priceはtrueになります。

次の一歩

「配列は順番のある複数件、オブジェクトは1件の項目、JSONはそれを運ぶ表現」と分けると、APIなど別の分野にもつながります。

次は、こうして書いたJavaScriptでWebページの文字を実際に変えてみましょう。

前の記事 / 次の記事:DOMとクリックイベント

← 記事一覧へ戻る