プログラミング経験者であるあなたにとって、変数の概念やデータ型の存在自体は目新しいものではありません。しかし、JavaScriptには「動的型付け」や「歴史的経緯による特殊なスコープ仕様」、「独特な型変換」といった、他言語経験者が特に躓きやすいポイントがあります。
本章では、モダンなJavaScript(ES6以降)における標準的な記述方法を中心に、レガシーな仕様との違いや、バグを生みやすい落とし穴について解説します。
現代のJavaScript開発において、変数宣言のルールは非常にシンプルです。
「基本は const、再代入が必要な場合のみ let を使い、var は決して使わない」 これが鉄則です。
ES6(2015年)で導入された const と let は、C++やJava、C#などと同様にブロックスコープを持ちます。
なぜ var を使うべきではないのでしょうか。それは var が関数スコープであり、意図しない変数の共有や「巻き上げ(Hoisting)」によるバグを引き起こしやすいからです。
以下のコードで、スコープの違いを確認してみましょう。
function checkScope() {
if (true) {
var functionScoped = "I am visible outside this block";
let blockScoped = "I am NOT visible outside this block";
const constantValue = "I am also block scoped";
}
console.log("var output:", functionScoped); // 参照可能(関数スコープのため)
try {
console.log("let output:", blockScoped); // ReferenceError
} catch (e) {
console.error("let error:", e.message);
}
}
checkScope();node scope_demo.jsvar output: I am visible outside this block let error: blockScoped is not defined
JavaScriptは動的型付け言語であり、変数は特定の型に紐付きませんが、値自体は型を持っています。JavaScriptの値は大きく分けて「プリミティブ型」と「オブジェクト(参照)型」に分類されます。
プリミティブ型はイミュータブル(変更不可)であり、以下の7種類が存在します。
`)」を使うと、変数の埋め込みが容易です。true または false。Number型では表現できない巨大な整数を扱います(末尾に n をつけます)。>) の後にコマンドを入力し、Enterキーで実行します。> // String: シングルクォートまたはダブルクォートで囲む
> const language = "JavaScript";
undefined
> language
'JavaScript'
> // テンプレートリテラル(バッククォート)を使うと変数を文字列に埋め込める
> const version = 2015;
undefined
> `${language} ES${version}`
'JavaScript ES2015'
> // Number: 整数も小数も同じ型
> const price = 1980;
undefined
> const tax = 0.1;
undefined
> price * (1 + tax)
2178.0000000000002
> // Boolean
> const isActive = true;
undefined
> typeof isActive
'boolean'
> // BigInt: 末尾に n をつける
> 9007199254740991n + 1n
9007199254740992n
他言語経験者にとって混乱しやすいのがこの2つです。
>) の後にコマンドを入力し、Enterキーで実行します。> let unassigned; undefined > unassigned undefined > let empty = null; undefined > typeof unassigned 'undefined' > typeof empty // JSの有名なバグ(仕様)で 'object' が返りますが、実際はプリミティブです 'object'
プリミティブ以外のすべての値はオブジェクト(参照型)です。これらはメモリ上のアドレス(参照)として扱われます。
主なオブジェクト型には以下があります。
>) の後にコマンドを入力し、Enterキーで実行します。> // Object: キーと値のペアの集合
> const user = { name: "Alice", age: 25 };
undefined
> user.name
'Alice'
> user["age"]
25
> // Array: 順序付きリスト(インデックスは 0 から始まる)
> const colors = ["Red", "Green", "Blue"];
undefined
> colors[0]
'Red'
> colors.length
3
重要な点として、const で宣言した変数は「再代入」ができませんが、中身がオブジェクトの場合、プロパティの変更は可能です。これは const が「参照先のメモリアドレス」を固定するものであり、ヒープ領域にあるデータそのものを不変にするわけではないためです。
const user = {
name: "Alice",
id: 1
};
// 再代入はエラーになる
// user = { name: "Bob" }; // TypeError: Assignment to constant variable.
// プロパティの変更は可能
user.name = "Bob";
console.log(user);
// 配列もオブジェクトの一種
const colors = ["Red", "Green"];
colors.push("Blue");
console.log(colors);node object_mutation.js{ name: 'Bob', id: 1 }
[ 'Red', 'Green', 'Blue' ]JavaScriptにおける最大の落とし穴の一つが「等価演算子」です。
**厳密等価演算子 (===) **は「値」と「型」の両方が等しいかを比較します。
等価演算子 (==) は、比較する前に暗黙的な型変換を行います。これにより、直感的ではない結果が生じることがあります。
基本的には常に === (および !==)を使用するようにしてください。
>) の後にコマンドを入力し、Enterキーで実行します。> 1 === "1" // 型が違うので false(推奨) false > 1 == "1" // 文字列が数値に変換されて比較されるため true(非推奨) true > 0 == false // true true > null == undefined // true(ここだけは例外的に許容するスタイルもあるが、基本は避ける) true > [] == ![] // 非常に難解な挙動(trueになる) true
JavaScriptは文脈に応じて勝手に型を変換しようとします。
+ 演算子は、数値の加算だけでなく文字列の連結にも使われます。片方が文字列であれば、もう片方も文字列に変換されて連結されます。
>) の後にコマンドを入力し、Enterキーで実行します。> 10 + 20 30 > 10 + "20" // 数値が文字列 "10" に変換され連結される '1020' > "10" + 20 '1020' > 10 - "2" // 減算は数値計算しかないので、文字列 "2" が数値に変換される 8
const をデフォルトとし、再代入が必要な場合のみ let を使う。var は使用しない。const で宣言したオブジェクトの中身は変更可能である。===(厳密等価演算子)を使用し、== による暗黙の型変換を避ける。itemName という変数に商品名(文字列)、price という変数に価格(数値)、stock という変数に在庫数(数値)をそれぞれ代入してください。その後、テンプレートリテラルを使って「商品: [商品名], 価格: [価格]円, 在庫: [在庫数]個」という形式の文字列を作り、console.log() で出力するコードを書いてみましょう。
node practice2_1.js(出力例) 商品: 高性能マウス, 価格: 4500円, 在庫: 2個
const を使って、name(商品名・文字列)と price(価格・数値)を持つ商品オブジェクト product を作成してください。
次に、price を別の値に変更し、さらに stock(在庫数・数値)プロパティを新たに追加してから、console.log() でオブジェクトの内容を出力してみましょう。
node practice2_2.js(出力例) { name: 'コーヒー', price: 550, stock: 30 }