JavaScript基礎 #2 変数と型
#1 はじめてとセットアップ で環境を整えたので、ここから言語そのものに入っていきます。今回の記事は — 変数をどう宣言するか、JavaScriptがどんな型を持っていて、その間を行き来するときに何に注意すべきかについてです。
変数の宣言 — let と const
#
JavaScriptで変数を作るときに使うキーワードは、let と const の2つです。
let count = 0; // 後で変えられる値
count = count + 1; // OK
const name = 'カーティス'; // 一度決めたら変えられない
name = '別の名前'; // ✗ TypeError
ルールはシンプルです — 基本は const、再代入が必要なときだけ let。最初は混乱するかもしれませんが、慣れてくるとコードの意図がより明確になります。「この値は変わらない」が一目で分かるようになるのです。
var はどこに行ったのですか?
#
昔のJavaScriptには var というキーワードがありました。今でも動作しますが、新しいコードではほとんど使いません。var には次のような落とし穴があります。
- 関数単位のスコープ — ブロック(
if、for)を無視する - ホイスティング時に
undefinedで初期化 — 宣言前にアクセスしてもエラーにならない - 同じ名前で重複宣言が可能 — 事故のリスク
let/const は、これらすべてをきれいに整理した後継です。このシリーズでは var をほとんど使いません。
8つの基本型 #
JavaScriptのすべての値は、次の8つの型のいずれかです。
| 型 | 例 | 分類 |
|---|---|---|
string | 'hello', "world", `tpl` | プリミティブ |
number | 42, 3.14, -1 | プリミティブ |
boolean | true, false | プリミティブ |
null | null (意図的に空) | プリミティブ |
undefined | undefined (まだ値がない) | プリミティブ |
bigint | 9007199254740993n | プリミティブ |
symbol | Symbol('id') | プリミティブ |
object | {}, [], 関数, クラスインスタンス | 参照 |
最初の7つは プリミティブ型(primitive)、最後の object だけが 参照型(reference) です。この分類が、JavaScriptのすべての動作を説明する出発点になります。
型の確認 — typeof
#
typeof 'hello'; // 'string'
typeof 42; // 'number'
typeof true; // 'boolean'
typeof undefined; // 'undefined'
typeof null; // 'object' ← JavaScript の有名なバグ
typeof {}; // 'object'
typeof []; // 'object' ← 配列もオブジェクト
typeof function(){}; // 'function' ← 実際は object だが特別扱い
typeof null === 'object' は、言語初期のバグがそのまま固まってしまったものです。互換性のために修正できずにいます。null を検査するときは、value === null で直接比較するほうが安全です。
null と undefined の違い
#
どちらも「値がない」を表現しますが、意図が少し異なります。
undefined— 値がまだ決まっていない。JavaScriptが自動で埋めてくれる。null— 値が意図的に空。開発者が明示的に書く。
let x; // 宣言のみで値なし → undefined
console.log(x); // undefined
const obj = { name: 'カーティス' };
console.log(obj.age); // undefined (存在しないプロパティ)
function f(arg) {
return arg;
}
f(); // undefined (引数なし)
let user = null; // まだログインしていない
// ... 後で
user = { id: 'u1', name: 'カーティス' };実務では、関数の戻り値やAPIレスポンスで両方に出会います。「あるかもしれないし、ないかもしれない値」 という意味では、両者を同じだと考えても差し支えありません。== で比較すれば両方を捕まえられます(次節)。
プリミティブ vs 参照 — JavaScriptの最も重要な違い #
ここが、入門者が一番よくつまずくところです。
プリミティブ型は、値が丸ごとコピーされます。
let a = 10;
let b = a; // a の値(10) を b にコピー
b = 20;
console.log(a); // 10 (影響なし)
console.log(b); // 20
参照型は、「どこを指しているか」がコピーされます。
const obj1 = { count: 10 };
const obj2 = obj1; // 同じオブジェクトを指すようになる
obj2.count = 20;
console.log(obj1.count); // 20 ← obj1 も一緒に変わる
console.log(obj2.count); // 20
obj1 と obj2 は 同じオブジェクトを指す2つの名前 です。片方からオブジェクトを変えると、もう片方から見ても変わって見えます。この違いが、JavaScriptで仕事をしていて出会う数多くの「なぜこれも一緒に変わるんだ?」問題の根本原因です。
オブジェクトを本当にコピーしたいとき #
const original = { name: 'カーティス', age: 30 };
const copy = { ...original };
copy.age = 31;
console.log(original.age); // 30 (影響なし)
console.log(copy.age); // 31
{...original} は、オブジェクトの1段階のプロパティだけをコピーする シャローコピー(shallow copy) です。ネストしたオブジェクトがあれば、それは依然として参照が共有されます。ディープコピーは次のシリーズで扱います。
型変換 — 落とし穴の多いところ #
JavaScriptは型を自動的に変換しようとする傾向があるため、意図とは違う結果が出やすいです。
意図的な変換 #
Number('42'); // 42
Number('hello'); // NaN (Not a Number)
String(42); // '42'
Boolean(0); // false
Boolean(1); // true
Boolean(''); // false
Boolean('false'); // true ← 'false' という文字列は空ではない
Boolean 変換のときに使われる falsy値 の7つを覚えておくと便利です。
Boolean(false); // false
Boolean(0); // false
Boolean(-0); // false
Boolean(0n); // false (bigint 0)
Boolean(''); // false
Boolean(null); // false
Boolean(undefined); // false
Boolean(NaN); // false
この7つ以外はすべて truthy です。空のオブジェクト {} と空の配列 [] も truthy だというのが、混乱しやすいところです。
Boolean({}); // true
Boolean([]); // true
Boolean('0'); // true ← 文字列 '0' は空ではない
Boolean('false'); // true
自動変換の落とし穴 #
演算子が自動的に型を変換する場面があります。
'5' + 3; // '53' ← 文字列連結として動作
'5' - 3; // 2 ← 数値演算
'5' * '2'; // 10 ← 両方とも数値に変換
[] + []; // ''
[] + {}; // '[object Object]'
+ が一番ややこしいです。片方が文字列なら 連結、それ以外は 加算 になります。混乱したら Number() や String() で明示的に変換しておくほうが安全です。
== と === — 常に === を使ってください
#
比較演算子が2種類あります。
// 緩い比較 — 自動変換後に比較
'5' == 5; // true ← 文字列を数値に変換して比較
0 == false; // true
null == undefined; // true
// 厳密な比較 — 型まで同じである必要がある
'5' === 5; // false
0 === false; // false
null === undefined;// false
ほぼ常に === を使ってください。自動変換は直感に反するケースが多く、事故のもとになります。null と undefined を一度に検査したいときだけ value == null パターンを使うくらいです(これは null || undefined と同等)。
let と const のスコープ
#
最後に、let/const が作る ブロックスコープ の意味について。
if (true) {
const message = 'こんにちは';
console.log(message); // こんにちは
}
console.log(message); // ✗ ReferenceError — ブロックの外からは見えない
{ と } の中で宣言された変数は、そのブロックの外からは見えません。forループ、ifの分岐、関数本体のすべてで同じです。これにより、変数の影響範囲が狭くなり、追跡が容易になります。
まとめ #
今回の記事で整理した内容:
- 変数宣言は基本
const、再代入が必要なときだけlet。varは新しいコードでは使わない - 8つの型 — string/number/boolean/null/undefined/bigint/symbol/object
typeof null === 'object'の落とし穴 —value === nullで直接比較- プリミティブは値コピー、オブジェクトは参照コピー。
{...obj}でシャローコピー - falsyの7つ: false, 0, -0, 0n, ‘’, null, undefined, NaN
==ではなく常に===を使うlet/constはブロックスコープ
次の記事(#3 制御フロー)では、if/while/for/switchのような分岐と繰り返し、そしてモダンな構文(for...of、for...in、switchの fallthrough の落とし穴)を扱います。