JavaScriptを用いて、フロントエンドのWebプログラミング、およびサーバーサイドJavaScriptを体験する。
Node.jsをインストールする。
Node.js公式ページに行き、Current と書いてあるリンクをクリックするとインストーラーが落ちてくる。
あとは指示通りインストールを進めていけばよい。
nvmをおすすめする。
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.2/install.sh | bash
でnvmをインストールしたあと、
nvm install node
で最新版のNode.jsをインストールする。
curl | bash
をしたくない人は公式ページに従ってインストールして。
まずは Hello, World! を実行してみる。
適当なフォルダに index.html
というファイルを作成し、以下のように書く。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第6回Web分科会</title>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
同じフォルダに index.js
というファイルを作成し、ここにJavaScriptコードを書く。
ブラウザで動くJavaScriptには標準出力という概念がないので、Hello, World! を出力する場所にはいくつか選択肢がある。
document.write('Hello, World!');
alert('Hello, World!');
console.log('Hello, World!');
ブラウザ上で F12
(MacではCmd + Opt + I
) を押すとデベロッパーツールが開き、ここでJavaScriptデバッグできる。コンソールはその画面のConsoleタブにある。
プログラマーの闇と希望を抱えたプログラミング言語。ブラウザ上で動かせる唯一のプログラミング言語(昔はPNaClとかあったけど葬られた)。よく言われるがJavaとは特に関係ない。
最初からブラウザ上で動かすことを目的として作られた言語で、誕生以来10年ほどほそぼそと使われてきたが、Web技術の発展と処理能力の向上によりだんだんとブラウザ上で高度な処理を行うことが主流となってきて、今ではほとんどのWebページでJavaScriptが動いている。
一方、何を思ったかJavaScriptをブラウザから解放し、PythonやRubyと同じような汎用スクリプト言語として動かせるようにするための仕組みを整備したいという流れが存在し、例えばNode.jsなどの処理系やnpmのようなパッケージマネージャが出現した。これも近年広く使われている。これにより最近は色んな所でJavaScriptが動く。やばい。
また、JavaScript開発の特徴として、別の言語でプログラムを記述し、それをコンパイルしてJavaScriptのコードに変換し実行するという開発の流れが存在し、そのようなコンパイルしてJavaScriptに変換する専用の言語を総称してAltJSと呼ぶ。
JavaScriptはブラウザで動く言語ということもあって昔から多数の処理系が存在し、実装もまちまちだったが、ある時ECMAという団体が標準化に着手し、ECMAScriptというJavaScriptの標準仕様を作成した。
ECMAScriptにはいくつかバージョンが存在し、最近ではES5.1からES6(ES2015)へのバージョンアップが行われた。これはだいたいPython2とPython3くらいの違いがある[要出典]が、後方互換性は保たれている。
このような最近のJavaScriptの激動はコミュニティの性格にも大きく影響し、JSerは特に新しい物を好む傾向がある。そのせいもあってJavaScriptでは「流行りの」フレームワークやライブラリが短期間で入れ替わり、資料があっという間に古くなったりプログラマーが追いつくのが大変だったりする。特にググるときには気をつけて欲しい。
Node.jsもv6がリリースされてChromeでもだいたいES6対応が進んでいるので、本記事では新しいES6の文法で解説していく。
プログラミング言語としては、JavaScriptには以下のような特徴がある。
- 強い動的型付け言語
- 軽快かつ高機能な関数処理
- それに伴う非同期処理の容易さ
- プロトタイプベースのオブジェクト指向
上で述べたとおり最近のJavaScriptは色んな所で動くので、言語仕様とAPIは厳密に区別して考える必要がある。例えば、Hello, World! のところで使用した document.write
, alert
, console.log
といった関数はすべて Web API の関数であり、JavaScriptの仕様ではない。
基本的な記法は以下のとおり。
-
記法はC言語に大きな影響を受けており、
if
,while
,for
などはCと同じ形の構文を使用できる。if (true) { console.log('true is true'); } for (let i = 0; i < 10; i++) { console.log(i); }
-
セミコロンはRubyと同じで半分任意みたいなものだが、Rubyと違ってつけることのほうが多い。
-
変数宣言が必要。ES5では
var
(関数スコープ)、ES6ではlet
,const
(ブロックスコープ)を用いる。let hoge = 10; { let hoge = 24; } console.log(hoge);
-
Rubyと同じくだいたい全部オブジェクトなので、以下のように書ける。
[3, 1, 4].concat([1, 5]).sort().reverse().slice(0, 3);
-
関数は、ES5では
function (a, b) {...}
、ES6では(a, b) => {...}
と書ける。組み込みオブジェクトのメソッドを用いて関数型言語に近い書き方もできる。const f = (number) => { console.log(number * 100); }; f(2525); setTimeout(() => { console.log('hogehoge'); }, 3000); [3, 1, 4, 1, 5].map((n) => n - 2).filter((n) => n > 0).sort((a, b) => b - a).reduce((a, b) => a + b);
-
変数名はわりと自由につけることができる(日本語もOK)が、文化としてcamelCaseで書くのが普通である。
const theAnswerToTheUltimateQuestionOfLife = 42;
ブラウザで表示されるページは基本的にHTMLであり、JavaScriptはHTMLから「呼び出される」形で実行される。
HTMLは<html>
から</html>
までで一つの文書を表すが、実際に表示される際にはHTMLファイルを上から読み込んでいき、順番に見つかった要素をページに放り込んでいっていると考えて良い。JavaScriptは<script>
という要素によって表現され、script要素が見つかったその時点で実行される。なのでscriptが最初に実行された段階では、その後に記述した要素は存在しないのと同じである。
script要素の使い方には二通りある。
インラインJavaScript:
<script>
console.log('hogehoge');
</script>
外部JavaScript:
<script src="hoge.js"></script>
ブラウザ上で実行されるJavaScriptは、Web APIにアクセスすることができ、ブラウザの色々な機能を使うことができる。例えば、
- 要素を作ったり消したりする
- 要素のCSSを編集し、ぎゅいんぎゅいん動かしたり色を変えたりする
- 外部のウェブサイトと通信を行う
- ブラウザに保存されているCookieやWebStorageなどにアクセスする
- スマホの位置情報を取得する
特にHTML上の要素にアクセスするAPIはDOM(Document Object Model)と呼ばれ、多くのブラウザで利用できる。
JavaScriptのAPIの特徴として、JavaScriptの非同期処理の容易さを生かしたイベントと呼ばれる仕組みで動作するものが多い。
あるオブジェクトに対して何かが起きた時にある処理を実行させたい時には、プログラムはそのイベントに対してリスナーを追加する。そして実際に何かが起きた時には、API側はそのイベントを発火し、イベントに追加されているリスナーを実行する。
例えば、Web API である要素がクリックされた際にそのX座標を表示するには、以下のようにする。
element.addEventListener('click', (event) => {
console.log(event.x);
});
// 以下でも同じ
element.onclick = (event) => {
console.log(event.x);
};
このようにイベントを軸にして記述するプログラミングを、JavaScriptに限らずイベント駆動プログラミングと呼ぶ。
先ほどのHTMLを書き換えて、以下のようにする。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>第6回Web分科会</title>
</head>
<body>
<button id="button" type="button"></button>
<script src="index.js"></script>
</body>
</html>
一回この状態で表示してみる。
確認したらindex.js
にコードを書いていく。まずはボタン要素を取得する。
const button = document.getElementById('button');
そして内部のテキストを書き換えてみる。
button.innerText = 'わかめを増やす';
index.html
を読み込み直すとボタンのラベルが変わっているのが分かる。
このボタン要素のclick
イベントにリスナーを追加してみる。
button.addEventListener('click', () => {
const wakame = document.createElement('p');
wakame.innerText = 'わかめ';
document.body.appendChild(wakame);
});
HTMLを読み込みなおし、ボタンをクリックするとわかめが増える。
このページの下の方にあるsig-web-06.md.html
という名前のファイルのコピペ(or ダウンロード)して開く。時計盤と時刻欄が表示されるはず。
index.js
にコードを書いてこの時計を動かしてみよう。具体的には、
- 現在時刻を時計盤に表示する
- デジタル表示のほうが楽
- リアルタイムに現在時刻を更新する
を順番にやってみよう。
-
時計の針や時刻入力欄の要素にはすでにIDが指定されている。具体的には、
- 秒針: second-hand
- 分針: minute-hand
- 時針: hour-hand
- 秒入力欄: second-input
- 分入力欄: minute-input
- 時入力欄: hour-input
-
IDから要素を取得するには
document.getElementById
を用いる -
input要素の値は
value
プロパティから変更できる -
要素を回転させるのは少しめんどくさいが、以下のようにする。たとえば秒針を60度回転させるには、
const element = document.getElementById('second-hand'); element.style.transform = 'rotate(60deg)';
-
何かを一定時間ごとに実行するには
setInterval
を用いる
- 現在時刻ではなく、時刻を入力したら時計がその時刻を指すようにする
- input要素に値が入力されると
input
イベントが発火する - その際にアニメーションするようにする
- インターバルは60fps(=16.7ms)としてよい
- あと
transition
というCSSプロパティも……
- 変な値を入力したらエラーを出す
- input要素に値が入力されると
- 時刻に合わせてページの背景色を変える
document.body.style.background = 'red'
で赤くなる
- 一日が始まってからの秒数を表示する欄を作る
- 世界各地の時刻を表示する
あまり思いつかなかったので自由な発想でやってってください。
const secondHand = document.getElementById('second-hand');
const minuteHand = document.getElementById('minute-hand');
const hourHand = document.getElementById('hour-hand');
const secondInput = document.getElementById('second-input');
const minuteInput = document.getElementById('minute-input');
const hourInput = document.getElementById('hour-input');
setInterval(() => {
const now = new Date();
const hour = now.getHours();
const minute = now.getMinutes();
const second = now.getSeconds();
hourInput.value = hour;
minuteInput.value = minute;
secondInput.value = second;
const seconds = hour * 3600 + minute * 60 + second;
secondHand.style.transform = `rotate(${seconds * (360 / 60)}deg)`;
minuteHand.style.transform = `rotate(${seconds * (360 / (60 * 60))}deg)`;
hourHand.style.transform = `rotate(${seconds * (360 / (60 * 60 * 12))}deg)`;
}, 1000);
const secondHand = document.getElementById('second-hand');
const minuteHand = document.getElementById('minute-hand');
const hourHand = document.getElementById('hour-hand');
const secondInput = document.getElementById('second-input');
const minuteInput = document.getElementById('minute-input');
const hourInput = document.getElementById('hour-input');
setInterval(() => {
const now = new Date();
const hour = now.getHours();
const minute = now.getMinutes();
const second = now.getSeconds();
const milli = now.getMilliseconds();
hourInput.value = hour;
minuteInput.value = minute;
secondInput.value = second;
const seconds = hour * 3600 + minute * 60 + second + milli / 1000;
secondHand.style.transform = `rotate(${seconds * (360 / 60)}deg)`;
minuteHand.style.transform = `rotate(${seconds * (360 / (60 * 60))}deg)`;
hourHand.style.transform = `rotate(${seconds * (360 / (60 * 60 * 12))}deg)`;
}, 50);
const secondHand = document.getElementById('second-hand');
const minuteHand = document.getElementById('minute-hand');
const hourHand = document.getElementById('hour-hand');
const secondInput = document.getElementById('second-input');
const minuteInput = document.getElementById('minute-input');
const hourInput = document.getElementById('hour-input');
const setClock = (date) => {
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
hourInput.value = hour;
minuteInput.value = minute;
secondInput.value = second;
const seconds = hour * 3600 + minute * 60 + second;
secondHand.style.transform = `rotate(${seconds * (360 / 60)}deg)`;
minuteHand.style.transform = `rotate(${seconds * (360 / (60 * 60))}deg)`;
hourHand.style.transform = `rotate(${seconds * (360 / (60 * 60 * 12))}deg)`;
};
const updateDate = () => {
const hour = hourInput.value;
const minute = minuteInput.value;
const second = secondInput.value;
const time = new Date(1970, 0, 1, hour, minute, second);
setClock(time);
};
hourInput.addEventListener('input', updateDate);
minuteInput.addEventListener('input', updateDate);
secondInput.addEventListener('input', updateDate);