JavaScript - JavaScriptの歴史とモダンな書き方について
JavaScriptの歴史について
JavaScriptを学習するにはその歴史についても少し知っておいた方が理解が進むでしょう。
というのもJavaScriptはとても複雑な歴史を歩んできておりその歴史の影響を受け、Node.jsなどのサーバーサイド言語や今のブラウザーで動くJavaScriptがあるからです。
また、Node.jsもブラウザーでのJavaScriptも今でも微妙に書き方が違うところがあり、そこがJavaScriptの歴史を知らない初学者を苦しめることがあります。
まずはJavaScriptの歴史をサクッと復習しましょう。
ライブラリが読み込みづらい問題
JavaScriptは元々ブラウザー上でテキストや画像を動かすなど、ちょっとしたプログラムを動作させるものだったので言語として不十分なところがありました。
例えば、ES2015未満のJavaScriptでは他の言語では当たり前のようにできる、他のライブラリやモジュールを読み込む機能がありませんでした。
C言語で言うとこういった文法ですね
#import <stdio.h>
これまでのJavaScriptは以下のようにHTMLであらかじめJavaScriptを読み込んでおく仕様だったので言語ファイル内で読み込む必要がなかったのかもしれません。
<script src="./jquery.js"></script>
<script src="./jquery-modal-video.js"></script>
CommonJSの登場
JavaScriptをブラウザーだけではなく他の環境(例えばサーバー)でも動かしたいというニーズが出てきました。
ただしサーバーサイドではHTMLは存在しないので、JavaScript側で他のライブラリを読み込む方法を決めておく必要があります。
そこでCommonJSはサーバーサイドやその他の環境でJavaScriptが動くように改めてJavaScriptの仕様を決めるために誕生しました。
その際にCommonJSでは以下のようにファイル内で他のライブラリやファイルを読み込む仕様になりました。
const fs = require("fs");
もしかすると読者の方の中にはこのrequire
というJavaScriptの文法を見たことがある人もいるかもしれません。
これはまさにCommonJSから来ています。
こういったrequire
を使って他のファイルを読み込む仕組みをモジュールシステムといいます。
Node.js
こうしたCommonJSの仕様をもとに2009年、サーバーサイドで動くJavaScript(Node.js)が誕生しました。
Node.jsは初めこそCommonJSの仕様にのとっていたのですが次第にCommonJSの仕様から外れていきます。
そのためNode.jsで動くJavaScriptとブラウザーで動くJavaScriptでは文法が微妙に違うところがあります。
npm
npmはNode.jsをインストールすると同時に使えるようになるパッケージマネージャーツールです。
npmは今となってはフロントエンドの開発にも必須なツールとなっていますが元々はサーバーサイドのライブラリを管理するためのツールとして使われていました。
例えば以下のような書き方で外部に公開されているライブラリをインストールできます。
$ npm install fs-extra --save
次第にこのnpmの仕組みを利用してサーバーサイドだけではなくフロントエンドでもnpmでインストールしたライブラリを利用したいという考え方が生まれてきました。
この時に生まれたツールがbrowserify
というものでした。
この時代にはまだブラウザー側のJavaScriptは他のライブラリをimportする仕組みはなかったのですが、browserify
は一度コマンドを実行することによってモジュールのimport元とimport先をうまく結合させてそれを実現しました。
browserify
はCommonJSの仕様に則っていたため、require
をモジュール同士で解決することができました。
またnpm
によってインストールされたパッケージもrequire
によって一つのファイルに合体(バンドル)させることによって解決しました。
さらにその後、JavaScript同士の依存関係を解決するだけではなく、cssや画像もrequire
できるWebpack
というツールが出てきて今ではモジュールの解決にWebpack
はよく利用されています。
ES2015
2015年にはJavaScriptの基本部分の使用を決めているEcmaScriptのES2015という新たな仕様がリリースされました。
EcmaScriptはCommonJSとは異なる仕様になりますが、この時にブラウザーでもやっとJavaScriptから他のモジュールを読み込むモジュールシステムが完成しました。
EcmaScriptでは以下のようにモジュールをインポートします。
import $ from "./path/to/jquery.js";
require
ではなく、import
で他のモジュールをインポートする点がCommonJSとは異なります。
この違いが時に初学者を混乱させます。
ES2015以降
ES2015が策定される以前は仕様がECMA-262 第3.1版 で進化がしばらく止まっていたのですが、ES2015以降は毎年JavaScriptの仕様がリリースされるようになりました。
毎年以下のように新しい仕様が追加されています。
- ECMAScript2015(ES6)
- ECMAScript2016(ES7)
- ECMAScript2017(ES8)
- ECMAScript2018(ES9)
- ECMAScript2019(ES10)
- ECMAScript2020(ES11)
ES2015以降のJavaScriptの文法について
ここでES2015以降のJavaScriptの文法について学習していきましょう。
アロー関数
まずはアロー関数です。アロー関数という名前ですが実際には普通の関数と考えていただいて差し支えありません。
const sum = (a, b) => a + b;
const value = sum(3, 4);
console.log(value); // 7
ただし、これまでにJavaScriptの関数に存在したthis
の束縛がありません。
例えば以下のコードを見てみましょう。
const obj = {
name: "daigo",
sayName: function () {
console.log(
`my name is ${this.name}`
);
},
};
この場合、obj.sayName()
を実行するとmy name is daigo
と出力されます。これはこの関数が、obj
に束縛されているからです。
一方アロー関数に書き直した場合はどうでしょうか?
const obj = {
name: "daigo",
sayName: () => {
console.log(
`my name is ${this.name}`
);
},
};
この場合、アロー関数のためthis.nameがobj
に束縛されておらずobj.sayName()
を実行するとmy name is
までの出力になります。
このようにthis
に対する縛りが変わってくるため、普通の関数とアロー関数ではそこを意識してかき分けるといいでしょう。
特にthis
に依存しない関数の場合はアロー関数を使うような感覚でいいと思います。
letとconst
またES2015から変数宣言をする際にvar
ではなくlet
やconst
を使うようになりました。
let
let
とはスコープ内で有効な変数を定義する時に使います。スコープとは関数スコープやifやforのスコープを指します。
例えば以下の例を見てみましょう。
let a = "test";
if (true) {
let a = "test2";
}
console.log(a);
この場合、console.log(a)
の出力結果はどうなると思いますか?
答えはtest
になります。
ifのスコープ内でletで宣言された変数はifの中でのみ有効のため、例え同じ変数名で上のスコープで宣言されていたとしてもその変数には影響しません。
それを保証するのがlet
になります。
const
constとはletのようにスコープ内でのみ有効な変数という点では変わりがないですがさらに書き換え不可能な変数となります。
例えば以下のような書き方だと値を書き換えてしまっているため文法エラーになります。
const name = "daigo";
name = "steelydylan";
ただし、オブジェクトのプロパティは以下のように書き換えができます。
const obj = {
name: "daigo",
sayName: () => {
console.log(
`my name is ${this.name}`
);
},
};
obj.name = "steelydylan";
Class
またES2015からクラス宣言もできるようになりました。それまでは以下のようにprototypeチェーンというものを利用してクラスを擬似的に実現していました。
function Character(name) {
this.name = name;
}
Character.prototype.sayName =
function () {
console.log(
`my name is ${this.name}`
);
};
const character = new Character(
"daigo"
);
character.sayName();
ES2015からは以下のようにClassで書くことができます。
class Character {
constructor(name) {
this.name = name;
}
sayName() {
console.log(
`my name is ${this.name}`
);
}
}
const character = new Character(
"daigo"
);
character.sayName();
機能がまとまってみやすくなりましたね。
まとめ
今回はES2015以前に登場した仕様などを紹介しました。
皆さんがネット上で情報を探す際に、JavaScriptの昔の文法やモジュールについての情報に惑わされなくなれば幸いです。
Authored by
Godai@steelydylan
Webサービスを作るのが好きなWebエンジニア。子供が産まれたことをきっかけに独立し法人化。サービス開発が大好き。
好きな言語はTypeScript。