ポケモンでイメージする 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