С ванильным JavaScript, как я могу получить доступ к данным, хранящимся методом jQuery.data()?
И прежде чем кто-то скажет:
document.querySelector('.myElem').getAttribute('data-*')
Нет, это не то, что я ищу. jQuery data()
имеет двойственные функции. 1) Он запрашивает атрибут данных HTML5, который можно увидеть в коде HTML следующим образом:
<div data-role="page" data-last-value="43" data-hidden="true" data-options='{"name":"John"}'></div>
$( "div" ).data( "role" ) === "page";
$( "div" ).data( "lastValue" ) === 43;
$( "div" ).data( "hidden" ) === true;
$( "div" ).data( "options" ).name === "John";
и 2) те, которые установлены .data(name, value)
, которые скрыты во внутреннем объекте jQuery, к которому документация только говорит "сохранять информацию под именами" события "и" обрабатывать ", но я не знаю, t выяснили способ доступа к ним или того, что на самом деле создает их.
Итак, мой вопрос, как я могу получить доступ к значениям данных jQuery из простого JavaScript?
Просто, чтобы это было абсолютно ясно... позвольте мне подчеркнуть: Я не хочу атрибуты HTML data-
, но данные объекта jQuery.
Ответы
Ответ 1
jQuery использует внутренний объект с именем $.cache
для хранения обработчиков событий и значений данных. $.cache
- простой словарь, который может выглядеть примерно так:
{ 1: { data: { role: "page"}, events: { click: ...} }, 2: { ... }}
Ключи - это все уникальные индексы, соответствующие некоторым узлам DOM. Если ваш элемент DOM был ранее затронут jQuery (вложенные события или некоторые данные), вы увидите, что он имеет странное свойство, подобное приведенному ниже:
jQuery17108624803440179676: 2
Здесь jQuery17108624803440179676
- это уникальная строка, сгенерированная jQuery, когда она была загружена на страницу. Этот уникальный идентификатор называется "expando" и сохраняется как
$.expando
Теперь, когда вы знаете, как обращаться к отдельным данным отдельных элементов, минуя API данных jQuery...
$.cache[element[$.expando]]
... вы можете спросить, почему jQuery использует промежуточный объект для хранения всех данных DOM и событий. Причина в том, что это способ избежать утечек памяти, что было бы в случае, если data
и обработчики событий были сохранены непосредственно в свойствах элементов DOM (из-за циклических ссылок).
Сказав все это, я хочу подчеркнуть, что вы должны не работать с объектами данных node, отличными от этого, через API данных jQuery по той простой причине, что он обрабатывает весь сложный материал сцены.
Ответ 2
Из комментариев ваша мотивация обходить $(...).data
, по-видимому, основана на том, что она вызывает проблемы с производительностью.
Я полностью согласен с @meagar в этой точке, $(...).data
не должен быть достаточно дорогим, чтобы вызвать узкие места. Однако, если вы постоянно повторно запрашиваете и повторно обматываете элементы DOM как элементы jQuery (например, $('#someEl').data(...)
несколько раз, а не кешировать $('#someEl')
и делать $someEl.data(...)
), это может быть проблемой.
Также обратите внимание, что если вы обнаружите, что у вас много данных для элементов DOM, вы, вероятно, ошиблись. Ваши данные не должны проживать на уровне презентации, и вам не нужно запрашивать DOM или получать ссылку на элемент DOM для доступа к вашим данным. Очевидно, вы могли бы в некоторых ситуациях, но это не должно быть нормой.
Если вы все еще хотите создать свою собственную функцию data
, то вот пример. Он не оптимизирован, но вы должны понять:
Обратите внимание, что WeakMap доступен только в современных браузерах, но без него мы будем утечки памяти, если только вы не предоставите механизм для уничтожения кэшированных атрибутов objecto-объекта при уничтожении связанного элемента DOM.
JSFIDDLE
JSPERF (тест может быть нечестным, так как я не уверен, что моя реализация делает все $(...).data
делает)
var data = (function () {
var attributes = new WeakMap();
return function (el, attr, val) {
var elAttrs = attributes.get(el),
isSetOperation = arguments.length > 2;
if (isSetOperation) {
if (!elAttrs) attributes.set(el, elAttrs = {});
elAttrs[attr] = val;
} else {
return datasetOrCachedAttrsValue();
}
function datasetOrCachedAttrsValue() {
var attrVal = el.dataset[attr];
return typeof attrVal !== 'undefined'?
attrVal:
elAttrs && elAttrs[attr];
}
};
})();
var div = document.querySelector('div');
console.log(data(div, 'test')); //test
console.log(data(div, 'notexisting')); //undefined
data(div, 'exists', true);
console.log(data(div, 'exists')); //true
"будет утечка памяти, если вы не предоставите механизм для уничтожения cached" - plalx. Даже с WeakMap он по-прежнему течет по той же причине, почему jQuery может привести к утечкам. Смотрите демонстрацию . - dfsq
Это была хорошая проблема с @dfsq, но здесь демо, которая на самом деле показывает, как WeakMap разрешает мусор когда они недоступны, в отличие от реализации jQuery, которая будет храниться на данных (если только $().remove
не используется).
Использовать распределения профилей и записи кучи во времени и сравнить результаты. Вот что я получил от 6 кликов по каждой кнопке (один тип кнопки на моментальный снимок). Мы можем ясно видеть, что $().data
протекает, а пользовательская реализация data
с использованием WeakMap
не является.