はじめに

JavaScriptでプログラムを書く上で、最初に理解すべき概念が「変数」と「データ型」です。変数はデータを格納する箱のようなものであり、データ型はその箱に入れられるデータの種類を表します。

本記事では、以下の内容を初心者向けにわかりやすく解説します。

  • 変数宣言キーワード(var・let・const)の違いと使い分け
  • スコープと巻き上げ(Hoisting)の挙動
  • JavaScriptのデータ型(プリミティブ型・オブジェクト型)
  • 型変換とtruthy/falsyの概念
  • 実践的なベストプラクティス

変数宣言の基本

変数とは

変数とは、プログラム内でデータを一時的に保存するための「名前付きの入れ物」です。変数にデータを格納することで、後からそのデータを参照したり、別の値に更新したりできます。

1
2
3
4
5
6
7
8
9
// 変数にデータを格納する
let message = "こんにちは";

// 変数を参照する
console.log(message); // "こんにちは"

// 変数の値を更新する
message = "さようなら";
console.log(message); // "さようなら"

JavaScriptでは、変数を宣言するためのキーワードとしてvarletconstの3つが用意されています。

var・let・constの概要

それぞれのキーワードには異なる特徴があります。まずは概要を表で確認しましょう。

キーワード 再代入 再宣言 スコープ 巻き上げ時の挙動
var 可能 可能 関数スコープ undefinedで初期化
let 可能 不可 ブロックスコープ TDZ(一時的デッドゾーン)
const 不可 不可 ブロックスコープ TDZ(一時的デッドゾーン)

ES6(ECMAScript 2015)以降は、letconstの使用が推奨されており、varは基本的に使用を避けるべきとされています。その理由を詳しく見ていきましょう。

varの特徴と問題点

関数スコープ

varで宣言された変数は「関数スコープ」を持ちます。これは、変数がその変数を含む関数全体で有効であることを意味します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function example() {
  var x = 10;
  
  if (true) {
    var y = 20; // if文の中で宣言
    console.log(x); // 10(アクセス可能)
  }
  
  console.log(y); // 20(if文の外でもアクセス可能)
}

example();

上記の例では、yifブロック内で宣言されていますが、varは関数スコープのため、関数内のどこからでもアクセスできてしまいます。これは意図しないバグの原因となります。

再宣言が可能

varでは同じ名前の変数を再宣言できてしまいます。

1
2
3
var count = 1;
var count = 2; // エラーにならない
console.log(count); // 2

大規模なコードでは、意図せず既存の変数を上書きしてしまうリスクがあります。

巻き上げ(Hoisting)

varで宣言された変数は、宣言より前のコードでも参照できます。この挙動を「巻き上げ(Hoisting)」と呼びます。

1
2
3
console.log(name); // undefined(エラーにならない)
var name = "太郎";
console.log(name); // "太郎"

JavaScriptエンジンは、上記のコードを内部的に以下のように解釈します。

1
2
3
4
var name; // 宣言が先頭に巻き上げられる
console.log(name); // undefined
name = "太郎"; // 代入はそのまま
console.log(name); // "太郎"

この挙動は直感に反するため、予期しないバグを生む原因となります。

letの特徴

ブロックスコープ

letで宣言された変数は「ブロックスコープ」を持ちます。ブロックとは、{}で囲まれた範囲のことです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function example() {
  let x = 10;
  
  if (true) {
    let y = 20;
    console.log(x); // 10(外側の変数にはアクセス可能)
    console.log(y); // 20
  }
  
  console.log(y); // ReferenceError: y is not defined
}

example();

yifブロック内でのみ有効であり、ブロックの外からはアクセスできません。これにより、変数の影響範囲を限定でき、コードの見通しが良くなります。

再宣言は不可

letでは同じスコープ内での再宣言はエラーになります。

1
2
let count = 1;
let count = 2; // SyntaxError: Identifier 'count' has already been declared

これにより、変数の意図しない上書きを防止できます。

一時的デッドゾーン(TDZ)

letで宣言された変数にも巻き上げは発生しますが、varとは異なり、宣言前に参照するとエラーになります。この宣言前の領域を「一時的デッドゾーン(Temporal Dead Zone、TDZ)」と呼びます。

1
2
console.log(name); // ReferenceError: Cannot access 'name' before initialization
let name = "太郎";

TDZにより、変数を宣言前に参照してしまうミスを防げます。

constの特徴

再代入不可の定数

