2013-08-09 15 views
6

Próbuję pobrać istniejącą działającą podstawę kodu i uczynić ją obiektową przy użyciu JavaScript. Mój system przyjmuje JSON zawierający grup i pozycji w relacji jeden do wielu i wizualizuje to na stronie. Elementy można przenosić do różnych grup, a ich obliczenia w obrębie tych grup również należy obliczyć. W związku z tym konieczne będzie ustalenie wydarzeń, które będą świadome zarówno grup, jak i biletów wokół nich.Okrężna zależność między klasą JavaScript a obiektem jQuery

Używam prostej konfiguracji dziedziczenia JavaScript w celu ustalenia dwóch klas: Item i Group. Po utworzeniu instancji Item odwołuje się ona do jej rodzica Group. Mój problem powstaje kiedy chcą założyć swoje wydarzenia, a najłatwiej jest wyjaśnić następującą funkcję:

var Group = Class.extend({ 
    ... 
    // Calculate where to place the new item within the group 
    calculate_new_position: function(item) { 
    var pos = -1; 
    // Loop through all DOM items in groups body 
    $(".item", this.elBody).each(function(i) { 

     // Retrieve it's class object 
     var next = $(this).data("_obj"); 

     // Calculating things using the class reference 
     var lowerPrio = item.tData.priority < next.tData.priority, 
      lowerId = item.id < next.id; 

     if(lowerPrio || lowerId) { 
     pos = i; 
     return false; 
     } 
    }); 
    return pos; 
    }, 
    ... 
)}; 

Uwaga użycie .data("_obj") w powyższym fragmencie. Zasadniczo, gdy potrzebuję zrobić coś takiego jak sortowanie przedmiotów, muszę znać obiekt (model) odpowiadający DOM DOM każdego elementu (widok/kontroler) w grupie. Teraz mógłbym ustanowić moją klasę Group tak, że po utworzeniu każdego dodaję odniesienie do tego z mojego Group (np. Tak Group.items = [i1, i2...]), a następnie zamiast powtarzać nad elementami DOM, mógłbym iterować nad instancjami Item. Jednak myślę, że borykałbym się z podobnymi problemami w dół, na przykład kiedy chcę przenieść Item do innego Group (ponieważ Group nie wiedziałoby o Item).

Krótko mówiąc: jest to wewnętrznie niebezpieczne/naiwny/bezcelowe mieć klasę, która po instancję tworzy element DOM, które potem z kolei punkty z powrotem do klasy? To wydaje się być zależnością cykliczną i pewnym rekurencyjnym koszmarem, ale być może odniesienie do obiektu nie jest tak straszne. Jeśli robię cokolwiek innego, to jest to głupie i jest o wiele prostszy sposób, więc proszę o to również.

+0

Czy obejrzałeś już działanie Backbone.js? Obiekt 'view' zawiera odniesienie do' modelu' (twój przedmiot, klasa grupy) i elementu DOM 'el'. Istnieje wiele sposobów na to i Kręgosłup.js nie poleca w żaden sposób tutaj: http://backbonejs.org/#FAQ-tim-toady – vsr

Odpowiedz

2

Każdy współczesny garbage collector może obsługiwać odwołania kołowe. Twój obiekt zostanie zebrany, jeśli utracisz wszystkie odniesienia do obiektu i usuniesz wszystkie węzły HTML z DOM (podczas gdy w DOM przeglądarka zobaczy odniesienie do twojego obiektu i zapobiegnie gromadzeniu śmieci). Zachowaj ostrożność przy obsłudze zdarzeń, ale mogą one zachować refencje, jeśli ich nie usuniesz.

Słyszałem, że niektóre starsze wersje IE mają problem z odwołaniami cyklicznymi. Więcej w depht wyjaśnienia: Precise explanation of JavaScript <-> DOM circular reference issue

Od tej odpowiedzi: jQuery .data() sprawia, że ​​rzeczą bardziej wiarygodne, ponieważ starsze wersje IE miał szczególny problem z właściwości, które zostały dodane do elementu DOM i nie obsługiwać okrągły referencje prawidłowo angażujące dane w te właściwości, a zatem nie uwolniłyby rzeczy, gdy powinny (przeciek). Funkcja .data() działa w ten sposób, używając tylko jednej dodanej właściwości w elemencie DOM, która jest bezpiecznym, nie przeciekającym ciągiem. Ten ciąg jest następnie kluczem do obiektu javascript, który może zawierać wszystkie właściwości, które chciałbyś powiązać z elementem DOM. Ponieważ wszystkie te właściwości są przechowywane w prostym javascript, gdzie przeglądarki nie mają okrągłych błędów odniesienia, robienie tego w ten sposób nie powoduje przecieków.

