私も最近この問題に遭遇しました。あなたはそれを解決したように見えますが、なぜこれが起こっているのかを正確に疑問に思っている他の人にとっても、より詳細な説明が役立つかもしれないと思います。
では、実際に何が起こっているのでしょうか。
モデル(本)のコレクション(ライブラリ)があるとします。
例えば:
console.log(library.models); // [object, object, object, object]
それでは、最初のアプローチを使用して、すべての本を調べて削除しましょう。
library.each(function(model) {
model.destroy();
});
each
Backboneコレクションに混合されているアンダースコアメソッドです。モデルへのコレクション参照(library.models
)を、これらのさまざまなアンダースコアコレクションメソッドのデフォルト引数として使用します。もちろん。それは合理的に聞こえます。
これで、モデルがを呼び出すとdestroy
、コレクションでも「破棄」イベントがトリガーされ、モデルへの参照が削除されます。内部remove
では、これに気付くでしょう:
this.models.splice(index, 1);
に慣れていない場合は、ドキュメントsplice
を参照してください。もしそうなら、なぜこれが問題になるのかがわかるかもしれません。
実証するためだけに:
var list = [1,2];
list.splice(0,1); // list is now [2]
これにより、each
経由するモデルオブジェクトへの参照がmodels
動的に変更されるため、ループは要素をスキップします。
ここで、JavaScript <1.6を使用している場合、次のエラーが発生する可能性があります。
Uncaught TypeError: Cannot call method 'destroy' of undefined
これは、のアンダースコア実装では、ネイティブが欠落each
している場合、それ自体の実装にフォールバックするためです。forEach
存在しない要素にアクセスしようとするため、反復の途中で要素を削除すると文句を言います。
ネイティブforEach
が存在する場合は、代わりにそれが使用され、エラーはまったく発生しません。
なんで?ドキュメントによると:
配列の既存の要素が変更または削除された場合、コールバックに渡されるそれらの値は、forEachがそれらにアクセスしたときの値になります。削除された要素は訪問されません。
それで、解決策は何ですか?
collection.each
コレクションからモデルを削除する場合は使用しないでください。モデルへの参照を含む新しい配列で作業できるようにするメソッドを使用します。clone
1つの方法は、アンダースコアメソッドを使用することです。
_.each(_.clone(collection.models), function(model) {
model.destroy();
});