constは「定数」を宣言するためのキーワードです。一度値を代入すると、再代入はできません。

1
2
const PI = 3.14159;
PI = 3.14; // TypeError: Assignment to constant variable

宣言時の初期化が必須

constは宣言と同時に初期値を設定する必要があります。

1
const value; // SyntaxError: Missing initializer in const declaration

オブジェクトや配列の場合

constで宣言したオブジェクトや配列は、再代入はできませんが、プロパティや要素の変更は可能です。

1
2
3
4
5
6
7
8
const user = { name: "太郎", age: 25 };

// プロパティの変更は可能
user.age = 26;
console.log(user); // { name: "太郎", age: 26 }

// 再代入はエラー
user = { name: "花子" }; // TypeError: Assignment to constant variable
1
2
3
4
5
6
7
8
const numbers = [1, 2, 3];

// 要素の追加・変更は可能
numbers.push(4);
console.log(numbers); // [1, 2, 3, 4]

// 再代入はエラー
numbers = [5, 6, 7]; // TypeError: Assignment to constant variable

これは、constが保護するのは「変数への参照」であり、参照先のオブジェクトの中身までは保護しないためです。

var・let・constの使い分け

flowchart TD
    A[変数を宣言する] --> B{再代入が必要か?}
    B -->|いいえ| C[constを使用]
    B -->|はい| D[letを使用]
    C --> E[完了]
    D --> E
    
    style C fill:#28a745,color:#fff
    style D fill:#007bff,color:#fff

現代のJavaScript開発では、以下のルールに従うことが推奨されています。

  1. 基本はconstを使う: 再代入が不要な変数はconstで宣言する
  2. 再代入が必要な場合はletを使う: ループカウンタや状態が変化する変数など
  3. varは使わない: レガシーコードのメンテナンス以外では避ける
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 良い例
const MAX_RETRY = 3;      // 定数
const user = { name: "太郎" }; // オブジェクト(中身は変更可能)
let count = 0;            // カウンタ(再代入が必要)

for (let i = 0; i < 10; i++) {
  count += i;
}

// 避けるべき例
var data = "something";   // varは使わない

JavaScriptのデータ型

JavaScriptは「動的型付け言語」です。変数を宣言する際に型を指定する必要がなく、実行時に値に応じて型が決まります。

データ型は大きく「プリミティブ型」と「オブジェクト型」の2種類に分けられます。

graph TB
    A[データ型] --> B[プリミティブ型]
    A --> C[オブジェクト型]
    B --> D[string]
    B --> E[number]
    B --> F[boolean]
    B --> G[undefined]
    B --> H[null]
    B --> I[symbol]
    B --> J[bigint]
    C --> K[Object]
    C --> L[Array]
    C --> M[Function]
    C --> N[Date, RegExpなど]
    
    style A fill:#6c757d,color:#fff
    style B fill:#007bff,color:#fff
    style C fill:#28a745,color:#fff

プリミティブ型

プリミティブ型は、それ以上分解できない基本的なデータ型です。値そのものがメモリに格納され、イミュータブル(不変)です。

string(文字列)

テキストデータを表します。シングルクォート、ダブルクォート、バッククォートで囲んで作成します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const single = 'シングルクォート';
const double = "ダブルクォート";
const template = `テンプレートリテラル`;

// テンプレートリテラルは式を埋め込める
const name = "太郎";
const greeting = `こんにちは、${name}さん`;
console.log(greeting); // "こんにちは、太郎さん"

// 文字列の長さ
console.log(name.length); // 2

// 文字列の結合
const fullName = "山田" + " " + "太郎";
console.log(fullName); // "山田 太郎"

number(数値)

整数と浮動小数点数の両方を表します。特殊な値としてInfinity-InfinityNaNがあります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const integer = 42;
const decimal = 3.14;
const negative = -100;
const exponential = 2.5e6; // 2,500,000

// 特殊な数値
console.log(1 / 0);        // Infinity
console.log(-1 / 0);       // -Infinity
console.log("abc" * 2);    // NaN(Not a Number)

// NaNの判定
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("abc")); // false

boolean(真偽値)

trueまたはfalseの2つの値のみを持ちます。条件分岐やループの制御に使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const isActive = true;
const isCompleted = false;

// 比較演算の結果はboolean
console.log(10 > 5);  // true
console.log(10 < 5);  // false
console.log(10 === 10); // true

// 論理演算
console.log(true && false); // false
console.log(true || false); // true
console.log(!true);         // false

