【JavaScript】JSを高速化するいくつかの方法

JavaScript

こんにちは、しきゆらです
最近はRubyよりもJSを書いている時間の方が長いです
JSであれこれやっていると、どうしても表示に時間がかかったり
途中でフリーズした?と思うくらい固まってしまうことが増えてきます
(書き方が悪いだけ・・・?)
その問題を解決する際の手がかりとして、いくつか実践してみたことがあるので
それをメモしておきます


以下、全ては以下の環境で試しています
  • MacBook Pro (Retina, 13-inch, Early 2015)
  • Intel Core i5 2.7 GHz
  • 8 GB 1867 MHz DDR3

DOMをfor文の中で追加しない

よく言われている手法です
DOM操作は時間がかかると言われます
ようは、要素を繰り返し追加すると
  • 要素を追加
  • 追加されたものをブラウザが描画
  • 要素を追加
  • ブラウザが再描画
  • ・・・
というように何度も描画するために時間がかかってしまうということ
そこで、複数行うのではなく1度にすべきということ
方法としては、「document fragment」というものを使うといいようです
// よくない例
for(var i = 0; i < array.length; i++){
     var div = document.createElement(“div");
     // divに対してあれこれする
     document.body.appendChild(div);
}

// こっちの方が良い
var temp = document.createDocumentFragment();
var length = array.length;
for(var i = 0; i < length; i++){
     var div = document.createElement(“div”);
     // divに対してあれこれする
     temp.appendChild(div);
}
document.body.appendChild(temp);

 

何度もinnerHTMLで追加するときは += を使わない

上記に関連して
for文等で繰り返しinnerHTMLで内容を追記するときは+=を使わない方がいいようです
配列に文字をpushしていき、最後にjoinでくっつける方が早いそうです
テストとして以下のような関数を作ってみました
引数のnumは繰り返す回数を指定します
どちらも、開始時と終了時の時間を取得し差分を求めています
// DOM要素を作り、innerHTMLに何度も追記する
function dom_plus(num){
     var start = new Date(),
         hoge = document.createElement("a");
     for(var i = 0; i < num; i++){
          hoge.innerHTML += "テスト" + i;
     };
     console.log(new Date() - start);
}

// innerHTMLを最後の一度だけ追記する
function dom_join(num){
     var start = new Date(),
         tmp = [],
         fuga = "fuga";
     for(var i = 0; i < num; i++){
          tmp.push("テスト" + i);
     }
     fuga.innerHTML += tmp.join("");
     console.log(new Date() - start);
}

 

結果は以下の通り
num = 1000;
dom_plus(num);dom_join(num);
51
1

num = 10000;
dom_plus(num);dom_join(num);
4955
7

この例では、実際にブラウザで表示している要素に対して文字列の追加を行っているわけではないですが

こんなにも差があることがわかります

実際にはこのほかに、ブラウザが再描画する時間が加算されるためもっと差は大きくなると思います
※おまけ
単純な文字列の追加も比較してみました
上記のものとの違いはhoge,fugaの中身がDOMから文字列に変わっただけ
function join(num){
     var start = new Date(),
         tmp = [],
         fuga = "fuga";
     for(var i = 0; i < num; i++){
          tmp.push("テスト" + i);
     } fuga += tmp.join("");
     console.log(new Date() - start);
}

function plus(num){
     var start = new Date(),
         hoge = "hoge";
     for(var i = 0; i < num; i++){
          hoge += "テスト" + i
     };
     console.log(new Date() - start);
}

 

結果は以下の通り
num = 100000;
plus(num);join(num)
51
41

num = 1000000
plus(num); join(num)
440
325

やはり、joinの方が早いようです

まあ、普通はただの文字列をこんなに追加することはないので

あまり気にするようなことでもないかもしれません

array.lengthは変数に保持する

 これもよく言われる手法
オブジェクトのプロパティへ毎回アクセスするより、変数に数字を入れておきそこにアクセスする方が早いらしい
簡単なテストを
function length_only(num) {
    var array = [];
    for (var i = 0; i < num; i++) {
        array.push(i);
    }

    var start = new Date();
    var result = [];
    for (var i = 0; i < array.length; i++) {
        result.push(i);
    }
    console.log(new Date() - start);
}


function length_variable(num) {
    var array = [];
    for (var i = 0; i < num; i++) {
        array.push(i);
    }

    var start = new Date();
    var result = [];
    var length = array.length;
    for (var i = 0; i < length; i++) {
        result.push(i);
    }
    console.log(new Date() - start);
}

 

結果は以下
num = 1000000;
length_only(num); length_variable(num);
27
29

length_only(num); length_variable(num);
29
28

length_only(num); length_variable(num);
27
30

length_only(num); length_variable(num);
24
29

length_only(num); length_variable(num);
24
20

 

・・・あれ?
あまり差がない
桁を増やしてみる
num = 10000000;
length_only(num);
length_variable(num);
280
252

length_only(num);
length_variable(num);
260
215

length_only(num);
length_variable(num);
204
201

length_only(num);
length_variable(num);
246
277

length_only(num);
length_variable(num);
224
215

 

う〜ん
いうほど差がないのかな
若干変数にした方が早いかも・・・?

無駄な変数を経由しない

私は、コードが汚いです
素のJSの場合はメソッド名が長いので一旦変数へ保持しておき
そこからあれこれするようにしていました
しかし、よくみてみるとその変数は1度しか使われていない
というような状況がたくさんみられました
そんな変数たちをさようならすると、多分早くなるはず
メモリを余分に確保し1度しか使わないより、メソッドの返り値をそのまま利用する方が早いはず
と、いうことで
いくつかの方法をメモしてみました
実際にこれらを行ったところ
処理に1.2sほどかかっていたものが600msくらいまで速くなりました
(そもそも、そんなにJSで何をやらせているんだって感じですが)
ここで挙げたもの以外にもこんな方法あるよ!とか
そこはこうしたほうがいいのでは?等がありましたら教えてください
おわり

Posted by しきゆら