コンテンツにスキップ

JavaScript応用編

オブジェクト

基礎編のページでは,特に断りなくオブジェクト(Object)という単語を用いていた.

JavaScriptはオブジェクト指向言語であり,変数に代入できるものはほぼ全てがオブジェクト,という特徴がある.(undefinedなどの特殊な値や,true/falseなどのプリミティブ値はオブジェクトではない)

「オブジェクト」は数値や文字列のように分かりやすいものではなく,定義が難しいが,さしあたっては

  • プロパティ(property)
  • メソッド(method)

を持つもの,と覚えておけばOK.(HTML基礎編のページにも書いたように,C言語の範囲内の知識だと「構造体を拡張したもの」に近い)

JavaScriptにおけるオブジェクト実装例

以下では,具体例で説明する.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>オプジェクト実装サンプル</title>
    <style>
      div {
        position: absolute;
        top: 50px;
      }
    </style>
    <script>
      var chara;
      function init() {
          window.addEventListener("keydown", callback_keydown);
          chara = new CHARACTER(document.getElementById("chara"), 100);
      }
      function callback_keydown(event) {
          if(event.keyCode == 37) {       // Left-arrow key
              chara.moveLeft();
          }
          else if (event.keyCode == 39) { // Right-arrow key
              chara.moveRight();
          }
      }
      function CHARACTER(element, xpos) {
          this.element = element;
          this.xpos = xpos;
          this.moveLeft = function () {
              this.xpos -= 10;
              this.element.style.left = this.xpos + "px";
          }
          this.moveRight = function () {
              this.xpos += 10;
              this.element.style.left = this.xpos + "px";
          }
          this.element.style.left = this.xpos + "px";
      }
    </script>
  </head>
  <body onload="init()">  // このHTMLファイルが読み込まれると init() が実行される
    <div id="chara">\(^^)/</div>
  </body>
</html>

このHTMLをブラウザに読み込ませると,\(^^)/というキャラクター?が表示され,左右の矢印キーを押すとウィンドウ上を動くはずである.

以下,このプログラムを少しずつ解説する.


Bodyタグのonload属性

この属性が指定されると,HTML文書の読み込みが終わったときに,指定された関数が実行される.今回の場合はinit()関数が実行される.

14
15
16
17
function init() {
    window.addEventListener("keydown", callback_keydown);
    chara = new CHARACTER(document.getElementById("chara"), 100);
}

15行目でkeydownイベントに対するハンドラを登録している.つまり,キーが押される度にcallback_keydown関数が実行されるようになる.

16行目が,実際にオブジェクトを生成している部分である.以下ではさらに細かく見る.


オブジェクトの作り方

JavaScriptでは

オブジェクト = new 関数(引数1, 引数2, ...) {
    ...
}

という形でオブジェクトを作る.関数定義と間違いやすいが,こちらにはキーワードnewがついている.オブジェクトを生成する関数のことを特に「コンストラクタ」(constructor)と呼ぶ.コンストラクタは,他の言語では分かりやすく(普通の関数とは)区別されていることが多いが,JavaScriptでは区別されていないので注意.

この16行目で,charaという名前のオブジェクトが生成され,(そのオブジェクトを操作するための)メソッドを呼び出せるようになる.今回の場合,charaにはmoveLeftmoveRightというメソッドが(定義・)実装されているので,これらを呼び出すことができる.


メソッドの呼び出し

オブジェクトのメソッドを呼び出すには

オブジェクト.メソッド();

のように,ピリオド(.)をつける.charaオブジェクトのmoveLeftメソッドを呼ぶにはchara.moveLeft()と書く.18行目からのcallback_keydown関数を見ると

18
19
20
21
22
23
24
25
function callback_keydown(event) {
    if(event.keyCode == 37) {       // Left-arrow key
        chara.moveLeft();
    }
    else if (event.keyCode == 39) { // Right-arrow key
        chara.moveRight();
    }
}

となっており,押されたキーが左矢印(キーコード37)ならchara.moveLeft()を呼び,右矢印(キーコード39)ならchara.moveRight()を呼び出している.

このプロパティ呼び出しの際,呼び出した側(オブジェクトを使う・利用する側)は実際にcharaオブジェクト内で何が起きているかを知る必要はなく,ただ結果としてcharaが右なり左なりに動いてくれれば良いだけ,である.この考え方がオブジェクト指向プログラミングの特徴.


オブジェクトの宣言(定義)