undefined

変数が宣言されているが、値が代入されていない状態を表します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
let value;
console.log(value); // undefined

// 存在しないプロパティにアクセスした場合
const obj = { name: "太郎" };
console.log(obj.age); // undefined

// 関数が何も返さない場合
function doNothing() {}
console.log(doNothing()); // undefined

null

「値が存在しない」ことを明示的に表します。undefinedがシステムによって設定されるのに対し、nullはプログラマが意図的に設定します。

1
2
3
4
let user = null; // ユーザーがまだ存在しないことを明示

// 後で値を設定
user = { name: "太郎" };

symbol

ES6で追加された、一意で不変な値を生成する型です。オブジェクトのプロパティキーとして使用されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const id1 = Symbol("id");
const id2 = Symbol("id");

console.log(id1 === id2); // false(同じ説明でも異なるシンボル)

// オブジェクトのユニークなキーとして使用
const user = {
  [id1]: 12345,
  name: "太郎"
};

bigint

ES2020で追加された、任意精度の整数を表す型です。number型で扱える範囲を超える大きな整数を扱えます。

1
2
3
4
5
6
7
8
const bigNumber = 9007199254740991n; // 末尾にnを付ける
const anotherBig = BigInt("12345678901234567890");

console.log(bigNumber + 1n); // 9007199254740992n

// numberとbigintは直接演算できない
// console.log(bigNumber + 1); // TypeError
console.log(bigNumber + BigInt(1)); // 9007199254740992n

オブジェクト型

プリミティブ型以外はすべてオブジェクト型です。オブジェクト型は、複数の値やメソッドをまとめて格納できます。

Object(オブジェクト)

キーと値のペアを格納するデータ構造です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const user = {
  name: "太郎",
  age: 25,
  isStudent: false,
  greet: function() {
    console.log(`こんにちは、${this.name}です`);
  }
};

// プロパティへのアクセス
console.log(user.name);     // "太郎"(ドット記法)
console.log(user["age"]);   // 25(ブラケット記法)

// メソッドの呼び出し
user.greet(); // "こんにちは、太郎です"

// プロパティの追加
user.email = "taro@example.com";

// プロパティの削除
delete user.isStudent;

Array(配列)

順序付きのデータコレクションです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const fruits = ["りんご", "バナナ", "オレンジ"];

// インデックスでアクセス(0から始まる)
console.log(fruits[0]); // "りんご"
console.log(fruits[2]); // "オレンジ"

// 配列の長さ
console.log(fruits.length); // 3

// 要素の追加
fruits.push("ぶどう");
console.log(fruits); // ["りんご", "バナナ", "オレンジ", "ぶどう"]

// よく使うメソッド
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);     // [2, 4, 6, 8, 10]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15

Function(関数)

JavaScriptでは関数もオブジェクトの一種であり、変数に代入したり、引数として渡したりできます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// 関数宣言
function add(a, b) {
  return a + b;
}

// 関数式
const subtract = function(a, b) {
  return a - b;
};

// アロー関数
const multiply = (a, b) => a * b;

// 関数を変数に代入
const calculate = add;
console.log(calculate(2, 3)); // 5

typeof演算子による型の確認

typeof演算子を使うと、値のデータ型を文字列で取得できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
console.log(typeof "hello");      // "string"
console.log(typeof 42);           // "number"
console.log(typeof true);         // "boolean"
console.log(typeof undefined);    // "undefined"
console.log(typeof null);         // "object"(歴史的なバグ)
console.log(typeof Symbol("id")); // "symbol"
console.log(typeof 123n);         // "bigint"
console.log(typeof {});           // "object"
console.log(typeof []);           // "object"
console.log(typeof function(){}); // "function"

注意点として、typeof null"object"を返しますが、これはJavaScriptの初期実装のバグが仕様として残ったものです。nullの判定には=== nullを使用してください。

1
2
const value = null;
console.log(value === null); // true

型変換

JavaScriptでは、異なる型同士の演算時に「暗黙的な型変換」が行われることがあります。また、Number()String()などを使って「明示的な型変換」を行うこともできます。

暗黙的な型変換

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 文字列への変換
console.log("5" + 3);      // "53"(数値が文字列に変換)
console.log("Hello" + 123); // "Hello123"

// 数値への変換
console.log("5" - 3);      // 2(文字列が数値に変換)
console.log("5" * 2);      // 10
console.log("5" / 2);      // 2.5

