M.Hiroi's Home Page

JavaScript Programming

お気楽 JavaScript プログラミング超入門

[ PrevPage | JavaScript | NextPage ]

クラスのないオブジェクト指向

JavaScript はオブジェクト指向スクリプト言語といわれていますが、そのオブジェクト指向は他のプログラミング言語とはちょっと違っています。Smalltalk, C++, Java などポピュラーなオブジェクト指向言語では、まず最初にクラスを定義し、それを元にしてインスタンス (オブジェクト) を生成します。このようにクラスを中心としたオブジェクト指向を「クラスベース」といいます。これはスクリプト言語の Perl, Python, Ruby や Common Lisp Object System (CLOS) でも同じです。

ところが、オブジェクト指向にはいろいろな考え方があり、クラスが存在しないオブジェクト指向言語もあります。これを「プロトタイプベース」もしくは「インスタンスベース」といいます。代表的な言語として Self や JavaScript があります。

プロトタイプベースの言語では、元になるインスタンスから新しいインスタンスを生成します。この元になるインスタンスを「プロトタイプ」といいます。そして、新しいインスタンスに必要となる機能 (インスタンス変数やメソッドなど) を追加します。このインスタンスが新たなプロトタイプとなり、あとは必要な分だけプロトタイプをコピーしてインスタンスを作ればいいわけです。また、元となったプロトタイプのインスタンスを覚えておいて、それをたどっていくことで継承と同じ動作を行わせることもできます。

プロトタイプベースのオブジェクト指向機能はとてもシンプルです。たとえば JavaScript のオブジェクトは単なるハッシュ (Perl, Ruby のハッシュや Python のディクショナリ) にすぎず、オブジェクトに関数を格納すればそれがメソッドになります。JavaScript は関数をオブジェクトとして扱うことができるので、関数型言語のように関数を変数に格納したり、引数として関数を渡すこともできます。また、値として関数を返すこともできるので、関数を作る関数を定義することも簡単にできます。

このように、JavaScript は関数をオブジェクトとして扱うことができるため、シンプルなプロトタイプベースのオブジェクト指向でも、柔軟で高度なプログラミングが可能になっています。文法はC言語とよく似ているので、プログラミングの経験のある方であれば、JavaScript をマスターするのは難しくないと思います。JavaScript は M.Hiroi が思っていたよりもクールなプログラミング言語のようです。

そこで、JavaScript で簡単なプログラムを作りながら、プロトタイプベースのオブジェクト指向について勉強することにしましょう。たいしたことはできませんが、よろしければお付き合いくださいませ。


JavaScript の概要

JavaScript の主な動作環境は Web ブラウザで、Web ページのユーザビリティ向上や Web アプリケーションの構築などで使われています。また、Web ブラウザだけではなく、アプリケーションの組み込み言語として用いられることもあります。以前は名称から Java のサブセットと誤解されたり、初心者向けの言語といわれることもあったのですが、Ajax という JavaScript を使った技術が注目を集めるようになると、JavaScript もプログラミング言語として正当に評価されるようになってきました。

M.Hiroi も JavaScript のことを誤解していて、Web ブラウザで使用するマクロ言語としか認識していませんでした。ところが、JavaScript はクロージャのような関数型言語の機能とプロトタイプベースのオブジェクト指向機能を備えていて、これらを組み合わせることで柔軟で高度なプログラミングが可能になっています。JavaScript はたんなるマクロ言語ではなく、まっとうなプログラミング言語だったのです。

クロージャは関数型言語だけではなく最近のスクリプト言語、たとえば Perl, Python, Ruby などにもあるので、ご存知の方も多いのではないかと思います。M.Hiroi's Home Page でも、関数型言語やスクリプト言語の入門講座でクロージャについて説明しています。JavaScript のクロージャは本格的なもので、Python や Ruby のそれよりも Lisp / Scheme に近いものです。

これに対し、プロトタイプのオブジェクト指向は初めての方がほとんどではないでしょうか。現在はクラスベースのオブジェクト指向が主流です。プロトタイプベースのオブジェクト指向言語では JavaScript がもっとも普及しているのですが、今まで正当に評価されていなかったこともあって、プロトタイプベースは一般にはあまり知られていないオブジェクト指向だと思います。

