Chapter 01

PHPとの違い

PHP を知っている人のための JavaScript 速習。アロー関数、テンプレートリテラル、分割代入など PHP にないモダン構文を集中的に学びます。

30 min

このチャプターで学ぶこと

  • let / const の使い分けを理解できる
  • アロー関数を書けるようになる
  • テンプレートリテラルで文字列を組み立てられる
  • 分割代入とスプレッド構文を使える
  • PHPとの truthy / falsy の違いを理解できる

PHPとの違い

このチャプターでは、PHP開発者がJavaScriptを学ぶときに「あれ、PHPと違う」と感じるポイントに絞って解説します。基本的な変数や制御構文はPHPと似ているので説明は省き、Reactでよく使うモダンな構文を中心に見ていきましょう。

let / const(var は使わない)

PHPでは変数はすべて $ で始まり、スコープはほぼ関数単位です。JavaScriptには3つの変数宣言方法がありますが、現代のJSでは letconst だけを使います

// PHP
// $name = "山田太郎";   // どこでも再代入できる
// $count = 0;

// JavaScript
const name = "山田太郎"; // 再代入不可(再代入しようとするとエラー)
let count = 0;           // 再代入可能

count = 1;    // OK
// name = "鈴木"; // TypeError: Assignment to constant variable.

const と let の使い分け

原則として const を使い、再代入が必要な場合だけ let を使います。Reactのコードでは const が圧倒的に多いです。

// const:値を変えない変数(オブジェクトや配列も const でOK)
const userName = "山田太郎";
const maxRetries = 3;
const config = { debug: true, version: "1.0" };

// オブジェクトのプロパティは変更できる(オブジェクト自体への再代入がNGなだけ)
config.debug = false; // これはOK
// config = {}; // これはエラー

// let:ループカウンターや条件によって値が変わるもの
let errorCount = 0;
let isLoading = true;

ブロックスコープ

PHPの変数はほぼ関数スコープですが、letconstブロックスコープ(波括弧 {} 単位)です。

// PHP では if ブロック内の変数が外からも見える
// if (true) { $msg = "hello"; }
// echo $msg; // "hello" → PHPではエラーにならない

// JavaScript では {} の外から let/const にアクセスできない
if (true) {
  const message = "hello";
  let count = 0;
}
// console.log(message); // ReferenceError: message is not defined
// console.log(count);   // ReferenceError: count is not defined
⚠️ var を使ってはいけない理由

var はJavaScriptの古い変数宣言方法で、ブロックスコープを無視して関数スコープになります。また「巻き上げ(hoisting)」という挙動により、宣言前に undefined として参照できてしまいます。これが予期しないバグの原因になるため、現代のJavaScriptでは var は使いません。ESLintを設定すれば var の使用を自動で禁止できます。

アロー関数

PHPには無名関数(クロージャ)がありますが、JavaScriptのアロー関数はより簡潔に書けます。Reactでは関数をあちこちに渡すので、アロー関数を読み書きできることは必須です。

基本的な書き方の比較

<?php
// PHP:通常の無名関数
$double = function($x) {
    return $x * 2;
};

// PHP 7.4+:アロー関数(短縮形)
$double = fn($x) => $x * 2;

echo $double(5); // 10
// JavaScript:従来の関数式
const double = function(x) {
  return x * 2;
};

// JavaScript:アロー関数(基本形)
const double = (x) => {
  return x * 2;
};

// JavaScript:アロー関数(暗黙のreturn)
// 処理が1行の式なら {} と return を省略できる
const double = (x) => x * 2;

console.log(double(5)); // 10

引数が1つのとき括弧を省略できる

// 引数が1つなら () を省略できる(どちらも同じ)
const double = x => x * 2;
const greet = name => `こんにちは、${name}さん!`;

// 引数が0個または2個以上のときは () が必要
const hello = () => "こんにちは!";
const add = (a, b) => a + b;

複数行の処理は で囲む

