質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.49%

  • JavaScript

    16934questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

NodeListやHTMLCollectionの配列化

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,366

flat

score 591

NodeListやHTMLCollectionといったArray-Like ObjectをArray Objectに変換すると、ループ処理などの時に高速に処理できるという旨の情報を目にしたのですが、これはgetElementsBy~などで取得したArray-Like Objectに対して「とりあえずやっておく」というおまじない的なレベルで利用できるものと見て良いのでしょうか?
また、Array Objectにすることによる他のメリットやデメリットなどもあればお聞きしたいです。

追記
実際に100個の要素をgetElementsByClassNameで取得してループさせ、classNameプロパティを使ってクラスを追加するという内容で処理時間を比較して計測したところ、2倍以上の処理時間を短縮を確認できました。
また、element.className = element.className + ' check';を削除するとその差がより大きくなり、少なくとも20倍の差が開きました。
この結果からすると、高速に処理できるという点は間違いないと見て良いのでしょうか。
それとも、この計測の仕方が適切ではないだけでしょうか?
ちなみに、使用したブラウザはGoogle Chrome 49です。

追記
一応計測結果をコードの方に追記

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>example</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
.check {
  color: red;
}
</style>
<script type="text/javascript">
;( function () {
  'use strict';
  function func(event) {
    var doc = event.target;

    console.time('get');

    // getElementsByTagName = HTMLCollection
    var elements = doc.getElementsByTagName('p');

    // getElementsByClassName = HTMLCollection
    //var elements = doc.getElementsByClassName('element');

    // querySelectorAll = NodeList
    //var elements = doc.querySelectorAll('.element');

    // Array
    //var elements = Array.prototype.slice.call(doc.getElementsByClassName('element'));

    console.timeEnd('get');

    var l = elements.length;
    var element;

    console.time('for');
    for (var i = 0; i < l; i++) {
      element = elements[i];
      element.className = element.className + ' check';
    }
    console.timeEnd('for');
  }

  document.addEventListener('DOMContentLoaded', func, false);

  /*
   * 取得(処理時間が短い順)
   *
   * getElementsByTagName & getElementsByClassName
   * 要素数1000:  約0.03ms
   * 要素数10000: 約0.03ms
   *
   * querySelectorAll
   * 要素数1000:  約0.1ms
   * 要素数10000: 約0.4ms
   *
   * Array(Array.prototype.slice.call(HTMLCollection)で配列に変換)
   * 要素数1000:  約2.5ms
   * 要素数10000: 約7ms
   * 要素の取得時間というよりは、ほぼ変換処理にかかる時間
   */

  /*
   * forループ処理(処理時間が短い順)
   *
   * Array
   * 要素数1000:  約0.8ms
   * 要素数10000: 約9ms
   *
   * getElementsByTagName
   * 要素数1000:  約2.5ms
   * 要素数10000: 約12ms
   *
   * querySelectorAll
   * 要素数1000:  約2.6ms
   * 要素数10000: 約13ms
   *
   * getElementsByClassName
   * 要素数1000: 約15ms
   * 要素数10000: 約756ms
   */
}());
</script>
</head>
<body>
<div class="elements">
  <!-- 100 element -->
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
  <p class="element">element</p>
</div>
</body>
</html>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

URLのページに書かれている通り、静的な配列に変換したほうが早いですね

変換のメリットは、配列メソッドが使えるようになったり、動的でないこと

例えば

var list = document.getElementsByTagName("li")
for(var i=0;i<list.length;i++){
    list[i].remove()
}


というのはうまくいかないです

動的なので remove した瞬間にずれてきます
配列なら [0] を remove したら [0] に remove 済みがあって [1] 以降に remove されていないのがありますが、 動的な場合は、 [0] に もともと [1] だったのが入ります
shift() したみたいになります

デメリットはこれも動的でなくなること
動的だと、上の例みたいな remove されたときに、何番目の要素がすでに remove されてるなんて気にしないで済みます
HTMLCollection の中にいるのは全部ツリーに属しています

動的であって欲しい使い方をしないならとりあえず 変換しますね
querySelectorAll で取得できる NodeList は静的なのでこっちは配列メソッド使うくらいしかメリットないです

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/25 01:34

    回答して下さりありがとうございます。

    動的なオブジェクトは、DOMに加えられた変更がそのままオブジェクトにも反映される。
    配列化した静的なオブジェクトは動的なオブジェクトの要素をコピーした配列なので、要素の変更などは出来てもDOMツリーの反映はされず、要素は欠けたままになる。
    という理解で大丈夫ですかね……?

    取得したオブジェクトに削除などの変更を加えなければ配列化しておいて良さそうですね。
    削除などをした場合もインデックスを詰めたら大丈夫そうな気がします。
    あまり自信はありませんが……。

    キャンセル

  • 2016/03/25 02:08

    だいたいそんなとこです

    削除に限らず「取得した条件にあっているか」なので、質問の追記にあるものだと、
    `element` というクラスのものを取得しているので、`elements ` のHTMLCollection には `element` というクラスがあるものだけになります

    要素は削除せずに、ひとつ目のelementのクラス名を消すと `elements` の要素数は 99 個になります

    上で書いたコードと同じ感じことですが、質問の追記コードで

    element.className = element.className + ' check';

    とあるのを

    element.className = "";

    こうしてしまうと、element を持たないクラスが飛び飛びでできてしまいます
    あと、先に length を取得しているので 途中から undefined のプロパティを見に行ってエラーになります

    この辺りを把握しておけば大丈夫かと思います
    ほとんどの場合は querySelector で NodeList を取得するでしょうし

    キャンセル

  • 2016/03/25 20:29

    確かに、取得した条件と処理内容が合っているのかはちゃんと確認しないといけませんね。
    取得した情報がどういうものなのかを正しく認識することは大事ですね……。

    ちなみに処理時間に関しては処理の内容にもよるとは思いますが、要素が1000以上あったり、何度もループで参照するようなケースでなければあまり気にしなくて良さそうだと思いました。
    通常はそのままgetElementsBy系のメソッドでHTMLCollectionを取得して、静的なオブジェクトが欲しければquerySelectorAllでNodeListを取得し、配列として扱いたい時は処理内容に注意した上で配列化するという風にしようと思います。

    回答して下さったお二人に感謝致します。
    ありがとうございました。

    キャンセル

+1

配列化する事で高速化される手法は聞いたことがありませんし、原理的にも速くなるとは思えません。
繰り返し処理で速度を競うなら「while > for > Array.prototype.forEach」となるので配列化せずとも繰り返し文を高速に処理できます。

配列化するメリットは Array.prototype 上にあるメソッドを何度も呼び出す場合に Function#call で this 値を指定するコストを支払わなくて良い事ですね。
配列化するデメリットは変換コストが余計にかかる事です。
Array.prototype 上にあるメソッドを呼び出す回数が著しく多く、メリットがデメリットを上回る状況で使用を検討して下さい。

Re: flat さん

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/25 00:13

    回答して下さりありがとうございます。

    Array.prototype が利用できるのは、回答して下さったcallメソッドも含め、使い方次第で何か活用できそうですね。

    ちなみに、この質問は以下の投稿を見て気になったのでさせて頂きました。

    http://inside.pixiv.net/entry/2015/12/23/120000

    自分なりにも色々と調べてみたのですが、今ひとつすっきりしないので「そうだ、実際に計測したら良いんだ」という事で計測してみたところ、配列化した処理をループさせると処理時間が大幅に短縮されました。

    質問の方にも追記しておきます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    16934questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。