ポケモンでイメージする JavaScript のプリミティブ型とオブジェクト型
はじめに
JavaScriptに触れてしばらくして、ふと『オブジェクトのプロパティ(キー)の値は何故上書きできるのか』と思ったことありませんか? 当時の筆者は『それはそんな仕様だから』と深追いせずにいた(思考停止なだけ)のですが『仕組みをしっかり理解しないと応用もできないし、不具合検証・対応にも支障が出る』と思い改めてインプットしました。
その中でプリミティブ型やオブジェクト型、immutable、メモリ、参照などなど色々な用語や仕様が出てきて混乱したので、それらを整理してこの記事を読むとサクッと分かるように備忘録も兼ねて書いていきたいと思います。
サクッと理解するには誰でもイメージできるシンプルな例があると良いので、今回はみんな大好きなポケモンをベースに話していきます。
プリミティブ型とオブジェクト型とは?
プリミティブ型とオブジェクト型は結論から言うと上記になります。
冒頭で述べた『オブジェクトのプロパティ(キー)の値は何故上書きできるのか』の理由は、オブジェクトがメタモンだからです。
『は?』って思われて仕方がないと思います。今しがたご辛抱して話を聞いてください。
- プリミティブ型
そもそもプリミティブ型とは、数値や文字列など基本的な型のことを指します。-
Number(数値) -
String(文字列) -
Boolean(真偽) Undefined-
Symbol(シンボル) -
Null
筆者はSymbolを使ったことがありませんが、多くの方はその他のNumberやString、Booleanなどには親しみがあるかと思います。
-
- オブジェクト型
プリミティブ型以外のすべての型はオブジェクト型になります。
両者の違いはその部分だけではなく、以下の重要な違いがあります。-
プリミティブ型はimmutable(不変) -
オブジェクト型はmutable(可変)
-
プリミティブ型とオブジェクト型の実体
こちらも結論から言うと、プリミティブ型とオブジェクト型のデータに対する振る舞いは以下になります。
-
プリミティブ型:代入や編集時に新しい実体を作る -
オブジェクト型:参照の代入が行われ、実体が共有される
ここを理解する上でメモリという言葉が出てきます。
メモリは変数などのデータの一時保管場所というイメージです。
では、話を進めていきます。
プリミティブ型
代入や編集時に新しい実体を作る
メモリイメージ
| ① Hello | ---- | ---- |
| ---- | ---- | ② Hello |
| ---- | ③ World | ---- |
| ④ HelloWorld | ---- | ---- |
- ①
let str1 = 'Hello';
str1に代入した文字列Helloがメモリのどこかに格納される - ②
const str2 = str1;
str2にstr1(文字列Hello)を代入する。この際str1の文字列Helloのデータが複製されてstr2はそのメモリを指し示す。結果str1とstr2は同じ内容(の文字列Hello)だが実体は別物となる。 - ③ & ④
str1 = str1.concat('World');
文字列Worldがメモリのどこかに格納され、concatによる文字列連結処理により連結後の文字列HelloWorldが新たに作成され(てメモリのどこかに格納され)る。この処理の結果str1の指し示す対象がHelloWorldに差し変わる。
「immutableの型」として前述した以下の内容通りの挙動です。
一度作成されたオブジェクトは変更されない、変更したい場合には新しいオブジェクトを作成する
プリミティブ型の「代入や編集時に新しい実体を作る」とは下記内容になります。
- 変数の代入時は新しくメモリ領域を確保したコピーが行われて別の実体として扱われる
-
immutableの性質として編集操作では新しい実態を作成する
つまりプリミティブ型とは内容や見た目は同じでも実体は違う(複製品)みがわり人形
ということになります。
オブジェクト型
参照の代入が行われ、実体が共有される
メモリイメージ
| ① 9 | ---- | ② Hello |
| ---- | ③ [作成された object の情報] | ---- |
| ---- | ---- | ⑤ こんにちは |
| ④ 9 | ---- | ---- |
- ① & ③
const obj1 = {message: "Hello"}
オブジェクトが新たに作成されるとメモリのどこかに格納(メモリ領域が確保)されて、参照(オブジェクトを一意に示すIDのようなもの)ができる。 - ②
message: "Hello"
新たに作成されたオブジェクト(obj1)はmessageというプロパティ(キー)を持っていて、値は文字列Helloとなっている。その文字列情報はメモリのどこかに格納(メモリ領域が確保)される。 - ④
const obj2 = obj1
参照の代入を行う。
参照(今回の例では9)をコピーしたメモリ領域が新たに作られて、それがobj2の指し示す先になる。結果、obj1とobj2は実体(同じオブジェクト)を共有している。
オブジェクト型の変数では格納されている値は オブジェクトへの参照となります。オブジェクトそのものが変数に代入されているわけではなく、オブジェクトの扱いはこの参照を介して行われることに注意してください。
- ⑤
obj1.message = "こんにちは"
文字列こんにちはがメモリのどこかに格納(メモリ領域が確保)され、messageプロパティ(キー)はそれを指し示す形に変更される。
結果、obj1とobj2の参照(9)は変わっていないが、その参照が指し示すオブジェクトのmessageプロパティ(キー)は変更された ので、どちらのmessageプロパティ(キー)で確認してもこんにちはという結果になる。
オブジェクト型の「参照の代入が行われ、実体が共有される」とは下記内容になります。
- 変数の代入時には参照のコピーが行われて同じ実体を共有する
- 編集操作では新しい実体を作成せず、自身が変更される
つまりオブジェクト型とは内容や見た目が変化しても実体は同じメタモン
ということになります。
さいごに
『身代わり人形とか、メタモンとか、ほぼこじつけやんけ!』というご意見もあるかと思います。しかし筆者的にはこのイメージですごく腑に落ちたので備忘録も兼ねて記事にしました。
ちなみに、記事の内容はほぼほぼ【ステップアップJavaScript フロントエンド開発の初級から中級へ進むために】という書籍の中身に依っています。
こちらはクロージャーやスコープ、非同期処理、Node.jsなどフロントエンドに関する幅広い事柄を分かりやすく丁寧に説明して下さっている書籍なので、関心のある方は是非お手に取ってみてください。
ここまで読んでいただき、ありがとうございました。
参照情報
Discussion