У jQuery очень низкий порог вхождения, о нем часто пишут и используют всюду, где только можно (и даже там, где, в общем-то, не нужно), поэтому некоторые даже не смотрят на чистый Javascript. Зачем, мол, его знать, когда есть jQuery, а по нему — тонны примеров и готовых плагинов? Даже на Хабре видел статью про рисование на Canvas, где автор подключил jQuery и использовал его только один раз — для того, чтобы получить доступ к Canvas по его идентификатору. И не считал это чем-то ненормальным.
Извините, отвлекся. Суть поста (возможно и следующих частей, если эта сообществом будет одобрена), в том, чтобы рассказать о том, как же работает библиотека изнутри и что же в ней происходит по мере выполнения каких-то команд.
Исходники
Исходники проекта лежат вот тут. Все разбито на несколько модулей, собирается (у кого-то даже получается) в одно целое с помощью Grunt. Для разбора в статье я буду использовать код последней стабильной версии (на момент написания статьи это — 1.8.3).
Образно, в этой статье мы рассмотрим скрипт, который можно получить склейкой intro.js. core.js, [sizzle] (мельком), sizzle-jquery.js и outro.js.
Инициализация
Первым делом при загрузке jQuery у нас отрабатывается core.js, ядро фреймворка. Что же происходит на этапе инициализации кроме объявления тонны использованных далее RegExp’ов и переменных:
Первым делом сохраняются ссылки на jQuery
и его алиас $
, в случае, когда они уже есть в window. Делается это на случай вызова функции noConflict, которая возвращает объект $ (а если в noConflict передан параметром true, то и jQuery) обратно на свое место, а в результате своей работы отдает нам jQuery, описанный уже в этом самом скрипте. Функция полезна, когда Вы планируете использовать свой код и jQuery на стороннем ресурсе и не хотите ничего поломать людям.
Создается локальная функция jQuery
, которая и является своего рода «конструктором», которая принимает себе селектор и контекст. Функция, с которой разработчики и работают большую часть своего времени. Именно она будет в самом конце экспортирована в window.jQuery
и window.$
(exports.js). Далее этот объект, собственно, и будет расширяться, путем подмешивания в его прототип (jQuery.prototype
, он же — jQuery.fn
) дополнительных методов. Вышеупомянутый «конструктор», собственно, вызывает один из методов в jQuery.fn
— init, о нем чуть ниже.
Внимание, магия:
jQuery.fn.init.prototype = jQuery.fn
Именно поэтому из результата работы этого самого «конструктора» всегда можно достучаться до всех методов jQuery.
Собственно, jQuery.fn
расширяется базовыми методами, среди которых jQuery.extend, с помощью которого осуществляется расширение объектов, в том числе и дальнейшее расширение функционала самого же jQuery.
Ну и напоследок, создается rootjQuery
, переменная, в которой лежит результат выполнения jQuery(document)
, именно относительно него будут искаться элементы из init
, если контекст не задан разработчиком напрямую.
Вроде бы все и относительно просто, но все это касается только core.js. Любой модуль что-то делает при загрузке и их лучше рассматривать отдельно.
Объект jQuery
И так, что же представляет из себя объект jQuery и зачем?
Обычно результат работы $([какой-то селектор])
представляет собой примерно такой вот объект:
{ 0: Элемент, 1: Элемент2, context: Элемент length: 2, selector: ‘тот самый какой-то селектор’ __proto__: (как и писали выше, прототип у объекта - jQuery.fn) }
Именно из-за свойства length
многие почему-то заблуждаются и думают о том, что это — на самом деле массив. На самом деле свойство length поддерживается внутри jQuery вручную и является количеством возвращенных элементов, которые располагаются в нумерованных снуля ключах-индексах объекта. Делается это именно за тем, чтобы с этим объектом можно было работать как с массивом. В свойство selector
помещается строка-селектор, если мы искали по ней, а в context
— контекст, по которому искали (если не задан, то он будет — document
).
Запомните, что любая функция jQuery, которая не предназначена для возвращения каких-то специальных результатов, всегда возвращает объект, прототип которого — jQuery.fn
, благодаря чему можно строить довольно большие цепочки вызовов.
jQuery.fn.init
И так, что проиcходит, когда мы выполняем что-то вроде $([какой-нибудь селектор])
? Внимательно читали? Правильно, вызовется тот самый «конструктор». Если быть точнее — нам вернется new jQuery.fn.init([тот самый селектор])
.
Сначала в функции будет проверено, передан ли ей вообще селектор и в случае, если не передан (или передана пустая строка, null, false, undefined) — в этом случае нам вернется пустой объект jQuery, как если бы мы обратились к нему через window.$.
Затем будет проверено, является ли селектор — DOM-элементом. В этом случае jQuery вернет объект прямо с этим элементом. Пример с $(document.body)
:
{ 0: <body>, context: <body>, length: 1, __proto__: ... }
В случае, если селектор является строкой, то относительно контекста (если контекста нет, то это — document, см. о rootjQuery выше) будет выполнен метод find указанного селектора, т.е.:
$(‘p’, document.body) -> $(document.body).find(‘p’) $(‘p’) -> rootjQuery.find(‘p’)
В случае, если селектор представляет из себя что-то вроде #id
, то для поиска элемента будет вызван обычный document.getElementById
(привет, чувак с Canvas из начала статьи).
А вот если вместо селектора передается html-код (это определяется по наличию знаков открытия тега вначале строки и закрытия — в конце), jQuery попытается его распарсить (parseHTML) и на основе него создать эти элементы и результат вернуть уже с ними.
Для случаев, когда вместо селектора на вход поступает функция, jQuery вызовет ее, когда документ будет готов к работе. Тут использованы promise, которым следует выделить отдельную главу. Многие зачем-то пользуются чуть более длинным аналогом — $(document).ready( callback )
, не знаю зачем, но в итоге получается совсем то же самое.
jQuery.find
Для поиска по документу в jQuery пользуется библиотека Sizzle, так что find, а так же методы expr, unique, text, isXMLDoc и contains ссылаются на соответствующие им методы из Sizzle. Как работают селекторы в jQuery писалось уже неоднократно и на Хабре все это найти можно. В итоге работы find мы получим все тот же объект jQuery со всеми найденными элементами.
Если Вас чем-то не устраивает Sizzle, а такое бывает (привет чувакам из Badoo), вместо нее можно использовать что-то свое (или чужое), см. sizzle-jquery.js, именно там создаются ссылки на методы из Sizzle. Не забудьте в этом случае выкинуть Sizzle из билда.
Заключение
jQuery все растет и растет, уже сейчас библиотека разрослась почти до 10 тысяч строк кода (без учета Sizzle). Тем не менее исходники разбиты не несколько файлов, аккуратно написаны и даже местами прокомментированы. Не бойтесь подсматривать туда, а даже наоборот — если чувствуете, что не знаете как работает то, что Вы хотите использовать, не поленитесь посмотреть в исходники библиотеки.
Статья вышла у меня не такая большая, как я боялся и это очень хорошо — читать будет легче (надеюсь). Следующая статья так и напрашивается и я ее напишу, если она будет востребована. О чем именно в первую очередь — можете подсказать.
В области профессиональной веб-разработки я новичок, поэтому, конечно, могу чего-то не знать, а что-то знать — не совсем правильно или до конца. Не стесняйтесь меня поправлять, дополнять, критиковать и т.д.
Спрашивать — тоже не стесняйтесь, всегда рад вопросам.
ссылка на оригинал статьи http://habrahabr.ru/post/164433/
Добавить комментарий