さて,ではオブジェクトを提供する側のプログラムも見ていこう.

26
27
28
29
30
31
32
33
34
35
36
37
38
function CHARACTER(element, xpos) {
    this.element = element;
    this.xpos = xpos;
    this.moveLeft = function () {
        this.xpos -= 10;
        this.element.style.left = this.xpos + "px";
    }
    this.moveRight = function () {
        this.xpos += 10;
        this.element.style.left = this.xpos + "px";
    }
    this.element.style.left = this.xpos + "px";
}

プログラム中のthisは,「オブジェクト自分自身」を表す(他言語ではselfなどというキーワードになっていることもある).16行目の

16
chara = new CHARACTER(document.getElementById("chara"), 100);

という命令でcharaオブジェクトが作成されるが,この第1引数はDOM(Document Object Model)要素,第2引数はウィンドウ上のx座標値である.

27行目で,第1引数であるelementをthis.elementに代入しており,これによってオブジェクト自身のelementプロパティに「<div id="chara">\(^^)/</div>」というDOM(Document Object Model)要素(への参照)が格納されることになる.同様に,28行目ではオブジェクトにxposというプロパティを登録し(てその初期値を第2引数で受け取ったxposにし)ている.このように,オブジェクトには自由にプロパティを追加することができる

29行目以降はメソッドの定義部分である.

29
30
31
32
this.moveLeft = function () {
    this.xpos -= 10;
    this.element.style.left = this.xpos + "px";
}

functionに名前がついていないが,JavaScriptではこのような無名(匿名)関数が利用できる.ここでは,moveLeftというプロパティに無名関数が代入されることでメソッドが実現されている.関数の中では,element(DOM)要素(this.element)のleftスタイルの値(style.left)を更新して表示場所を変更しているだけである.ここで,「style.left」は左端からの距離を表している.

class構文を用いた書き方

上記のプログラムは class構文を用いて書くこともできる.class構文を利用して書くと以下のようになる.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>オプジェクト実装サンプル(class構文)</title>
    <style>
      div {
        position: absolute;
        top: 50px;
      }
    </style>
    <script>
      function init() {
          window.addEventListener("keydown", callback_keydown);
          chara = new Character(document.getElementById("chara"), 100);
      }

      function callback_keydown(event) {
          if(event.keyCode == 37) {       // Left-arrow key
              chara.moveLeft();
          }
          else if (event.keyCode == 39) { // Right-arrow key
              chara.moveRight();
          }
      }

      class Character {
        constructor(element, xpos) {
          this.element = element;
          this.xpos = xpos;
          this.element.style.left = this.xpos + "px";
        }
        moveLeft() {
          this.xpos -= 10;
          this.element.style.left = this.xpos + "px";
        }
        moveRight() {
          this.xpos += 10;
          this.element.style.left = this.xpos + "px";
        }
      }
    </script>
  </head>
  <body onload="init()">
    <div id="chara">\(^^)/</div>
  </body>
</html>

組み込みオブジェクト

JavaScriptには,最初から用意されている便利な関数やオブジェクトがある.

タイマー

文字通りのタイマー機能.一定時間後に関数を実行したり,定期的に(一定間隔で)関数を実行したりできる.

メソッド 機能
setTimeout(関数名,ミリ秒) ミリ秒後に関数を1回だけ呼び出す
clearTimeout(timerId1) setTimeoutの処理を停止する.timerId1はsetTimeoutの戻り値
setInterval(関数名,ミリ秒) ミリ秒間隔で関数を定期的に呼び出す
clearInterval(timerId2) setIntervalの処理を停止する.timerId2はsetIntervalの戻り値
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>タイマーサンプル</title>
    <script>
      var timerId;
      function timerStart() {
        timerId = setTimeout(what_time_isitnow, 3000);  // 3秒後に1回
      }
      function timerStop() {
        clearTimeout(timerId);
      }
      function intervalStart() {
        clearInterval(timerId);
        timerId = setInterval(what_time_isitnow, 2000);  // 2秒ごとに定期的に
      }
      function intervalStop() {
        clearInterval(timerId);
      }
      function what_time_isitnow() {
        document.getElementById("timeinfo").textContent = new Date();
      }
    </script>
  </head>
  <body>
    <button onclick="timerStart()">3秒タイマー</button>
    <button onclick="timerStop()">3秒タイマー停止</button>
    <button onclick="intervalStart()">2秒間隔タイマー</button>
    <button onclick="intervalStop()">2秒間隔タイマー停止</button>
    <p id="timeinfo"></p>
  </body>
