W dniu dzisiejszym Backbone.js zaskoczył mnie kolejny raz. Niestety tym razem negatywnie…
Właściwości
Tworzenie “klas”, które dziedziczą po sobie właściwości jest czym naturalnym w Backbone. Nadpisywanie tychże właściwości jest równie proste. Przykład:
var AbstractWidget = Backbone.View.extend({
template: 'foo.html',
});
var MenuWidget = AbstractWidget.extend({
template: 'bar.html',
});
var v = new MenuWidget();
v.template; // bar.html
Niestety, to działa tylko wtedy, gdy wartość właściwości przypisujemy na nowo. Jeśli ją tylko modyfikujemy, zaczynają się problemy.
Problem
Inaczej sytuacja wygląda gdy mamy do czynienia z obiektami:
var AbstractWidget = Backbone.View.extend({});
var MenuWidget = AbstractWidget.extend({
templateParams: {},
initialize: function () {
this.templateParams.id = _.uniqueId();
},
});
var x = new MenuWidget();
var v = new MenuWidget();
x.templateParams.id; // "4"
v.templateParams.id; // "4"
Co ciekawe… gdy zamienimy kolejność wywołania ostatnich linijek:
var x = new MenuWidget();
x.templateParams.id; // "2"
var v = new MenuWidget();
v.templateParams.id; // "4"
// Ale gdy uruchamiamy następującą linię:
x.templateParams.id; // "4"
Wartości są stringami i co drugą liczbą, bo konstruktor Backbone.View przed
initialize sam woła _.uniqueId('view'), żeby nadać widokowi cid.
Rozwiązanie
Problemy leży o podnóża działań JavaScript.
var AbstractWidget = Backbone.View.extend({});
var MenuWidget = AbstractWidget.extend({
templateParams: {},
initialize: function () {
// czyścimy obiektowe właściwości
this.templateParams = {};
this.templateParams.id = _.uniqueId();
},
});
var x = new MenuWidget();
var v = new MenuWidget();
x.templateParams.id; // "2"
v.templateParams.id; // "4"
Skąd ten problem? Właściwości przekazane do extend() trafiają do
prototypu, więc wszystkie instancje MenuWidget współdzielą
jeden i ten sam obiekt templateParams. Zapis
this.templateParams.id = ... nie tworzy nowego obiektu w instancji, tylko
modyfikuje ten wspólny. Przy template problem nie występował, bo
przypisanie this.template = ... tworzy własną właściwość instancji
i przesłania wartość z prototypu. Dlatego w initialize tworzymy każdej
instancji jej własny obiekt templateParams.
Cya! 👋