●ECMAScript

JavaScript の言語仕様は Ecma International という団体によって標準化されています。これを ECMAScript といいます。ECMAScript は JavaScript のコアな部分のみを標準化したものです。たとえば、Web ブラウザに搭載されている JavaScript は、ECMAScript に Web ブラウザ特有の機能を追加しています。ECAMScript の最新版は 2015 年 6 月に公開される ECMAScript2015 (旧 ECMAScript6) ですが、Web ブラウザが新しい仕様に対応するのはまだまだ先の話でしょうから、本稿では 3rd edition の範囲内でプログラムを記述することにします。

●JavaScript コンソール

JavaScript の基本を学ぶ場合、Lisp / Scheme の REPL (read eval print loop) のような対話モードがあると便利です。Python にも対話モードがありますし、Ruby には irb (interactive ruby) というツールがあります。最近の Web ブラウザには JavaScript 用のコンソールが用意されていて、コンソールからプログラムを入力して実行することが可能です。

本ページでは Google Chrome の JavaScript コンソールを使って JavaScript の学習を進めていくことにします。コンソールはメニューや設定から開くことができますが、ショットカットキー (Ctrl-Shift-J) でも開くことができます。

簡単な例として、hello, world をコンソールに表示するプログラムを作ってみましょう。次のリストを見てください。

リスト : hello, world の表示 (index.html)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>お気楽 JavaScript プログラミング超入門</title>
</head>
<body>
  <script>
    console.log("hello, world");
  </script>
</body>
</html>

HTML ファイルに JavaScript プログラムを書く場合、script タグの中で定義します。script タグは body タグの最後、</body> の直前に記述するのが一般的です。この HTML ファイルを読み込んでブラウザのコンソールを開いてください。hello, world と表示されているはずです。また、リロードしても hello, world と表示されます。

●たらいまわし関数

ところで、Google Chrome に搭載されている JavaScript エンジン (V8) はとても速いといわれています。そこで、実際に拙作のページ Algorithms with Python 再帰定義 の「たらいまわし関数」で実行速度を比較してみました。

リスト : たらいまわし関数 (tak.js)

function tak(x, y, z) {
    if(x <= y) return z;
    return tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y));
}

function test(x, y, z) {
    var s = new Date().getTime();
    console.log(tak(x, y, z));
    var e = new Date().getTime();
    console.log(e - s);
}

HTML ファイルで JavaScritp プログラムが書かれたファイルを読み込む場合は、script タグで次のように指定します。

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

あとは、コンソールで関数 test を実行すると、時間が msec 単位で表示されます。

それでは実行結果を示します。tak(22, 11, 0) を計算しました。なお、下表では JavaScript を JS と表記します。

表 : tak(22, 11, 0) の結果
処理系
Python (ver 2.7.3)91.9
JS (nashorn 1.8.0_05)48.3
PyPy (ver 2.2.1)24.7
SBCL (ver 1.0.55)5.85
SML/NJ (ver 110.74)3.48
JS (Google Chrome)2.96
GCC -O (ver 4.5.3)2.37
Julia (ver 0.3.1)2.30
SBCL (最適化)2.01
Go (ver 1.2)1.98
GHC -O (ver 7.4.1)1.92
GCC -O2 (ver 4.5.3)1.89
Scala (ver 2.11.1)1.79
Java (ver 1.8.0_05)1.09
ocamlopt (ver 3.12.1)1.09

Google Chrome の JS は Python や Java 8 に付属している JS (nashorn) とは次元の異なる速さで、ネイティブコードにコンパイルするプログラミング言語に匹敵する結果になりました。こんなに速いとは M.Hiroi も予想していなかったので大変驚きました。興味のある方はいろいろ試してみてください。

●FizzBuzz 問題

それでは簡単な例題として FizzBuzz 問題を JavaScript で解いてみましょう。FizzBuzz 問題は 1 から 100 までの値を表示するとき、3 の倍数のときは Fizz を、5 の倍数ときは Buzz を表示するというものです。FizzBuzz 問題の詳細については Fizz Buzz - Wikipedia をお読みください。

プログラムは次のようになります。

リスト : FizzBuzz 問題 (fizzbuzz.js)

