とあるコードで
var View = Backbone.View.extend({
data: [],
initialize: function() {
this.data.push($('#hoge'));
},
reset: function() {
this.data = [];
}
});
みたいなのがあったんですが、これ若干はまりポイントがあるので注意です。
何故か
Backbone.extendによるコンストラクタ生成の時に指定したオブジェクトのプロパティは 全てprototypeオブジェクトのプロパティとして定義 されます。
つまり、上記のコードを通常のJavaScriptのコンストラクタ生成パターンで示すと
function View() {
}
View.prototype = {
data: [],
initialize: function() {
},
reset: function() {
}
}
// 本来ならBackbone.Viewのprototypeとかの処理も入りますが省略
のような定義と同様になるため、インスタンスを作成した時に
var view = new View();
console.log(view.__proto__.data); // -> []
console.log(view.__proto__.initialize); // -> function() {}
console.log(view.__proto__.reset); // -> function() {}
のように プロトタイプチェーンの参照オブジェクト(__proto__
)上に定義 されます。まぁそりゃそうですね。
で、何を注意しなければならないか
注意すべきは、参照と代入です。
意図せずプロトタイプチェーン上の__proto__
オブジェクトに存在するプロパティへアクセスを行ったり、逆にさっきまで触っていたデータへアクセス出来なくなるなどの可能性があります。
また、インスタンスに付く__proto__
オブジェクトはprototype
オブジェクトの参照なので全インスタンス間とコンストラクタのprototype
で共有されるのも注意です。
var View = Backbone.View.extend({
data: [],
});
var view = new View();
// view.data に $('#hoge') を push しようとするが、view に data というプロパティは存在しないので
// プロトタイプチェーンを遡って this.__proto__.data へ代入される
view.data.push('hoge'); // view.__proto__.push('hoge') と同等
// view.data に [](空配列) を代入しようとすると、view オブジェクトの data というプロパティへ空配列が定義される
view.data = []; // この時点で view.data と view.__proto__.data が存在することになる
console.log(view.data); // -> [](空配列) (view オブジェクトに data プロパティは存在するので)
// では、view.dataを削除したとすると?
delete view.data;
console.log(view.data); // -> ['hoge'] (view.__proto__.data は存在するので)
// view.__proto__.data を消したくても delete view.data では消せないので
// 勢い余って delete view.__proto__.data などとすると…
delete view.__proto__.data;
console.log(view.data); // -> undefined
console.log(view.__proto__.data); // -> undefined
console.log(View.prototype.data); // -> undefined (view.__proto__ === View.prototype なので当然コンストラクタ側のものも消えてしまう)
みたいなのにハマったりしました。まぁそんなに話題に出ないのでエッジケースではあるのですが。
あとはこんなことが起こりそう
- リセット or delete したはずなのに
__proto__
側が参照を持っていてメモリリークが発生し突然の死 - 複数インスタンスで
__proto__
オブジェクトが共用されていて訳分からんデータで埋まったり消えたりして突然の死 - 今どこのオブジェクトにデータを入れたのか分からなくなって突然の死
などなど。人類の可能性は無限大だぜ!
これからもprototype
のことをよろしくな!!
余談
個人的にはBackbone.Hoge.extend
に渡すオブジェクトへ、ダイナミックな操作を行うオブジェクト(や配列)を入れなければいいんじゃねと思うます。そういうのはinitialize
メソッドでやった方が確実に幸せになれます。
Backboneガチ勢はどうしてるんですかね。