// 処理が複数行になる場合は {} と return が必要
const processUser = (user) => {
  const fullName = `${user.firstName} ${user.lastName}`;
  const greeting = `こんにちは、${fullName}さん!`;
  return greeting;
};

// オブジェクトを暗黙のreturnで返すときは () で囲む
// {} がオブジェクトリテラルではなく関数ボディと解釈されるのを防ぐため
const createUser = (name, age) => ({ name, age });
console.log(createUser("山田", 30)); // { name: "山田", age: 30 }
💡 Reactでのアロー関数の使われ方

Reactではイベントハンドラーや配列メソッドのコールバックとして頻繁にアロー関数が登場します。

// イベントハンドラー
<button onClick={() => console.log("クリックされました")}>
  クリック
</button>

// 配列の変換
const doubled = [1, 2, 3].map(n => n * 2); // [2, 4, 6]

テンプレートリテラル

PHPでは二重引用符の中に変数を埋め込めます。JavaScriptでは バッククォート(` を使ったテンプレートリテラルが同じ役割を果たします。

<?php
$name = "山田太郎";
$age = 30;

// PHP:二重引用符で変数展開
echo "こんにちは、{$name}さん。{$age}歳ですね。";

// PHP:ヒアドキュメント(複数行)
$html = <<<EOT
<div>
  <h1>{$name}</h1>
  <p>年齢:{$age}歳</p>
</div>
EOT;
const name = "山田太郎";
const age = 30;

// JavaScript:テンプレートリテラル(バッククォートを使う)
const message = `こんにちは、${name}さん。${age}歳ですね。`;
console.log(message);

// 複数行もそのまま書ける(PHPのヒアドキュメントに相当)
const html = `
<div>
  <h1>${name}</h1>
  <p>年齢:${age}歳</p>
</div>
`;

${} の中には式を書ける

const price = 1500;
const taxRate = 0.1;

// 計算式をそのまま埋め込める
console.log(`税込価格:${price * (1 + taxRate)}`);
// → "税込価格:1650円"

// 三項演算子も使える
const isLoggedIn = true;
console.log(`状態:${isLoggedIn ? "ログイン中" : "未ログイン"}`);
// → "状態:ログイン中"

// 関数呼び出しも使える
const items = ["りんご", "バナナ", "みかん"];
console.log(`${items.length}件のアイテムがあります`);
// → "3件のアイテムがあります"
📝 文字列の結合との比較

テンプレートリテラル以前は + で文字列を結合していましたが、可読性が低くなりがちです。

// 古い書き方(読みにくい)
const msg = "こんにちは、" + name + "さん。" + age + "歳ですね。";

// テンプレートリテラル(読みやすい)
const msg = `こんにちは、${name}さん。${age}歳ですね。`;

現代のJavaScriptでは原則としてテンプレートリテラルを使います。

分割代入(Destructuring)

分割代入はJavaScript独自の強力な構文で、PHPには対応するものがほとんどありません。Reactで最も頻繁に使う構文の一つなのでしっかり理解しましょう。

配列の分割代入

<?php
// PHP:list() で配列を分解
$colors = ["", "", ""];
[$first, $second, $third] = $colors;  // PHP 7.1+
// list($first, $second, $third) = $colors; // 古い書き方

echo $first;  // "赤"
// JavaScript:配列の分割代入
const colors = ["", "", ""];
const [first, second, third] = colors;

console.log(first);  // "赤"
console.log(second); // "緑"

// 一部だけ取り出す(飛ばしたい要素はカンマで空白にする)
const [red, , blue] = colors; // 2番目をスキップ
console.log(red);  // "赤"
console.log(blue); // "青"

// デフォルト値を設定できる
const [a = "デフォルトA", b = "デフォルトB"] = ["only-a"];
console.log(a); // "only-a"
console.log(b); // "デフォルトB"

Reactでは useState が配列を返すため、この構文を毎回使います:

// useState は [現在の値, 更新関数] という配列を返す
const [count, setCount] = useState(0);
const [isOpen, setIsOpen] = useState(false);
const [userName, setUserName] = useState("");

オブジェクトの分割代入

// PHPにはオブジェクトの分割代入に相当する構文がない
// JavaScriptではオブジェクトのプロパティを簡単に取り出せる

const user = {
  id: 1,
  name: "山田太郎",
  email: "yamada@example.com",
  role: "admin",
};

// プロパティを個別の変数に取り出す
const { name, email, role } = user;
console.log(name);  // "山田太郎"
console.log(email); // "yamada@example.com"

// 変数名を変えて取り出す(: で別名を付ける)
const { name: userName, role: userRole } = user;
console.log(userName); // "山田太郎"
console.log(userRole); // "admin"

// デフォルト値を設定できる
const { name: displayName, avatar = "/default-avatar.png" } = user;
console.log(avatar); // "/default-avatar.png"(userにavatarがないのでデフォルト値)

ネストされたオブジェクトの分割代入

const config = {
  server: {
    host: "localhost",
    port: 3000,
  },
  database: {
    name: "myapp",
    user: "root",
  },
};

// ネストされたオブジェクトも一度に分割代入できる
const {
  server: { host, port },
  database: { name: dbName },
} = config;

console.log(host);   // "localhost"
console.log(port);   // 3000
console.log(dbName); // "myapp"

関数の引数での分割代入

Reactのコンポーネントはpropsをオブジェクトとして受け取りますが、引数の時点で分割代入するパターンが一般的です:

// 分割代入なし(PHPの $props['name'] に似た感じ)
function greet(props) {
  return `こんにちは、${props.name}さん!`;
}

// 引数の時点で分割代入(Reactでよく見る書き方)
function greet({ name, age = 0 }) {
  return `こんにちは、${name}さん!${age}歳ですね。`;
}

greet({ name: "山田太郎", age: 30 }); // "こんにちは、山田太郎さん!30歳ですね。"
💡 分割代入はReactで必須の知識

Reactのコードでは至るところで分割代入が使われます。useState の戻り値、コンポーネントのprops、APIレスポンスのデータなど、分割代入を理解していないとReactのコードが読めません。ここでしっかりマスターしておきましょう。

スプレッド構文とレスト引数

スプレッド構文(...)は配列やオブジェクトを「展開」する構文です。PHPの ...(スプラット演算子)に似ていますが、JavaScriptではさらに多用途です。

配列のスプレッド

<?php
// PHP:配列のマージ
$a = [1, 2, 3];
$b = [4, 5, 6];
$merged = array_merge($a, $b); // [1, 2, 3, 4, 5, 6]

// PHP 7.4+:スプレッド演算子
$merged = [...$a, ...$b]; // [1, 2, 3, 4, 5, 6]
// JavaScript:配列のスプレッド
const a = [1, 2, 3];
const b = [4, 5, 6];

// 配列を展開してマージ
const merged = [...a, ...b];
console.log(merged); // [1, 2, 3, 4, 5, 6]

// 配列のコピー(元の配列を変更しないための重要なテクニック)
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3] ← 変わっていない
console.log(copy);     // [1, 2, 3, 4]

// 配列の先頭や末尾に要素を追加
const withFirst = [0, ...original];   // [0, 1, 2, 3]
const withLast = [...original, 4];    // [1, 2, 3, 4]

オブジェクトのスプレッド

// PHPにはオブジェクトのスプレッドに相当する構文がない
// JavaScriptではオブジェクトのマージやコピーに使う

const defaults = { theme: "light", language: "ja", fontSize: 14 };
const userSettings = { theme: "dark", fontSize: 16 };

// 後から書いたプロパティが優先される(上書き)
const settings = { ...defaults, ...userSettings };
console.log(settings);
// { theme: "dark", language: "ja", fontSize: 16 }
//   ↑ userSettingsの値  ↑ defaultsの値     ↑ userSettingsの値

// オブジェクトのコピー(Reactのstateを更新する典型的なパターン)
const user = { id: 1, name: "山田", age: 30 };
const updatedUser = { ...user, age: 31 }; // ageだけ更新した新しいオブジェクト
console.log(user);        // { id: 1, name: "山田", age: 30 } ← 元は変わらない
console.log(updatedUser); // { id: 1, name: "山田", age: 31 }
📝 オブジェクトのスプレッドはPHPにない

PHPの array_merge() は配列のマージに使えますが、オブジェクトのスプレッド構文はJavaScript独自の機能です。Reactではstateを更新するときに { ...prevState, newProperty: newValue } というパターンを頻繁に使うので、しっかり覚えておきましょう。

レスト引数

スプレッド構文の逆で、複数の引数を配列としてまとめて受け取ります:

// 可変長引数(残りの引数をすべて配列にまとめる)
function sum(...numbers) {
  // numbersは配列 [1, 2, 3, 4, 5]
  return numbers.reduce((total, n) => total + n, 0);
}

console.log(sum(1, 2, 3, 4, 5)); // 15

// 最初の引数だけ受け取り、残りをまとめる
function first(head, ...rest) {
  console.log("最初:", head);  // "最初: 1"
  console.log("残り:", rest);  // "残り: [2, 3, 4]"
}

first(1, 2, 3, 4);

truthy / falsy と比較演算子

PHPとJavaScriptでは「偽」と見なされる値が少し異なります。この違いを知らないとバグの原因になります。

falsy な値

// JavaScriptでfalsy(条件式でfalseとして扱われる)な値
if (false)     { /* 実行されない */ }
if (0)         { /* 実行されない */ }
if ("")        { /* 実行されない */ }
if (null)      { /* 実行されない */ }
if (undefined) { /* 実行されない */ }
if (NaN)       { /* 実行されない */ }

// 上記以外はすべてtruthy(trueとして扱われる)
if ([])        { console.log("配列はtruthy!"); } // 実行される!
if ({})        { console.log("オブジェクトもtruthy!"); } // 実行される!
if ("0")       { console.log("文字列の'0'はtruthy!"); } // 実行される!
⚠️ PHPとの重要な違い:空配列と空文字列

PHPでは [](空配列)は false として扱われますが、JavaScriptでは true として扱われます。また PHP では "0"false ですが、JavaScriptでは true です。この違いを知らないとバグの原因になります。

// JavaScript
if ([]) {
  console.log("空配列はtruthy"); // これが実行される!
}

// PHP
// if ([]) { echo "実行される?"; } // 実行されない(PHPでは空配列はfalsy)

=== vs == (厳密等価 vs 緩い等価)

PHPでも ===== がありますが、JavaScriptではさらに重要です:

// == は型変換してから比較(使わないことを推奨)
console.log(0 == "0");   // true(型変換が起きる)
console.log(0 == false); // true
console.log("" == false); // true
console.log(null == undefined); // true

// === は型も含めて厳密に比較(こちらを常に使う)
console.log(0 === "0");   // false(型が違う)
console.log(0 === false); // false
console.log(null === undefined); // false
💡 常に === を使おう

PHPでも === が推奨されますが、JavaScriptでは特に重要です。== の型変換ルールは複雑で直感に反する動作をすることが多いため、常に === を使うのが現代のベストプラクティスです。ESLintの eqeqeq ルールで自動チェックできます。

オプショナルチェーン ?.

ネストされたオブジェクトのプロパティにアクセスするとき、途中のプロパティが null または undefined の場合にエラーになることがあります:

// オブジェクトがネストしている場合
const user = {
  profile: {
    address: {
      city: "東京",
    },
  },
};

// 通常のアクセス(profileがnullだとエラーになる)
// console.log(user.profile.address.city); // エラーになる可能性がある

// オプショナルチェーン(?.):途中がnull/undefinedならundefinedを返す
console.log(user?.profile?.address?.city);    // "東京"
console.log(user?.settings?.theme);           // undefined(エラーにならない)

// メソッド呼び出しにも使える
const name = user?.getName?.();  // getNameが存在しない場合もエラーにならない

// 配列のインデックスアクセスにも使える
const firstTag = user?.tags?.[0]; // tagsが存在しない場合もundefinedになる

Nullish 合体演算子 ??

PHPの ??(null合体演算子)と同じ構文で、JavaScriptにも同じ演算子があります:

<?php
// PHP:左辺がnull/undefinedならデフォルト値を使う
$name = $user['name'] ?? "ゲスト";
// JavaScript:??(null と undefined のみに反応する)
const name = user?.name ?? "ゲスト";

// || との違い:|| は falsy 全般に反応する
const count1 = 0 || 10;   // 10(0はfalsyなので右辺が使われる)
const count2 = 0 ?? 10;   // 0(nullでもundefinedでもないので左辺が使われる)

// 実用例:設定値が 0 や空文字列も有効な値として扱いたい場合
const timeout = config.timeout ?? 3000; // config.timeoutが0でも0が使われる
const label = config.label ?? "デフォルト"; // config.labelが""でも""が使われる
💡 ?? と || の使い分け
  • ??nullundefined のときだけデフォルト値に切り替える
  • ||false0""nullundefinedNaN すべてでデフォルト値に切り替える

0"" を有効な値として扱いたい場合は ?? を使います。単純に「値がなければデフォルト」という場合は || でも良いですが、バグを防ぐために ?? を優先する習慣をつけましょう。

✍ やってみよう:PHP スタイルのコードをモダン JS に書き換える

以下のPHP/古いJavaScriptスタイルのコードを、モダンなJavaScript構文に書き換えてください。

問題1:アロー関数に変換する

// 古い書き方
const greet = function(name) {
  return "こんにちは、" + name + "";
};

const square = function(n) {
  return n * n;
};

問題2:テンプレートリテラルに変換する

// 古い書き方
const price = 1200;
const tax = 0.1;
const message = "税込価格は " + Math.floor(price * (1 + tax)) + " 円です。";

問題3:分割代入を使う

// 古い書き方
const user = { name: "山田太郎", age: 30, city: "東京" };
const name = user.name;
const age = user.age;
const city = user.city;

問題4:スプレッド構文でオブジェクトをコピーする

// 古い書き方(Object.assign)
const base = { theme: "light", lang: "ja" };
const custom = Object.assign({}, base, { theme: "dark" });

問題5:オプショナルチェーンと ?? を使う

// 古い書き方
const data = { user: null };
const city = data && data.user && data.user.address && data.user.address.city
  ? data.user.address.city
  : "不明";

解答例:

// 問題1
const greet = (name) => `こんにちは、${name}`;
const square = (n) => n * n;

// 問題2
const price = 1200;
const tax = 0.1;
const message = `税込価格は ${Math.floor(price * (1 + tax))} 円です。`;

// 問題3
const user = { name: "山田太郎", age: 30, city: "東京" };
const { name, age, city } = user;

// 問題4
const base = { theme: "light", lang: "ja" };
const custom = { ...base, theme: "dark" };

// 問題5
const data = { user: null };
const city = data?.user?.address?.city ?? "不明";

まとめ

このチャプターで学んだことをおさらいします:

機能PHPJavaScript
変数宣言$name = "..."const name = "..." / let name = "..."
アロー関数fn($x) => $x * 2(x) => x * 2
文字列展開"こんにちは {$name}"`こんにちは ${name}`
配列分解[$a, $b] = $arrconst [a, b] = arr
オブジェクト分解(なし)const { name } = obj
配列マージ[...$a, ...$b][...a, ...b]
オブジェクトマージ(なし){ ...a, ...b }
null合体$a ?? "デフォルト"a ?? "デフォルト"
nullチェック$a?->method()a?.method()

これらの構文はReactのコードで毎日のように登場します。次のチャプターでは、Reactで特に重要な配列メソッドmapfilterreduce など)を学びます。