function fizzbuzz() {
    for (var i = 1; i <= 100; i++) {
    if (i % 15 == 0) {
        console.log("FizzBuzz");
    } else if (i % 3 == 0) {
        console.log("Fizz");
    } else if (i % 5 == 0) {
        console.log("Buzz");
    } else {
        console.log(i);
    }
  }
}

JavaScript の場合、関数を定義するときは function を使います。そのあとに関数名と引数を指定します。基本的な制御構造はC言語とほぼ同じです。ローカル変数の宣言には var を使います。C言語と違って JavaScript の変数に型はありません。どんなデータ型でも格納することができます。このように、JavaScript のプログラムはC言語によく似ていることがわかると思います。

実行結果は次のようになります。

> fizzbuzz()
  1
  2
  Fizz
  4
  Buzz
  Fizz
  7
  8
  Fizz
  Buzz
  11
  Fizz
  13
  14
  FizzBuzz
  16
・・・ 省略 ・・・

JavaScript の基礎知識 (データ構造編)

JavaScript はC言語や Perl と同じく、手続き型のプログラミング言語です。JavaScript も Perl と同様に、プログラムの実行を制御する "文"(命令文ともいう)、データを格納する "変数"、決められた処理を行う "関数"、という構造に分かれます。C言語と同様に、文の終わりにはセミコロン ( ; ) を付けます。コメントは // から行末まで、もしくは /* から */ の間に記述します。

変数と関数は名前をつけて区別します。名前には英数字とアンダースコア _ が使えます。英大文字と英小文字は区別されるので、FOO と Foo と foo は異なる名前と判断されます。関数は JavaScript にあらかじめ用意されている「組み込み関数」のほかに、私達ユーザーが定義することもできます。もちろん「再帰定義」も可能です。

●数

それでは、JavaScript に用意されている基本的なデータ型について簡単に説明しましょう。JavaScript の数値は浮動小数点数 (floating-point number) で表されます。これはC言語の倍精度浮動小数点数 (double) と同じで、範囲は絶対値で約 1e-307 から 1e+308 までです。簡単な例を示します。

> b = 1.234
1.234
> a
100
> b
1.234
> c = 10 + 20
30
> d = 10 * 20
200
> e = 10 - 20
-10
> f = 10 / 20
0.5
> c
30
> d
200
> e
-10
> f
0.5

JavaScript はC言語と違い、変数のデータ型を宣言する必要はありません。変数に値をセットすることを「代入」といいます。代入には = を使います。これはC言語や Perl と同じです。コンソールで変数名を入力するとその値が表示されます。主な算術演算子を下表に示します。

表 : 算術演算子
操作結果
-x x を負にする
x + y x と y の和
x - y x と y の差
x * y x と y の積
x / y x 割る y の商
x % y x 割る y の剰余

●文字列

文字列 (string) はシングルクオート ' で囲むか、ダブルクオート " で囲んで表します。

> a = "hello, world"
"hello, world"
> a
"hello, world"

変数 a に文字列 "hello, world" を代入しています。文字列には演算子 + を適用することができます。

> "abc" + "def"
"abcdef"

演算子 + は文字列を連結した新しい文字列を作ります。

文字列には「エスケープシーケンス」を含めることができます。これは、画面に表示することができない文字を表すのに用いられる方法です。よく使われる記号に改行を表す \n とタブを表す \t があります。

> "abc\ndef"
"abc
def"
> "abc\tdef"
"abc     def"

この例はタブが 8 文字に設定されている場合です。

このほかに、JavaScript には文字列を操作する便利な関数 (メソッド) が用意されています。また、正規表現も利用することができます。

●数と文字列の変換

JavaScript は Perl と同様に、数が必要な演算に文字列が与えられると、文字列を数値に変換して処理を行います。数に変換できない場合は非数を表すデータ NaN (Not-a-Number) を返します。逆に、文字列が必要な演算で数が与えられると、数を文字列に変換して処理を行います。簡単な例を示しましょう。

> 1 - "2"
-1
> 1 + "2"
"12"
> 1 + "x"
"1x"
> 1 - "x"
NaN

最初の例は文字列 "2" を数値に変換して 1 - 2 を計算します。2 番目の例は数値 1 を文字列 "1" に変換して、"1" と "2" を連結します。最後の例は "x" を数値に変換できないので NaN を返します。

