JavaScript開発時のTips

# DOMを読み込んでからJavaScriptを起動する

基本的にDOMを操作するため、DOMの読み込みが完了する前にJSを読み込むとDOMがないためエラーになるか想定しない動作になります。

のでDOMの読み込みを待ちましょう。

```js:DOMの読み込み待ち
window.onload = function () {
  // DOMが読み込まれた後に実行される
};

// jQueryを使う
$(function () {
  // DOMが読み込まれた後に実行される
});

// RequireJS
require(["domReady"], function () {
  // DOMが読み込まれた後に実行される
});
```

```html:DOMの読み込み待ち
<body>
  <p>読み込み待ち</p>
  <script type="text/javascript">
    (function () {
      // ここに到達するまでに読み込まれたDOMは使用できる
      // BODYの終わりの直前なので全部使える
    })();
  </script>
</body>
```

# 画面が固まるので非同期 + Deferred を使う

JavaScriptはシングルスレッドです。
なのでスクリプト実行中は描画しません。
ということはブラウザが操作不能になります。

避けるためにHTTP通信は非同期にしましょう。

```js:非同期にする
$.ajax({async: true, url: "http:/localhost/user"});
```

でも非同期だと結果を取れないのでDeferredを使います。

```js:非同期で結果を取得する
$.ajax({async: true, url: "http:/localhost/user"})
  .done(function (resp, status, xhr) {
    // 結果をほにゃららする
    resp;
  });
```

なぜ使うかの記事
http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23

# 関数定義は先に処理される

JavaScriptは先に関数定義が処理されます。
なので関数定義の前に処理があっても動きます。

```js:処理が先、関数定義が後
if (f) {
  console.log("fは存在します");
}

function f () {
}
```

# thisの扱い

JavaScriptではthisは変動します。
クロージャーには自動で現在のthisではなくwindow(global)がくっつきます。

```js:thisがwindow
obj = {
  main: function () {
    this.name = "main";
    (function () {
      console.log(this.name); // undefined
      console.log(this); // window
    })();
  }
}
obj.main();
```

解決するには自信の参照を変数に格納しておくか、関数のbind関数(es5)を使用します。
基本的にはselfを定義して、それを使うようにします。

```js:bind関数
// selfへthisを格納
obj = {
  main: function () {
    this.name = "main";
    var self = this;
    (function () {
      console.log(self.name); // main
      console.log(self); // obj
    })();
  }
}

// bind
obj = {
  main: function () {
    this.name = "main";
    (function () {
      console.log(this.name); // main
      console.log(this); // obj
    }.bind(this))();
  }
}
```

# for文を使うとクロージャーでバインドする値が変わる

まずは例を

```js:バインドする値が変わる
var funcs = [];
var ary = ["a", "b", "c"];
for (var i = 0 ; i < ary.length ; i++) {
  var value = ary[i];
  funcs.push(function () {
    console.log(value);
  });
}

for (var i = 0 ; i < funcs.length ; i++) {
  funcs[i](); // -> c, c, c
}
```

このようにバインドされたvalueの参照がa, b, c と変わってしまいます。
JavaScriptでは関数内に変数のスコープを閉じ込めることができますが、ブロック内、今回でいえばfor文内でスコープを閉じ込めることができません。
そのため変数valueが再利用されてしまい中身が書き変わります。
そして当然バインドしているクロージャーの変数の中身も書き変わります。

ではどうするか。
関数内に閉じこめます。

```js:関数内に閉じ込める
var funcs = [];
var ary = ["a", "b", "c"];
for (var i = 0 ; i < ary.length ; i++) {
  var value = ary[i];
  (function (value) {
    funcs.push(function () {
      console.log(value);
    });
  })(value);
}

for (var i = 0 ; i < funcs.length ; i++) {
  funcs[i](); // -> a, b, c
}
```

でもこれではださいですね。
forEachを使いましょう。

```js:forEachを使う
var funcs = [];
var ary = ["a", "b", "c"];
ary.forEach(function (value) {
  funcs.push(function () {
    console.log(value);
  });
});

for (var i = 0 ; i < funcs.length ; i++) {
  funcs[i](); // -> a, b, c
}
```

# IEにはコンソールが存在しない

IEではデバッグモードしないとconsoleオブジェクトが存在しません。
対処としてconsole-polyfillライブラリなどを使ってコンソールオブジェクトのダミーを生成しておきます。

# 独自例外は諦める

Errorの継承の仕方は諸説ありますが、完璧なものがどれなのか定まっていません。
なのでErrorを継承して独自例外を作るのは動作しない場合があるのであきらめましょう。

# Deferredでの例外処理

Defferedで例外を発生させるとグローバルに例外が投げられるので、自分でtry-catchを記述しましょう。

```js:Defferedで例外
$.get("/users/1").done(function (resp) {
  try {
    throw new Error();
  } catch (e) {
    // ここで例外処理する
  }
}).fail(function (xhr, status) {
  // そのまま例外を投げてもここには入らない
});
```

# JSONにする場合は循環参照のオブジェクトに注意する

循環参照を処理できないので、循環参照するオブジェクトを取り除く必要があります。

```js:循環参照を回避する
var user = {
  name: "me"
};
var address = {
  adrs: "tokyo"
};
user.address = address;
address.user = user;

JSON.stringify(user);
// -> TypeError: Converting circular structure to JSON

JSON.stringify(user, function (key, value) {
  if (key == "user") {
    return;
  }
  return value;
});
// -> "{"name":"me","address":{"adrs":"tokyo"}}"
```