DOMを読み込んでからJavaScriptを起動する
基本的にDOMを操作するため、DOMの読み込みが完了する前にJSを読み込むとDOMがないためエラーになるか想定しない動作になります。
のでDOMの読み込みを待ちましょう。
window.onload = function () {
// DOMが読み込まれた後に実行される
};
// jQueryを使う
$(function () {
// DOMが読み込まれた後に実行される
});
// RequireJS
require(["domReady"], function () {
// DOMが読み込まれた後に実行される
});
<body>
<p>読み込み待ち</p>
<script type="text/javascript">
(function () {
// ここに到達するまでに読み込まれたDOMは使用できる
// BODYの終わりの直前なので全部使える
})();
</script>
</body>
画面が固まるので非同期 + Deferred を使う
JavaScriptはシングルスレッドです。
なのでスクリプト実行中は描画しません。
ということはブラウザが操作不能になります。
避けるためにHTTP通信は非同期にしましょう。
$.ajax({async: true, url: "http:/localhost/user"});
でも非同期だと結果を取れないのでDeferredを使います。
$.ajax({async: true, url: "http:/localhost/user"})
.done(function (resp, status, xhr) {
// 結果をほにゃららする
resp;
});
なぜ使うかの記事
http://qiita.com/yuku_t/items/1b8ce6bba133a7eaeb23
関数定義は先に処理される
JavaScriptは先に関数定義が処理されます。
なので関数定義の前に処理があっても動きます。
if (f) {
console.log("fは存在します");
}
function f () {
}
thisの扱い
JavaScriptではthisは変動します。
クロージャーには自動で現在のthisではなくwindow(global)がくっつきます。
obj = {
main: function () {
this.name = "main";
(function () {
console.log(this.name); // undefined
console.log(this); // window
})();
}
}
obj.main();
解決するには自信の参照を変数に格納しておくか、関数のbind関数(es5)を使用します。
基本的にはselfを定義して、それを使うようにします。
// 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文を使うとクロージャーでバインドする値が変わる
まずは例を
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が再利用されてしまい中身が書き変わります。
そして当然バインドしているクロージャーの変数の中身も書き変わります。
ではどうするか。
関数内に閉じこめます。
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を使いましょう。
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を記述しましょう。
$.get("/users/1").done(function (resp) {
try {
throw new Error();
} catch (e) {
// ここで例外処理する
}
}).fail(function (xhr, status) {
// そのまま例外を投げてもここには入らない
});
JSONにする場合は循環参照のオブジェクトに注意する
循環参照を処理できないので、循環参照するオブジェクトを取り除く必要があります。
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"}}"