●配列

「配列 (array) 」は複数のデータを格納するデータ構造です。配列に格納されたデータを「要素」といいます。特に、要素を一列に並べたものを「1 次元配列」もしくは「ベクタ (vector) 」と呼びます。配列の要素は 0 以上の整数で指定します。これを「添字 (subscripts) 」といいます。添字はC言語と同じく 0 から始まります。配列は角カッコ '[' と ']' で囲み、要素をカンマ ( , ) で区切って表します。[ ] は要素が一つもない空の配列になります。簡単な例を示します。

> ary1 = [10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]
> ary1[0]
10
> ary1[4]
50
> ary1[4] = 100
100
> ary1[4]
100
> ary1
[10, 20, 30, 40, 100]

C言語は配列の大きさを宣言する必要がありますが、JavaScript の配列は大きさを宣言する必要はありません。配列の大きさは JavaScript が自動的に調整してくれます。大きさを自由に変えることができる配列を「可変長配列」といいます。他のスクリプト言語、たとえば Perl, Python, Ruby でも可変長配列をサポートしています。

配列の要素には、いろいろなデータ型が混在していてもかまいません。また、要素に式を書くこともできます。

> ary2 = ["a", 0, "b", 1, "c", 2]
["a", 0, "b", 1, "c", 2]
> ary2[0]
"a"
> ary2[5]
2
> ary3 = [1 + 2, 3 * 4 - 5]
[3, 7]
> ary3[0]
3
> ary3[1]
7

ary2 の配列は、0, 2, 4 番目の要素が文字列で、1, 3, 5 番目の要素が整数になっています。要素に式を書くと、その式の評価結果が要素になります。

配列は入れ子にすることができます。つまり、配列の要素に配列を入れてもかまいません。これで多次元配列を表すことができます。

> ary4 = [[1,2,3],[4,5,6],[7,8,9]]
[▲Array[3], ▲Array[3], ▲Array[3]]
> ary4[0][0]
1
> ary4[1][0]
4
> ary4[2][2]
9

ary4 のように、配列の中に配列を入れることで 2 次元配列を表すことができます。ary4 の 0 番目の要素は配列 [1, 2, 3] で、その配列の 0 番目の要素は 1 です。この要素は角カッコを 2 つ使って ary4[0][0] とアクセスすることができます。最初の ary4[0] で 0 番目の配列を取り出し、その配列の 0 番目の要素を次の [0] で取り出します。

JavaScript の場合、配列は Array というオブジェクトとして定義されているので、オブジェクトを生成する演算子 new を使っても配列を生成することができます。JavaScript のオブジェクト指向については後で詳しく説明しますが、配列は new を使って次のように生成することができます。

  1. new Array(); // 空の配列を生成
  2. new Array(n); // 大きさ n の配列を生成
  3. new Array(item1, item2, ...); // 要素の初期値を指定する場合

2 の場合、配列の要素は undefined という値に初期化されます。undefined は未定義を表す JavaScript の特別な値です。このほかに、JavaScript には何もないことを表す null という特別な値もあります。

●連想配列 (ハッシュ)

配列が数値を使って要素を指定するのに対し、連想配列 (ハッシュ) はキーというデータを使って要素を指定します。一般に、ハッシュのキーには文字列が用いられますが、JavaScript は配列もキーに指定することができます。ハッシュは中カッコ '{' と '}' で囲み、要素をカンマで区切って表します。要素は「キー: 値」で指定します。{ } は要素が一つもない空のハッシュになります。

簡単な例を示します。

> d = {foo: 10, bar: 20}
Object {foo: 10, bar: 20}
> d["foo"]
10
> d["bar"]
20
> d.foo
10
> d.bar
20
> d["foo"] = 100
100
> d["foo"]
100
> d.foo = 1000
1000
> d.foo
1000
> d["baz"] = 200
200
> d["baz"]
200

ハッシュのアクセスは配列と同様に角カッコ [ ] を使うか、"ハッシュ + ドット ( . ) + キー" で行います。最初にハッシュを生成して変数 d にセットします。d["foo"] または d.foo でキー foo の値を取り出したり、そこに値を代入すれば、d["foo"] の値を書き換えることができます。値を取り出すとき、キーが見つからない場合は undefined を返します。また、新しいキー baz を追加する場合は、d["baz"] や d.baz に値を代入すると、ディクショナリに "baz" とその値が追加されます。

