JavaScriptのバージョンとは?初心者向けにES6以降の違いを解説
JavaScriptのバージョン(ES5、ES6、ES2017以降)について初心者向けに詳しく解説。ES6で追加された新機能やモダンJavaScriptの特徴、ブラウザ対応状況、実際の開発での使い分けを具体的なコード例で学びます。
JavaScriptのバージョンとは?初心者向けにES6以降の違いを解説
みなさん、JavaScriptを学習していて戸惑ったことありませんか?
「ES6って何?」 「ECMAScriptってよく聞くけど分からない」 「どのバージョンを覚えればいいの?」
こんな疑問を持っている方って多いですよね。
実は、JavaScriptは継続的に進化している言語なんです。 定期的に新しい機能が追加されています。 特にES6(ES2015)以降、大幅な機能追加が行われました。
この記事では、JavaScriptのバージョンについて基本から詳しく解説します。 ES6以降の新機能や実際の開発での使い分け、ブラウザ対応状況まで、具体的なコード例を交えて初心者向けに分かりやすく説明しますよ。
JavaScriptバージョンの基本概念
ECMAScriptとJavaScriptの関係
JavaScriptの正式な仕様はECMAScriptという標準で定義されています。
簡単に言うと、ECMAScriptは「設計図」です。 JavaScriptは「実際の製品」のような関係ですね。
// これらはすべて同じJavaScriptの機能ですconsole.log("Hello, World!"); // どのバージョンでも使える
// ES6で追加された機能const message = "ES6の新機能";let userName = "田中太郎";
// ES2017で追加された機能async function fetchData() { return "非同期処理";}
// ES2020で追加された機能const user = { name: "佐藤", age: null};const userAge = user.age ?? 20; // Nullish coalescing
このコードを見てください。 同じJavaScriptでも、書き方が時代と共に変わっているのが分かりますね。
バージョンの命名規則
バージョンの対応表を見てみましょう。
// バージョンの対応表const jsVersions = { "ES3": "1999年リリース - 基本的なJavaScript", "ES5": "2009年リリース - 現在でも広く使用", "ES6/ES2015": "2015年リリース - 大幅な機能追加", "ES2016": "2016年リリース - 小規模な追加", "ES2017": "2017年リリース - async/await追加", "ES2018": "2018年リリース - 正規表現強化", "ES2019": "2019年リリース - 配列メソッド追加", "ES2020": "2020年リリース - BigInt、Nullish coalescing", "ES2021": "2021年リリース - 論理代入演算子", "ES2022": "2022年リリース - クラス拡張", "ES2023": "2023年リリース - 配列メソッド追加"};
2015年以降は毎年新しいバージョンが出ています。 でも心配いりません!全部覚える必要はありませんよ。
年次リリースサイクル
// 開発で重要なバージョンconst importantVersions = { "ES5": "Internet Explorer対応のベース", "ES6": "モダンJavaScriptの基礎", "ES2017": "async/await - 非同期処理の革命", "ES2020": "?? - null判定の改善"};
console.log("重要なバージョン:", importantVersions);
この4つのバージョンを押さえておけば大丈夫です。
ES5以前の古いJavaScript
ES5の主要な特徴
ES5は古いブラウザでも動く基本的なJavaScriptです。
// ES5で使える基本的な書き方var userName = "田中太郎";var userAge = 30;
// 関数の定義function greetUser(name) { return "こんにちは、" + name + "さん!";}
// 配列の操作var numbers = [1, 2, 3, 4, 5];var doubled = [];
for (var i = 0; i < numbers.length; i++) { doubled.push(numbers[i] * 2);}
console.log(doubled); // [2, 4, 6, 8, 10]
これがES5の書き方です。 シンプルですが、ちょっと書くのが大変ですね。
オブジェクトの作成方法も見てみましょう。
// オブジェクトの作成var user = { name: "佐藤花子", age: 25, greet: function() { return "私は" + this.name + "です"; }};
// プロトタイプベースの継承function Person(name, age) { this.name = name; this.age = age;}
Person.prototype.introduce = function() { return "私は" + this.name + "、" + this.age + "歳です";};
var person1 = new Person("山田太郎", 28);console.log(person1.introduce());
書くのがちょっと面倒ですが、これが基本的な形です。
ES5の制限と問題点
ES5にはいくつかの問題がありました。
// ES5での問題のあるコード例
// 1. varのスコープ問題function problematicScope() { for (var i = 0; i < 3; i++) { setTimeout(function() { console.log("ES5のループ:", i); // 3, 3, 3と出力される }, 100); }}
// 2. 文字列結合の煩雑さvar name = "田中";var age = 30;var message = "私の名前は" + name + "で、年齢は" + age + "歳です。";
このコードを実行すると、期待した結果になりません。 文字列の結合も長くなって読みにくいですね。
さらに、コールバック地獄という問題もありました。
// 3. コールバック地獄function fetchUserData(userId, callback) { setTimeout(function() { var user = { id: userId, name: "ユーザー" + userId }; callback(null, user); }, 100);}
// ネストが深くなりがちfetchUserData(1, function(err, user) { if (err) { console.error(err); return; } fetchUserPosts(user.id, function(err, posts) { if (err) { console.error(err); return; } console.log(user.name + "の投稿:", posts); });});
コードがどんどん深くなって読みにくくなります。 これらの問題をES6が解決してくれました。
ES6(ES2015)の革命的な変化
変数宣言の改善
ES6では新しい変数宣言が導入されました。
// ES6の新しい変数宣言const PI = 3.14159; // 定数(再代入不可)let userName = "田中太郎"; // ブロックスコープの変数
// ブロックスコープの確認function modernScope() { for (let i = 0; i < 3; i++) { setTimeout(() => { console.log("ES6のループ:", i); // 0, 1, 2と正しく出力 }, 100); }}
これで先ほどのスコープ問題が解決されました!
const
とlet
の使い分けを見てみましょう。
// constの使用例const users = ["田中", "佐藤", "鈴木"];users.push("山田"); // 配列の中身は変更可能console.log(users); // ["田中", "佐藤", "鈴木", "山田"]
// users = []; // エラー:constは再代入不可
// 適切な使い分けfunction variableExamples() { const baseUrl = "https://api.example.com"; // 変更されない値 let currentPage = 1; // 変更される可能性がある値 for (let i = 0; i < 5; i++) { // ループ変数 currentPage++; } return { baseUrl, currentPage };}
値が変わらないものにはconst
、変わるものにはlet
を使います。
var
はもう使わなくて大丈夫ですよ。
アロー関数
関数の書き方も大幅に改善されました。
// 従来の関数定義(ES5)var add = function(a, b) { return a + b;};
// アロー関数(ES6)const addArrow = (a, b) => { return a + b;};
// 省略記法const addShort = (a, b) => a + b;
// 単一引数の場合は括弧省略可能const double = x => x * 2;
// 引数なしの場合const getCurrentTime = () => new Date();
とってもシンプルになりましたね!
実用的な例も見てみましょう。
const numbers = [1, 2, 3, 4, 5];
// 従来の書き方const doubled1 = numbers.map(function(num) { return num * 2;});
// アロー関数を使った書き方const doubled2 = numbers.map(num => num * 2);
console.log(doubled2); // [2, 4, 6, 8, 10]
アロー関数を使うと、とても簡潔に書けます。
配列メソッドとの組み合わせも便利です。
const users = [ { name: "田中", age: 30 }, { name: "佐藤", age: 25 }, { name: "鈴木", age: 35 }];
const adults = users .filter(user => user.age >= 30) .map(user => user.name);
console.log(adults); // ["田中", "鈴木"]
30歳以上のユーザーの名前だけを取得するコードです。 とても読みやすいですね。
テンプレートリテラル
文字列の結合が劇的に楽になりました。
// 従来の文字列結合(ES5)var name = "田中太郎";var age = 30;var oldMessage = "私の名前は" + name + "で、年齢は" + age + "歳です。";
// テンプレートリテラル(ES6)const modernMessage = `私の名前は${name}で、年齢は${age}歳です。`;
バッククォート()を使って文字列を囲みます。 変数は
${}`の中に入れるだけです。
複数行の文字列も簡単に書けます。
// 複数行の文字列const multiLineText = ` これは複数行の テキストです。 改行が保持されます。`;
// 式の埋め込みconst user = { name: "佐藤", age: 25 };const greeting = `こんにちは、${user.name}さん!来年は${user.age + 1}歳ですね。今日は${new Date().toLocaleDateString()}です。`;
console.log(greeting);
式や関数の呼び出しも埋め込めます。
実用的な例として、HTMLテンプレートを作ってみましょう。
function createUserCard(user) { return ` <div class="user-card"> <h3>${user.name}</h3> <p>年齢: ${user.age}歳</p> <p>メール: ${user.email}</p> </div> `;}
const userCard = createUserCard({ name: "山田花子", age: 28, email: "yamada@example.com"});
HTMLテンプレートもとても簡単に作れるようになりました。
分割代入
オブジェクトや配列から値を取り出すのが簡単になりました。
// 配列の分割代入const numbers = [1, 2, 3, 4, 5];
// 従来の書き方const first = numbers[0];const second = numbers[1];
// 分割代入const [a, b, c] = numbers;console.log(a, b, c); // 1, 2, 3
// 一部をスキップconst [x, , z] = numbers;console.log(x, z); // 1, 3
// デフォルト値const [p, q, r = 0] = [10, 20];console.log(p, q, r); // 10, 20, 0
配列から好きな場所の値を簡単に取り出せます。
オブジェクトの分割代入も便利です。
const user = { name: "田中太郎", age: 30, email: "tanaka@example.com", address: { city: "東京", zipCode: "123-4567" }};
// プロパティを個別の変数にconst { name, age, email } = user;console.log(name, age, email);
// 変数名を変更const { name: userName, age: userAge } = user;console.log(userName, userAge);
// ネストしたオブジェクトconst { address: { city, zipCode } } = user;console.log(city, zipCode);
オブジェクトのプロパティを一度に複数の変数に代入できます。
関数の引数でも使えます。
// 関数の引数での分割代入function greetUser({ name, age }) { return `こんにちは、${name}さん(${age}歳)`;}
console.log(greetUser(user));
必要なプロパティだけを関数で受け取ることができます。
スプレッド演算子
配列やオブジェクトの操作がとても楽になりました。
// 配列のスプレッドconst arr1 = [1, 2, 3];const arr2 = [4, 5, 6];
// 従来の連結方法const combined1 = arr1.concat(arr2);
// スプレッド演算子const combined2 = [...arr1, ...arr2];console.log(combined2); // [1, 2, 3, 4, 5, 6]
// 配列のコピーconst originalArray = [1, 2, 3];const copiedArray = [...originalArray];
// 配列の中に要素を挿入const numbers = [1, 2, 3];const withZero = [0, ...numbers, 4];console.log(withZero); // [0, 1, 2, 3, 4]
...
を使って配列を展開できます。
オブジェクトでも使えます。
const baseUser = { name: "田中太郎", age: 30};
const extendedUser = { ...baseUser, email: "tanaka@example.com", isActive: true};
console.log(extendedUser);// { name: "田中太郎", age: 30, email: "tanaka@example.com", isActive: true }
// プロパティの上書きconst updatedUser = { ...baseUser, age: 31, // ageを更新 city: "大阪" // 新しいプロパティを追加};
既存のオブジェクトをベースに新しいオブジェクトを作れます。
関数の引数でも便利です。
function sum(...numbers) { return numbers.reduce((total, num) => total + num, 0);}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 配列から関数に渡すconst values = [10, 20, 30];console.log(Math.max(...values)); // 30
可変長引数を簡単に扱えるようになりました。
ES2017以降の主要な新機能
async/await(ES2017)
非同期処理の書き方が劇的に改善されました。
// 従来のPromiseチェーンfunction fetchUserDataOld(userId) { return fetch(`/api/users/${userId}`) .then(response => response.json()) .then(user => { return fetch(`/api/users/${userId}/posts`); }) .then(response => response.json()) .then(posts => { return { user, posts }; }) .catch(error => { console.error("エラー:", error); });}
// async/awaitを使った書き方async function fetchUserDataNew(userId) { try { const userResponse = await fetch(`/api/users/${userId}`); const user = await userResponse.json(); const postsResponse = await fetch(`/api/users/${userId}/posts`); const posts = await postsResponse.json(); return { user, posts }; } catch (error) { console.error("エラー:", error); }}
async/awaitを使うと、まるで同期処理のように書けます。 とても読みやすくなりましたね。
実用的なAPIクライアントクラスを作ってみましょう。
class APIClient { constructor(baseURL) { this.baseURL = baseURL; } async get(endpoint) { try { const response = await fetch(`${this.baseURL}${endpoint}`); if (!response.ok) { throw new Error(`HTTP Error: ${response.status}`); } return await response.json(); } catch (error) { console.error("API取得エラー:", error); throw error; } } async post(endpoint, data) { try { const response = await fetch(`${this.baseURL}${endpoint}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return await response.json(); } catch (error) { console.error("API送信エラー:", error); throw error; } }}
このクラスを使うと、APIの呼び出しがとても簡単になります。
使用例も見てみましょう。
async function loadUserProfile(userId) { const api = new APIClient("https://api.example.com"); try { const user = await api.get(`/users/${userId}`); const posts = await api.get(`/users/${userId}/posts`); const comments = await api.get(`/users/${userId}/comments`); return { profile: user, activities: [...posts, ...comments] }; } catch (error) { console.error("プロフィール読み込みエラー:", error); return null; }}
エラーハンドリングも含めて、とても分かりやすいコードですね。
Optional Chaining(ES2020)
プロパティに安全にアクセスできるようになりました。
const user = { name: "田中太郎", profile: { age: 30, address: { city: "東京", zipCode: "123-4567" } }};
// ES5/ES6での書き方function getUserCity(user) { if (user && user.profile && user.profile.address) { return user.profile.address.city; } return null;}
// Optional Chainingを使った書き方const userCity = user?.profile?.address?.city;console.log(userCity); // "東京"
// 存在しないプロパティconst userCountry = user?.profile?.address?.country;console.log(userCountry); // undefined
?.
を使うと、途中でnullやundefinedがあってもエラーになりません。
配列でも使えます。
const users = [ { name: "田中", posts: [{ title: "投稿1" }, { title: "投稿2" }] }, { name: "佐藤" }, // postsプロパティがない null];
// 安全な配列アクセスusers.forEach((user, index) => { const firstPostTitle = user?.posts?.[0]?.title; console.log(`ユーザー${index + 1}の最初の投稿:`, firstPostTitle || "なし");});
メソッドの安全な呼び出しもできます。
const api = { users: { get: function(id) { return { id, name: `ユーザー${id}` }; } }};
// メソッドが存在する場合のみ実行const userData = api?.users?.get?.(1);const missingMethod = api?.posts?.get?.(1); // undefined
console.log(userData); // { id: 1, name: "ユーザー1" }console.log(missingMethod); // undefined
メソッドが存在しない場合でもエラーにならず、undefinedが返されます。
Nullish Coalescing(ES2020)
デフォルト値の設定がより正確になりました。
// 従来のデフォルト値設定function setDefaultValue(value) { return value || "デフォルト値";}
console.log(setDefaultValue(null)); // "デフォルト値"console.log(setDefaultValue(undefined)); // "デフォルト値"console.log(setDefaultValue("")); // "デフォルト値" (意図しない場合がある)console.log(setDefaultValue(0)); // "デフォルト値" (意図しない場合がある)console.log(setDefaultValue(false)); // "デフォルト値" (意図しない場合がある)
// Nullish Coalescing(??)を使った場合function setNullishDefault(value) { return value ?? "デフォルト値";}
console.log(setNullishDefault(null)); // "デフォルト値"console.log(setNullishDefault(undefined)); // "デフォルト値"console.log(setNullishDefault("")); // "" (空文字列は有効な値として扱われる)console.log(setNullishDefault(0)); // 0 (0は有効な値として扱われる)console.log(setNullishDefault(false)); // false (falseは有効な値として扱われる)
??
演算子は、nullとundefinedの場合のみデフォルト値を使います。
0や空文字列、falseは有効な値として扱われます。
実用的な例を見てみましょう。
function createUserConfig(options = {}) { return { theme: options.theme ?? "light", fontSize: options.fontSize ?? 16, showNotifications: options.showNotifications ?? true, maxRetries: options.maxRetries ?? 3, timeout: options.timeout ?? 5000 };}
console.log(createUserConfig({ theme: "dark", fontSize: 0, // 0は有効な値として扱われる showNotifications: false // falseは有効な値として扱われる}));// { theme: "dark", fontSize: 0, showNotifications: false, maxRetries: 3, timeout: 5000 }
0やfalseも正しく設定値として使われています。
Optional ChainingとNullish Coalescingを組み合わせることもできます。
const settings = { user: { preferences: { theme: null, language: "ja" } }};
const userTheme = settings?.user?.preferences?.theme ?? "default";const userLanguage = settings?.user?.preferences?.language ?? "en";const userTimeout = settings?.user?.preferences?.timeout ?? 30;
console.log({ userTheme, userLanguage, userTimeout });// { userTheme: "default", userLanguage: "ja", userTimeout: 30 }
とても安全で読みやすいコードが書けるようになりました。
ブラウザ対応とトランスパイル
ブラウザサポート状況
どの機能がサポートされているかチェックする方法です。
// 機能サポートチェック関数function checkBrowserSupport() { const support = { // ES6 arrowFunctions: (() => true)(), templateLiterals: typeof `template` === 'string', destructuring: (() => { try { const [a] = [1]; return true; } catch (e) { return false; } })(), // ES2017 asyncAwait: (async () => true)(), // ES2020 optionalChaining: (() => { try { return ({}?.test) === undefined; } catch (e) { return false; } })(), nullishCoalescing: (() => { try { return (null ?? true) === true; } catch (e) { return false; } })() }; return support;}
console.log("ブラウザサポート状況:", checkBrowserSupport());
この関数で現在のブラウザの対応状況が分かります。
古いブラウザでは、ポリフィルを使って機能を補完できます。
// ポリフィルの例if (!Array.prototype.at) { Array.prototype.at = function(index) { if (index >= 0) { return this[index]; } else { return this[this.length + index]; } };}
このようにして、新しい機能を古いブラウザでも使えるようにできます。
実用的な使い分け
プロジェクトの規模に応じた推奨機能を見てみましょう。
// 小規模プロジェクト(学習・プロトタイプ)const smallProjectFeatures = { variables: "const/let", functions: "アロー関数", strings: "テンプレートリテラル", objects: "分割代入、スプレッド演算子", async: "async/await"};
// 中規模プロジェクト(企業サイト・Webアプリ)const mediumProjectFeatures = { ...smallProjectFeatures, safety: "Optional Chaining, Nullish Coalescing", arrays: "Array.flat(), Array.at()", modules: "ES Modules (import/export)"};
// 大規模プロジェクト(SPA・システム開発)const largeProjectFeatures = { ...mediumProjectFeatures, classes: "Private Fields, Static Blocks", performance: "BigInt, WeakRef", advanced: "Top-level await, Dynamic imports"};
プロジェクトの規模に応じて、使う機能を選択するのがおすすめです。
実用的なモダンJavaScriptのクラス例も見てみましょう。
class UserManager { #users = new Map(); // Private field async loadUser(id) { try { const user = await this.#fetchUser(id); this.#users.set(id, user); return user; } catch (error) { console.error(`ユーザー${id}の読み込みエラー:`, error); return null; } } async #fetchUser(id) { const response = await fetch(`/api/users/${id}`); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return response.json(); } getUser(id) { return this.#users.get(id) ?? null; } getUserName(id) { return this.getUser(id)?.name ?? "不明なユーザー"; } updateUser(id, updates) { const user = this.getUser(id); if (!user) return false; const updatedUser = { ...user, ...updates }; this.#users.set(id, updatedUser); return true; }}
このコードには最新の機能がたくさん使われています。 プライベートフィールド、async/await、Optional Chaining、Nullish Coalescingなどですね。
まとめ
JavaScriptのバージョンについて詳しく解説しました。
重要なバージョン:
- ES5(2009): 基本的なJavaScript、IE対応
- ES6/ES2015(2015): モダンJavaScriptの基礎
- ES2017(2017): async/awaitの追加
- ES2020(2020): Optional Chaining、Nullish Coalescing
ES6以降の主要な新機能:
- 変数宣言: const/let でスコープ問題を解決
- アロー関数: 簡潔な関数記法
- テンプレートリテラル: 文字列結合の改善
- 分割代入: オブジェクト・配列の展開
- スプレッド演算子: 配列・オブジェクトの操作
実践的な活用方針:
- 学習・小規模: ES6の基本機能をマスター
- 業務・中規模: ES2020までの機能を活用
- 大規模システム: 最新機能も積極的に採用
- レガシー対応: Babelでトランスパイル
ブラウザ対応:
- モダンブラウザ:ES2020以降も利用可能
- レガシー対応:ES5 + Babelトランスパイル
- 機能検出:サポート状況に応じた分岐
JavaScriptのバージョンを理解することで、より効率的で読みやすいコードが書けるようになります。 まずはES6の基本機能から始めて、徐々に新しい機能も取り入れてみてください。
大丈夫です。一つずつ覚えていけば、必ずモダンなJavaScriptが書けるようになりますよ。
ぜひ今日から、これらの知識を活用してモダンなJavaScriptでの開発を始めてみませんか?