JavaScriptのバージョンとは?初心者向けにES6以降の違いを解説

JavaScriptのバージョン(ES5、ES6、ES2017以降)について初心者向けに詳しく解説。ES6で追加された新機能やモダンJavaScriptの特徴、ブラウザ対応状況、実際の開発での使い分けを具体的なコード例で学びます。

Learning Next 運営
37 分で読めます

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);
}
}

これで先ほどのスコープ問題が解決されました!

constletの使い分けを見てみましょう。

// 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での開発を始めてみませんか?

関連記事