ところで JavaScript の場合、オブジェクトを表す Object というデータ型がハッシュになります。{ } または new Object() で空のハッシュを生成することができますが、実際にはオブジェクト指向機能を実現するためのデータが暗黙のうちに付加されることがあります。これはオブジェクト指向のところで詳しく説明します。


JavaScript の基礎知識 (制御構造編)

JavaScript の制御構造を簡単に説明します。

●条件分岐

条件分岐には if を使います。JavaScript の if はC言語とほぼ同じですが、真偽の判定が少し異なります。JavaScript の場合、真偽を表すデータ型 (boolean) として true と false が用意されていますが、数値や文字列など他のデータ型でも真偽を判定することができます。

まず、数値 0 (0.0) は偽を表し、それ以外の数値は真となります。文字列の場合、空文字列 "" は偽を表し、それ以外の文字列は真と判断されます。それから、特別な値 null と undefined は偽を表し、それ以外のデータは真と判断されます。

if の構文を示します。

if (test) { 処理A1; ...; 処理AZ; } else { 処理B1; ...; 処理BZ; }
if (test_A) { 処理A; } else if (test_B) { 処理B; } else { 処理C; }

条件部 test を実行し、その結果が真であれば、処理A1 から処理AZ を実行します。{ } で囲まれた部分を「ブロック」と呼び、ここに複数の処理を書くことができます。test の結果が偽であれば、else から始まるブロックで書かれている処理B1 から処理BZ を実行します。else ブロックは省略することができます。なお、ブロック内の処理 (文) が一つしかない場合は { } を省略することができます。これはC言語と同じです。

また、else if を使うことで、if を連結することができます。test_A が偽の場合は、次の else if の条件 test_B を実行します。この結果が真であれば処理B を実行します。そうでなければ、else ブロックの処理C を実行します。else if はいくつでも繋げることができます。

このほかにも、JavaScript にはC言語と同様の switch 文があります。

●比較演算子と論理演算子

JavaScript には下表に示す比較演算子が用意されています。

表 : 比較演算子
演算子意味
== 等しい
=== 等しい (型変換なし)
!= 等しくない
!== 等しくない (型変換なし)
<より小さい
>より大きい
<=より小さいか等しい
>=より大きいか等しい

C言語の比較演算子と同じですが、JavaScript は数値だけではなく、文字列の比較にも用いることができます。数値と文字列を比較することもできますが、その場合は文字列が数値に型変換されます。演算子 === と !== は型変換を行わないでデータを比較します。データ型が異なる場合は異なる値と判断されます。

簡単な例を示しましょう。

> 1 < 2
true
> 1 > 2
false
> 1 == 1
true
> 1 != 1
false
> 1 == "1"
true
> 1 === "1"
false
> "abc" < "def"
true
> "abc" > "def"
false
> "abc" == "abc"
true
> "abc" != "abc"
false

JavaScript には下表に示す論理演算子があります。

表 : 論理演算子
操作意味
!x x の否定(真偽の反転)
x && y x が真かつ y が真ならば真
x || y x が真または y が真ならば真

これもC言語と同じです。簡単な使用例を示しましょう。

> !true
false
> !false
true
> true && true
true
> true && false
false
> true || true
true
> true || false
true
> false || true
true
> false || false
false

論理積 && は、左項が偽ならば右項を評価せずに偽を返します。論理和 || は、左項が真ならば右項を評価せずに真を返します。

●繰り返し

繰り返しは同じ処理を何度も実行することです。JavaScript の繰り返しはC言語とほぼ同じです。まずは簡単な繰り返しから紹介しましょう。while 文は test が真であるあいだ、ブロック内の処理を繰り返し実行します。

while (test) { 処理A; 処理B; ...; }

簡単な例を示しましょう。hello, world を 10 回表示します。

> n = 0
0
> while (n < 10) {console.log("hello, world"); n += 1}
(10) hello, world

console.log() はデータをコンソールに出力する関数です。これはブラウザの JavaScript の組み込み関数です。Google Chrome のコンソールでは、同じデータが出力されたとき、その右側に回数 (本稿では (n) と表記) が表示されます。