</html>

Math

各種計算を行うためのメソッドを提供している.

メソッド 機能
Math.min(a, b) aとbの小さい方を返す
Math.max(a, b) aとbの大きい方を返す
Math.random() 0以上1未満の乱数を返す
Math.floor(n) nを切り捨てた整数値を返す
Math.ceil(n) nを切り上げた整数値を返す
Math.round(n) nを四捨五入した整数値を返す
Math.sqrt(n) nの平方根を返す
Math.PI 円周率(3.1415926535...)を返す

Array

基礎編のページに記載した「配列」は,実はArrayオブジェクト.

関数

JavaScriptの関数は「第一級オブジェクト」であり,通常のオブジェクトと同じ振る舞いが可能である.

第一級オブジェクト

あるプログラミング言語において、たとえば生成、代入、演算、(引数・戻り値としての)受け渡しといったその言語における基本的な操作を制限なしに使用できる対象のこと

// 関数宣言
function func1(a, b) {
    // 省略
}

// 変数に代入して呼び出し
var f = function() {
    console.log('f is called.');
}
f();  // 'f is called.'

// パラメータ的な使い方
var f2 = function() {
    console.log('f2 is called.');
}
function func2(func) {
    func();
}
func2(f);  // 'f is called.'
func2(f2); // 'f2 is called.'

さらに,関数は「通常のオブジェクトと同様に扱える」ので,関数自身にも固有のプロパティやメソッドを動的に追加できる

var myFunc = function() {
    // 省略
}

// この時点ではプロパティiは存在しない
console.log(myFunc.i); // undefined

// プロパティiを追加
myFunc.i = 'Software Design';
console.log(myFunc.i); // 'Software Design'

また,通常のオブジェクトには無い,関数だけの特徴として,カッコ演算子で処理を呼び出せるというものがある.カッコ演算子をつけずに呼び出すと,自身の定義内容が参照される.

var f = function() {
    return 'csd';
}

console.log(f());  // 'csd'
console.log(f);    // '[Function: f]'

関数の引数

他の言語が「関数の定義通りに引数を渡さないとエラーになる」ことに対し,JavaScriptでは引数チェックが一切行われない.つまり,定義済み引数の個数より多く渡しても少なく渡しても,また,個々の引数の型が違っていたとしても動作するグループ開発する際は十分に注意しよう

function func(a, b, c) {
    console.log(a, b, c);
}

// 少なく渡す
func('a');                 // 'a undefined undefined'

// 多く渡す
func('a', 'b', 'c', 'd');  // 'a b c'

// 渡さない
func();                    // 'undefined undefined undefined'

無名関数

JavaScriptでは,関数名が必要なければ省略することができる.この名前の無い関数を無名関数という.無名関数は,イベントハンドラやコールバックのように「複数回呼び出すことがない関数」を定義する際によく利用される.

// イベントハンドラで
window.onload = function () {
    // 略
}

// コールバックで
setTimeout( function() = {
    // 略
}, 1000);

window.onload で指定した関数は,windowオブジェクトが読み込まれたタイミングで実行される.setTimeout(arg1, arg2)では,arg2で指定した時間後に,arg1で指定した処理が実行される.


コールバック

引数として渡される関数のことをコールバック(またはコールバック関数)とよぶ.引数として渡すことで,何らかの任意のタイミングで関数を実行させられる.(代表的な用途としては非同期処理の実装)

function func(callback) {
    console.log('func');
    callback();
}

// コールバック
func( function() {
    console.log('aaa');  // func aaa という順で出力
});

// コールバック
func( function() {
    console.log('bbb');  // func bbb という順で出力
});

JavaScriptを使う場合,このような「任意のタイミングで実行したい処理(関数)を,引数として渡す」ということが当たり前に考えられるようになる必要がある.(イベントハンドラは,まさにこの考え方)


即時関数

JavaScriptでは,関数を定義すると同時に実行することができる.これを即時関数,あるいは即時呼び出しという.

無名関数を定義し,そのまま即実行することで,グローバルスコープを汚すこと(不要なプロパティの追加とか)を防げる.

// 基本的な使い方
(function() {
    console.log('software');
}());  // <- この行の()によって無名関数が実行される,ということ.softwareと表示される

// 引数の渡し方
(function(a,b) {
    console.log(a + b);
}('software','design'));  // softwaredesign

即時関数は「全体をカッコで括る」必要があるので注意すること.