Наскоро (преди около месец де) излезе Prototype 1.6.1 RC1, основната цел на тази версия е съвместимост с ново излезлият IE8, и Element.Store ( за който бях писал преди време ).
Toчно за Element.Store исках да драсна някои ред, защото изненадващо за този тип storage няма много материали в нет-а.
Основната идея на Element.Store е да може да се добавя информация (като обекти) към даден елемент, без да се страхуваме от memory leaks. Това в Prototype е реализирано със следните методи (под $(element) имам предвид референция към prototype extend element):
- $(element).getStorage() – директен достъп до Storage-а на даденият елемент, той просто представлява един Hash в който се запазват всички обекти, които искаме да прикачим към елемента. Дефакто store/retrive се явяват shortcut-и на get/set методите на този Hash.
- $(element).store(key, value) – записва информация в даденият елемент, ключът трябва да е string (или обект с метод toString), после когато искаме да си вземем информацията отново използваме ключа. Възможно е наведнъж да се запишат повече от една двойка ключ – стойност: $(element).store({ key1: ‘value1′, key2: ‘value2′ /* и така на татък */});
- $(element).retrieve(key, defaultValue) – извлича информация от елемента по даден ключ, като ако не съществува такъв ключ се връща defaultValue.
- вероятно $(element).unsetStorage() (ticket | gist) – този метод все още го няма, но е много вероятно да се появи във финалната 1.6.1 версия, той трябва да премахва цялата информация за дадения обект от Storage-a му. Ако искате в момента да ползвате unsetStorage може да вземете моята примерна версия от тук.
Как обаче това би се ползвало в реалния свят ? Първо най-вероятно във финалната версия 1.6.1 цялата event система на Prototype ще използва Element.Store. А аз ще си позволя да дам един малък пример за това как и къде се ползва Element.Store:
Имаме примерно масив с футболисти на Арсенал, и на страницата имаме бутони, чийто id-та се образуват от ‘button_’ + номера на футболиста. Целта е при натискане на бутон за даден футболист да се показва името му с alert.
var footballers = [
{number: 14, firstName: 'Theo', lastName: 'Walcott'},
{number: 12, firstName: 'Carlos', lastName: 'Vela'},
{number: 23, firstName: 'Andrei', lastName: 'Arshavin'},
{number: 4, firstName: 'Francesc', lastName: 'Fabregas'},
{number: 5, firstName: 'Kolo', lastName: 'Toure'},
{number: 25, firstName: 'Emmanuel', lastName: 'Adebayor'}
];
Общо взето без имаме 4 възможни варианта:
// Вариант 1: използваме closure за достъп до футболиста
footballers.each(function(f){
$('button_' + f.number).observe('click', function(){
alert('#' + f.number + ' ' + f.firstName + ' ' . f.lastName);
});
});
// Вариант 2: използване на скит closure
function show(f){
alert('#' + f.number + ' ' + f.firstName + ' ' . f.lastName);
}
footballers.each(function(f){
$('button_' + f.number).observe('click', show.curry(f));
});
// Вариант 3: използване на референция в самия обект
function show(){
var f = this.footballer;
alert('#' + f.number + ' ' + f.firstName + ' ' . f.lastName);
}
footballers.each(function(f){
var button = $('button_' + f.number);
button.footballer = f;
button.observe('click', show);
});
// Вариант 4: използване на Element.Store
function show(f){
var f = this.retrieve('footballer');
alert('#' + f.number + ' ' + f.firstName + ' ' . f.lastName);
}
footballers.each(function(f){
$('button_' + f.number).store('footballer', f).observe('click', show);
});
Всички варианти работят и правят едно и също (или поне така си мисля
), но всеки си има и своите особености. Така Вариант 1 изглежда добър вариант и също така е доста се използва именно този метод, но това което ме притеснява е че анонимната функция към бутона се пре-декларира за всеки бутон (въпреки, че това пак зависи от интерпретатора). Също така при по сложни скриптове може да се получат обърквания кое от къде идва и на къде отива, а и от самия код не е много “чист”. Във Вариант 2 се използва curry, което прави кода малко по-чист, но въпреки, че не е веднага видимо пак се използва closure ( в curry метода ) и отново има генериране на доста анонимни функции. Вариант 3 променя още структурата кода, като се маха curry, и се добавя референция към футболиста в самия обект, така не се генерират множество функции, но вероятността да се появят memory leak-ове е много голяма ( а и самия код изглежда най-зле от всички варианти ). За мен лично вариант 4 е най-удачния (особено при по-големи проекти), сега към обекта пак се записва референция към дадения футболист, но този път се използва безопасния Element.store .
Надявам се с този малък пример да съм показал част от възможностите който предоставя новия Element.Store механизъм и защо е полезен той
В заключения ще дам за пример с CD3.Select класа ми, който се използва за правенето на custom html select контроли. Като просто като се напише new CD3.Select(selectElement), автоматично selectElement-а се замества с група от div/ul/li елементи и така се дава възможност на дизайнерите ни в Pixeldepo, да ги настройват както намерят за добре (всъщност в моите ControlDepo 3 Widgets имам още цял арсенал от компоненти, с подобни функции). Та преди в CD3.Select за капсулирането на връзката между options обектите и техните производни li елементи използвах closure, като след появата на Element.Store промених нещата. При което се забелязах че доста по-добре и глатко работят самите контроли и много по-малко памет се използва
Element.Store използвам още за Event.deletege, който много се надявам да попадне в core-a на Prototype.js и за който в следващата седмица мисля да напиша един пост.