変数 n を 0 に初期化し、n の値が 10 よりも小さいあいだ処理を繰り返します。C言語と同様に、n += i は n = n + i と同じ意味です。このほかに、-=, *=, /= も使うことができます。n の値はブロックを実行するたびに +1 されていくので、n が 10 になった時点で繰り返しを終了します。

このほかに、C言語と同様の do - while 文もあります。

do { 処理A; ...; 処理Z; } while (test)

while 文と違って、do - while 文は最初にブロック内の処理を実行します。簡単な例を示します。

> n = 0
0
> do { console.log("hello, world"); n++} while(n < 10)
(10) hello, world

n++ の ++ はインクリメント演算子といって変数の値を +1 します。つまり、n += 1 と同じ処理です。Cプログラマにはお馴染みの処理ですね。実は ++n のように、++ を変数の前につけることもできます。前と後ろでは +1 することは変わりませんが、処理結果が異なるところがあります。これは、それが問題になるところで説明しましょう。値を -1 するデクリメント演算子 -- も使えます。

次は for 文を説明します。JavaScript の for 文はC言語とほぼ同じです。

for (初期化; 条件部; 更新処理) { 処理A; ...; 処理Z; }

           図 : for の処理

for 文の特徴は、いちばん最初に行われる初期化と、繰り返すたびに行われる更新処理があることです。上図を見ればおわかりのように、初期化はただ一度しか行われず、更新処理はブロックの処理を実行してから行われます。簡単な使用例を示しましょう。

> for (n = 0; n < 10; n++) console.log("hello, world")
(10) hello, world

まず最初に、n = 0 で変数 n を 0 に初期化します。この処理は for が始まるときに一度だけ実行されます。次に条件部 n < 10 がチェックされます。n は 0 ですから条件を満たしますね。そこで、ブロックの処理が行われ、console.log() で hello, world を画面に表示します。

更新処理で変数 n の値を +1 したら、条件部のチェックを行います。あとは、while と同様に条件部が成立しているあいだは、ブロックの処理と更新処理を繰り返します。結局、n の値は 10 になるので、条件部が不成立となり繰り返しを終了します。したがって、このプログラムを実行すると hello, world が 10 回表示されます。

●繰り返しの制御

while 文や for 文は break 文によって繰り返しを脱出することができます。contiune 文は繰り返しの先頭に戻ります。これはC言語と同じ動作です。break 文と continue 文の動作を下図に示します。

test_b が真で continue 文が実行されると、それ以降の処理を実行せずに条件部のチェックが行われます。つまり、処理 B, test_c, 処理 C は実行されません。for 文で continue 文が実行されると、それ以降の処理は実行されずに更新処理と条件部のチェックが行われます。

test_c が真で break 文が実行されると、それ以降の処理を実行せずに while 文や for 文の繰り返しを脱出します。上図では、break 文で while 文の繰り返しを脱出すると、while 文の次の処理 D が実行されます。

●配列とハッシュの操作

配列は [ ] を使ってアクセスするだけではありません。JavaScript には便利な関数 (メソッド) が用意されています。主な操作を下表に示します。

表 : 配列に適用できる主な操作
操作意味
ary.length配列の大きさ
ary.reverse()配列を逆順に並べ替える
ary.push(x1, x2, ...)最後尾に要素を追加する
ary.pop() 最後尾から要素を削除する
ary.unshift(x1, x2, ...)先頭に要素を追加する
ary.shift() 先頭から要素を削除する

ary は配列を表します。配列の大きさは ary.length で求めることができます。length は関数ではなく値を格納する変数です。これを「属性 (プロパティ)」といい、ハッシュのキーと同じです。配列の操作はメソッド (method) として定義されています。メソッドは次の形式で呼び出します。

object.method(args, ...)

object はデータ (Object) のことで、その後ろにドット ( . ) を付けて、メソッド名と引数を続けて書きます。メソッドはオブジェクトを操作する関数のことで、JavaScript では多くの操作がメソッドとして定義されています。オブジェクト指向機能はあとで詳しく説明します。

簡単な例を示します。