// 真偽値への変換
console.log(Boolean(""));   // false
console.log(Boolean("abc")); // true
console.log(Boolean(0));    // false
console.log(Boolean(42));   // true

明示的な型変換

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 数値への変換
console.log(Number("42"));     // 42
console.log(Number("3.14"));   // 3.14
console.log(Number("abc"));    // NaN
console.log(Number(true));     // 1
console.log(Number(false));    // 0
console.log(parseInt("42px")); // 42
console.log(parseFloat("3.14em")); // 3.14

// 文字列への変換
console.log(String(42));       // "42"
console.log(String(true));     // "true"
console.log(String(null));     // "null"
console.log((42).toString());  // "42"

// 真偽値への変換
console.log(Boolean(1));       // true
console.log(Boolean(0));       // false
console.log(Boolean(""));      // false
console.log(Boolean("hello")); // true

truthyとfalsy

JavaScriptでは、条件式において各値が「truthy(真と評価される)」か「falsy(偽と評価される)」かが決まっています。

falsy値(全8つ)

以下の値は条件式においてfalseとして評価されます。

説明
false 真偽値のfalse
0 数値のゼロ
-0 負のゼロ
0n BigIntのゼロ
"" 空文字列
null null値
undefined 未定義
NaN Not a Number

truthy値

falsy値以外はすべてtruthy(真)と評価されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// falsy値の例
if (!false) console.log("false is falsy");
if (!0) console.log("0 is falsy");
if (!"") console.log("empty string is falsy");
if (!null) console.log("null is falsy");
if (!undefined) console.log("undefined is falsy");
if (!NaN) console.log("NaN is falsy");

// truthy値の例(注意が必要なもの)
if ("0") console.log("'0' is truthy"); // 文字列の"0"はtruthy
if ("false") console.log("'false' is truthy"); // 文字列はtruthy
if ([]) console.log("[] is truthy"); // 空配列はtruthy
if ({}) console.log("{} is truthy"); // 空オブジェクトはtruthy

実践的な活用

truthy/falsyを活用した短絡評価は、デフォルト値の設定などに便利です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// デフォルト値の設定(従来の方法)
function greet(name) {
  const displayName = name || "ゲスト";
  console.log(`こんにちは、${displayName}さん`);
}

greet("太郎"); // "こんにちは、太郎さん"
greet("");     // "こんにちは、ゲストさん"(空文字はfalsyなのでデフォルト値に)

// Null合体演算子(??)を使う方法(ES2020以降)
function greet2(name) {
  const displayName = name ?? "ゲスト";
  console.log(`こんにちは、${displayName}さん`);
}

greet2("");    // "こんにちは、さん"(空文字もそのまま使われる)
greet2(null);  // "こんにちは、ゲストさん"

??演算子はnullundefinedのみをチェックするため、0や空文字列を有効な値として扱いたい場合に便利です。

変数命名のベストプラクティス

可読性の高いコードを書くために、以下の命名規則を守りましょう。

命名規則

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// キャメルケース(変数、関数)
const userName = "太郎";
const calculateTotal = (a, b) => a + b;

// パスカルケース(クラス、コンストラクタ)
class UserAccount {}

// スネークケース + 大文字(定数)
const MAX_RETRY_COUNT = 3;
const API_BASE_URL = "https://api.example.com";

良い命名の例

1
2
3
4
5
6
7
8
9
// 悪い例
const d = new Date();    // 何を表すかわからない
const temp = users[0];   // 一時変数だが意味不明
const flag = true;       // 何のフラグか不明

// 良い例
const currentDate = new Date();
const firstUser = users[0];
const isLoggedIn = true;

まとめ

本記事では、JavaScriptの変数とデータ型について解説しました。重要なポイントを振り返ります。

変数宣言

  • const: 再代入不要な値に使用(基本はこれ)
  • let: 再代入が必要な値に使用
  • var: 現代のJavaScriptでは使用を避ける

データ型

  • プリミティブ型: string、number、boolean、undefined、null、symbol、bigint
  • オブジェクト型: Object、Array、Functionなど

重要な概念

  • ブロックスコープと関数スコープの違い
  • 巻き上げ(Hoisting)とTDZ
  • 暗黙的/明示的な型変換
  • truthy/falsyの値

変数とデータ型を正しく理解することで、バグの少ない堅牢なコードを書けるようになります。次のステップとして、演算子や制御構文について学んでいくと、より実践的なプログラムが書けるようになるでしょう。

参考リンク