HTML5のCanvasでつくるダイナミックな表現―CreateJSを使う

第35回 たくさんのパーティクルに弾けるようなアニメーションをさせる

この記事を読むのに必要な時間:およそ 5 分

配列の扱いを最適化する

前掲コード1は,前述「たくさんのパーティクルをステージのランダムな位置に置く」の項で抜書きして解説したJavaScriptコードと実は2か所だけ微妙に違っている。別に間違い探しではなく,どちらでも正しく動く。コード1が異なるのはつぎのふたつのステートメントだ。

// var particles = new Array();
var particles = [];

function createParticles(amount) {
  for (var i = 0; i < amount; i++) {

    var particle = new Particle(_x, _y, stageWidth, stageHeight);
    // particles.push(particle);
    particles[i] = particle;

  }
}

第1に,空の配列インスタンスをつくるとき,Array()コンストラクタでなく[]リテラルとして書いてもよい。第2に,配列エレメントを加えるには,Array.push()メソッドの替わりに,やはり[]アクセスが使える。コード1は,どちらも[]演算子を用いた。ちなみに,[]「ブラケット」bracketと呼ばれる。たまに,⁠ブランケット」blanketと間違える人もいるが,それはピーナッツのライナス(Linus)が抱えているあれだ。

配列の扱いは,どちらもブラケットを使った方がお得だ。なぜなら,処理が速くなる。なるほど,ブラケットを使うと配列の読み書きが速いのか,と覚えるのは少しばかり早とちりだろう。ブラケットのアクセスが速い訳では決してない。配列をつくるときと,エレメントを加える場合について,それぞれ順に解説しよう。

まず,Array()コンストラクタでインスタンスをつくるとき,渡す引数はつぎの表1のようにエレメントと長さのふたつの意味があり得る。引数が複数なら,つくられた配列インスタンスのエレメントとして納められる。しかし,正の整数ひとつを引数に渡したときは,その長さの配列がつくられる。それ以外のデータが引数であれば,そのひとつの値はやはりエレメントになる※1⁠。

表1 コンストラクタメソッドの引数によってつくられる配列の違い

コンストラクタ呼出し引数の意味つくられる配列
new Array()エレメント(なし)[]
new Array(3)長さ[undefined, undefined, undefined]
new Array(0, 1, 2)エレメント[0, 1, 2]
new Array("a")エレメント["a"]

このようにArray()コンストラクタは,引数の数とデータ型によって処理が変わる※2⁠。したがって,コンストラクタは呼び出されたとき,引数の数とデータ型から,値をどう扱うのか選ばなければならない。そのタメが一瞬生じる図3⁠。ところが,[]を用いたリテラルで書けば,配列の長さは決められないので,中身はすべてエレメントだ。つまり,ブラケットなら一切の迷いがない。そのスタートの差が生じる。

図3 引数の数とデータ型から値の扱いを選ばなければならない

図3 引数の数とデータ型から値の扱いを選ばなければならない

つぎに,Array.push()メソッドは,引数のエレメントを配列の最後に加える。それはつまり,配列の最後尾を調べなければならないということだ。つい先頃も,ビッグウェーブに乗った行列が話題になった図4⁠。長い行列の最後尾を探すのはそれなりに手間だ。だが,前掲コード1のパーティクルをつくる関数(createParticles())のように,加える配列のインデックスがわかっているときは,それをブラケットで与えれば手間が省ける。

図4 配列の最後尾を探さないとけいない

図4 配列の最後尾を探さないとけいない

結局,ふたつの場合において,ブラケットを使うとひと手間省けることが速さにつながっている。ブラケットを用いたとしても,たとえば長さがわからない配列の最後にエレメントを加えようとしたら,つぎのようにArray.lengthプロパティで長さを調べるしかない。こうなると,手間はArray.push()メソッドを使うのと変わらなくなってしまう。⁠ブラケットのアクセスが速い訳では決してない」と述べたのはこういう意味だ。

配列[配列.length] = エレメント

では,ブラケットを用いたとき速さがどれだけ稼げるかというと,100万回処理を繰返して数ミリから数百ミリ秒,つまりせいぜいコンマ数秒といったところだ。しかも,オペレーティングシステムやブラウザおよびそのバージョンによってばらつきも大きい。ブラウザがJavaScriptの最適化を十分に施していれば,実質的な差が認められないこともある。

しかも,今回のコード1では,配列インスタンス(particles)をたったひとつしかつくっていない。配列にエレメントを加えるのも,パーティクルをつくる関数(createParticles())が初めに1度行うだけだ。書き替えたコンテンツの動きに違いはほとんどない。とはいえ,ちりも積もれば山となる。ブラケットを使う不利益はない。コードを書くのも簡単だし,見やすいだろう。⁠よい習慣」best practiceとしてお勧めする所以だ※3⁠。

配列はたくさんのエレメントをまとめて,すべてに処理を加えるときよく用いられる。だがそれだけでなく,他のさまざまな用途にも幅広く対応している。つまり,汎用性が高い。けれど,あえて使わない機能を削れば,速さの稼げる余地が生まれる。次回は,エレメントすべてを取出して処理する機能に絞り込んだ「連結リスト」⁠linked list)について解説しよう。

※1
Array()コンストラクタの引数に,整数でない数値や負数をひとつ渡すとエラー(例外: RangeError)になる(MDN「Array」引数の項参照⁠⁠。
※2
プログラミングでは,引数の数やデータ型によって同じ名前の関数に異なる定義を与えることはオーバーロード⁠多重定義)と呼ばれる。ただし,JavaScriptコードで関数やメソッドをオーバーロードして定めることはできない。
※3
プログラムは基本的な書き方をそのときどきで変えると,統一性がなくなり,間違いや戸惑いのもととなる。Arrayインスタンスをつくるときは,一般にコンストラクタよりリテラルを用いる方が,見やすく意図しないエラーも防げるとされる(Google JavaScript スタイルガイド配列リテラルとオブジェクトリテラル参照⁠⁠。

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書