> a = []
[]
> for (i = 0; i < 5; i++) { a.push(i); console.log(a); }
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
undefined
> for (i = 0; i < 5; i++) { console.log(a.pop()); console.log(a); }
4
[0, 1, 2, 3]
3
[0, 1, 2]
2
[0, 1]
1
[0]
0
[]
undefined
> for (i = 0; i < 5; i++) { a.unshift(i); console.log(a); }
[0]
[1, 0]
[2, 1, 0]
[3, 2, 1, 0]
[4, 3, 2, 1, 0]
undefined
> for (i = 0; i < 5; i++) { console.log(a.shift()); console.log(a); }
4
[3, 2, 1, 0]
3
[2, 1, 0]
2
[1, 0]
1
[0]
0
[]
undefined

オブジェクトのプロパティ (ハッシュのキー) は for - in 文で求めることができます。

for (変数 in オブジェクト) { 処理A; ...; 処理Z; }

for - in 文はオブジェクトから順番にプロパティを取り出して変数に代入し、ブロックに書かれている処理を繰り返し実行します。for と in の間にプロパティを格納する変数を指定し、in の後ろにオブジェクトを指定します。簡単な例を示します。

> d = {foo: 10, bar: 20, baz: 30}
Object {foo: 10, bar: 20, baz: 30}
> for (x in d) {console.log(x + " " + d[x]); }
foo 10
bar 20
baz 30
undefined

なお、for - in 文で取得できないプロパティもあります。たとえば、配列の大きさはプロパティ length に格納されていますが、for - in 文のオブジェクトに配列を指定しても length を取り出すことはできません。

●素数を求める

最後に簡単な例題として、素数を求めるプログラムを作ってみましょう。いちばん簡単な方法は、奇数 3, 5, 7, 9, ...をそれまでに見つかった素数で割ってみることです。見つけた素数は配列に格納しておけばいいでしょう。プログラムは次のようになります。

リスト : 素数を求める (1)

prime_list = [2];

for (x = 3; x <= 100; x += 2) {
    a = true;
    for (y = 0; y < prime_list.length; y++) {
        z = prime_list[y];
        if (x % z == 0) {
            a = false;
            break;
        }
    }
    if (a) prime_list.push(x);
}

console.log(prime_list);

変数 prime_list は素数の配列で [2] に初期化します。奇数の生成は for 文を使うと簡単です。変数 x を 3 に初期化し、更新処理で +2 していけばいいわけです。次の for 文で prime_list から素数を取り出して変数 z にセットします。

ここで変数 a の使い方がポイントになります。最初に a を true に初期化しておきます。x % z が 0 ならば x は素数ではないので、a を false に書き換えてから break します。そして、for ループが終了した後、変数 a をチェックして true であれば x を push() で prime_list に追加します。最後 に console.log() で prime_list を表示します。

それでは実行してみましょう。プログラムをファイル prime.js に保存して、HTML ファイルから読み込んでください。コンソールで Array[25] の左側の三角マークをクリックすると、配列の要素が表示されます。

▼Array[25]
0: 2
1: 3
2: 5
3: 7
4: 11
5: 13
6: 17
7: 19
8: 23
9: 29
10: 31
11: 37
12: 41
13: 43
14: 47
15: 53
16: 59
17: 61
18: 67
19: 71
20: 73
21: 79
22: 83
23: 89
24: 97

100 以下の素数は全部で 25 個あります。

ところで、この方法には無駄があります。x が素数か判別するため、x より小さい素数で割り切れるか調べていますが、実は√x より小さい素数を調べるだけでいいのです。次のリストを見てください。

リスト : 素数を求める (2)

prime_list = [2];

for (x = 3; x <=  100; x += 2) {
    a = true;
    for (y = 0; y < prime_list.length; y++) {
        z = prime_list[y]
        if (z * z > x) break;
        if (x % z == 0) {
            a = false;
            break;
        }
    }
    if (a) prime_list.push(x);
}

console.log(prime_list);

z > √x のかわりに z * z > x をチェックし、真であれば break で for 文を脱出します。これでプログラム (1) よりも高速に素数を求めることができます。

ところで、これらのプログラムはちょっとわかりにくいですね。この場合、関数を使うとわかりやすいプログラムを作ることができます。関数は次回で詳しく説明します。


Copyright (C) 2010-2015 Makoto Hiroi
All rights reserved.

[ PrevPage | JavaScript | NextPage ]