Być może zechcesz zajrzeć do D3.js, który robi coś podobnego do tego, którego potrzebujesz.Wiąże dane do elementów DOM i zapewnia łatwy sposób na generowanie wizualizacje: http://d3js.org/

Korzystanie D3 można związać tablicę liczb do tablicy okrąg tagów SVG i zrobić to tak, że każde koło ma promień na podstawie liczby z tablicy, z którą jest skojarzony.

EDYCJA: Zapomniałem wspomnieć, że jeśli użyjesz funkcji $ .remove(), procedury obsługi zdarzeń również zostaną usunięte. Ale jeśli masz na przykład procedurę obsługi zdarzenia, która znajduje się wewnątrz zamknięcia, która również ma odniesienie do twojego (usuniętego) węzła HTML, to nie będzie zbiorem śmieci. Zwykle nie jest to duży problem, ponieważ gdy znajdzie się poza DOM, nie będzie zużywał zbyt dużo zasobów i nie będzie możliwe wielokrotne wykorzystanie tych odniesień do zamknięcia, o ile wiem.

1

Podczas korzystania z jQuery.data nie robi nic z elementem dom. Element dom po prostu zapisuje prymitywny wpis klucza do wewnętrznej pamięci podręcznej jQuery, bez względu na *. Odniesienie utworzyć dlatego jest z JS do JS:

var div = document.createElement("div"); 
$(div).data("asd", "daa") 
console.log(div[jQuery.expando]); 
//41 
console.log(jQuery.cache[41].data.asd); 
//"daa" 

(oczywiście powyższe jest wewnętrzne jQuery i nie powinny być powołane w kodzie produkcji)

powiedział, że dane będą tam, jeśli wyciek nie przechodzisz przez jQuery, aby wykonać wszystkie manipulacje, ponieważ jQuery nie zostanie powiadomiony, jeśli przejdziesz za jego plecami.

Najlepszą praktyką z klasami widżetów jest dostarczenie metody destroy, która usuwa wszystkie elementy, za które jest odpowiedzialna, usuwa powiązania wszystkich dołączonych zdarzeń i ustawia wszystkie odwołania do DOM na wartość null.

* Jeśli nie ma absolutnie żadnych zdarzeń lub danych dotyczących elementu, wpis nie istnieje. Ale jeśli wpis istnieje z jakiegokolwiek powodu, użycie jQuery.data nie będzie oddziaływać z samym elementem.

1

@ Odpowiedź Hoffmanna jest dobra, ponieważ oferuje praktyczne porady i objaśnienia. Oferuję inną perspektywę, inspirowaną działaniem Backbone.js. Jest to rozszerzenie na to, co @vsr zasugerował w swoim pierwszym komentarzu.

Podstawowy ciąg: Nie przechowuj swoich obiektów JS w $ .data(), w ogóle.

Używanie $ .data() sugeruje, że używasz $ (foo) do wyszukiwania obiektów DOM, a następnie drążenia ich właściwości, aby uzyskać niestandardowe funkcje JS, które mogą działać na więcej elementów DOM, aby uzyskać ich funkcje niestandardowe. Jest to nadmiernie skomplikowane i nieefektywne.

Lepszym rozwiązaniem jest stworzenie zestawu obiektów "czystego JS", przy użyciu dowolnej składni i mechanizmu odpowiadającego Twoim potrzebom. Obiekty te mogą następnie mieć właściwość, która wskazuje na ich element DOM na te nieliczne okazje, w których jest to potrzebne. Backbone (ponownie) sugeruje, że dobrą praktyką jest również buforowanie wybranego elementu DOM jQuery. Przetwarzanie tych obiektów "czystego JS" będzie szybsze i łatwiejsze do utrzymania niż przejście przez $ .data(). Zwiększa również abstrakcję między twoim JS i HTML, co jest dobre.

Ponadto uważam, że dobrym sposobem pracy NIE jest zagnieżdżanie obiektów, nawet jeśli istnieje tylko jeden związek jeden do wielu. Nie twórz właściwości "items" w "grupach". Zamiast tego zachowuj tablice obu grup i elementów i zarządzaj nimi przez odniesienie zamiast członkostwa.

Powiązane problemy