Skip to content

Instantly share code, notes, and snippets.

@hakatashi
Last active June 29, 2016 11:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hakatashi/34d185a44407398b4c88c376ce44d995 to your computer and use it in GitHub Desktop.
Save hakatashi/34d185a44407398b4c88c376ce44d995 to your computer and use it in GitHub Desktop.

TSG 第6回Web分科会 カンペ

JavaScriptを用いて、フロントエンドのWebプログラミング、およびサーバーサイドJavaScriptを体験する。

事前準備

Node.jsをインストールする。

Windows もしくは OS X

Node.js公式ページに行き、Current と書いてあるリンクをクリックするとインストーラーが落ちてくる。

あとは指示通りインストールを進めていけばよい。

Linux

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! (ブラウザ編)

まずは 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タブにある。

JavaScriptとは

プログラマーの闇と希望を抱えたプログラミング言語。ブラウザ上で動かせる唯一のプログラミング言語(昔はPNaClとかあったけど葬られた)。よく言われるがJavaとは特に関係ない。

最初からブラウザ上で動かすことを目的として作られた言語で、誕生以来10年ほどほそぼそと使われてきたが、Web技術の発展と処理能力の向上によりだんだんとブラウザ上で高度な処理を行うことが主流となってきて、今ではほとんどのWebページでJavaScriptが動いている。

一方、何を思ったかJavaScriptをブラウザから解放し、PythonやRubyと同じような汎用スクリプト言語として動かせるようにするための仕組みを整備したいという流れが存在し、例えばNode.jsなどの処理系やnpmのようなパッケージマネージャが出現した。これも近年広く使われている。これにより最近は色んな所でJavaScriptが動く。やばい。

また、JavaScript開発の特徴として、別の言語でプログラムを記述し、それをコンパイルしてJavaScriptのコードに変換し実行するという開発の流れが存在し、そのようなコンパイルしてJavaScriptに変換する専用の言語を総称してAltJSと呼ぶ。

ECMAScript

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;

ブラウザからJavaScriptが実行される仕組み

ブラウザで表示されるページは基本的にHTMLであり、JavaScriptはHTMLから「呼び出される」形で実行される。

HTMLは<html>から</html>までで一つの文書を表すが、実際に表示される際にはHTMLファイルを上から読み込んでいき、順番に見つかった要素をページに放り込んでいっていると考えて良い。JavaScriptは<script>という要素によって表現され、script要素が見つかったその時点で実行される。なのでscriptが最初に実行された段階では、その後に記述した要素は存在しないのと同じである。

script要素の使い方には二通りある。

インラインJavaScript:

<script>
	console.log('hogehoge');
</script>

外部JavaScript:

<script src="hoge.js"></script>

Web API

ブラウザ上で実行される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プロパティも……
    • 変な値を入力したらエラーを出す
  • 時刻に合わせてページの背景色を変える
    • 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);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>TSG Clock</title>
<style>
html, body {
width: 100%;
height: 100%;
margin: 0;
}
svg {
position: absolute;
max-width: 100%;
max-height: 100%;
}
.hand {
transform-origin: center;
}
.form {
position: absolute;
top: 60%;
left: 0;
right: 0;
font-size: 36px;
text-align: center;
}
.form input {
font-size: inherit;
width: 2em;
text-align: right;
}
</style>
</head>
<body>
<svg class="clock" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image class="board" xlink:href="http://hakatashi.com/nc85562.png" x="0" y="0" height="97" width="97"/>
<image id="second-hand" class="hand" xlink:href="http://hakatashi.com/second-hand.svg" x="0" y="0" height="100" width="100"/>
<image id="minute-hand" class="hand" xlink:href="http://hakatashi.com/minute-hand.svg" x="0" y="0" height="100" width="100"/>
<image id="hour-hand" class="hand" xlink:href="http://hakatashi.com/hour-hand.svg" x="0" y="0" height="100" width="100"/>
</svg>
<div class="form">
<input id="hour-input" class="input" type="number" value="0" min="0" max="23"> :
<input id="minute-input" class="input" type="number" value="0" min="0" max="59"> :
<input id="second-input" class="input" type="number" value="0" min="0" max="59">
</div>
<script src="index.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment