For work, I had to participate several times in interviewing candidates for the client-side position in our company, to look at their knowledge in Javascript. It's amazing that none of them really knew how jQuery works from the inside, even those who marked their knowledge of jQuery as “excellent”, alas.
JQuery has a very low entry threshold, it is often written and used about wherever it can (and even where, in general, is not needed), so some do not even look at pure Javascript. Why, they say, to know it when there is jQuery, and according to it - tons of examples and ready-made plugins? Even on Habré saw an article about drawing on Canvas, where the author connected jQuery and used it only once - in order to access the Canvas by its identifier. And did not consider it something abnormal.
Sorry, distracted. The essence of the post and the following parts of the series is to tell about how the library works from the inside and what happens in it as some methods are performed.
Sources
Sources of the project are
here . Everything is divided into several modules, is assembled (from someone even successfully) into one whole with the help of
Grunt . For the analysis of the article, I will use the code of the latest stable version (at the time of this writing, this is 1.8.3).
')
Figuratively, in this article we will consider a script that can be obtained by gluing together intro.js. core.js, [sizzle] (catch a glimpse), sizzle-jquery.js, support.js (also catch a glimpse) and outro.js.
The intro.js and outro.js scripts are needed just to wrap the library code in an anonymous function, so as not to litter the window. The function is passed to the window and undefined parameters (this parameter is not passed, which is why undefined). What for? Names of such variables do not change during the minification, and the names of the parameters of the function are compressed, and such manipulations result in a serious profit.
Initialization
First of all, when jQuery is loaded, we work on
core.js , the core of the framework. What happens at the initialization stage except for the declaration of a ton of RegExp and variables used further:
First of all, references to
jQuery
and its alias
$
are saved, in case they are already in the window. This is done in the case of the
noConflict function
call , which returns the $ object (and if passed in true, then jQuery in noConflict) back to its place, and as a result of its work, it gives us the jQuery already described in this very script. The function is useful when you plan to use your code and jQuery on a third-party resource and do not want to break anything to people.
A
local jQuery
function is created, which is a kind of “constructor” that takes a selector and context for itself. The function with which developers and work most of their time. It will be at the very end exported to
window.jQuery
and
window.$
(
Exports.js ). Further, this object will be expanded by
jQuery.fn
additional methods to its prototype (
jQuery.prototype
, it is
jQuery.fn
). The aforementioned “constructor” calls one of the methods in
jQuery.fn
-
init , about it a little bit below.
Attention magic:
jQuery.fn.init.prototype = jQuery.fn
That is why from the result of the work of this “constructor” you can always reach out to all the methods of jQuery.
Actually,
jQuery.fn
extended by basic methods, among which
jQuery.extend , with the help of which objects are expanded, including the further expansion of the functionality of jQuery itself.
A service hash
class2type
, which is necessary for the framework to operate the
type function and its derivatives (
isArray
,
isFunction
,
isNumeric
, etc.). Here you can pay attention to the
special magic - the usual
typeof
not very convenient for defining some types of variables, so this method exists in jQuery for this. Accordingly, its implementation is slightly different from the usual
typeof
.
And finally, a
rootjQuery
is created, a variable in which the result of
jQuery(document)
execution lies, exactly the elements from
init
will be searched for if the context is not set by the developer directly.
Everything seems to be relatively simple, but all this concerns only core.js. Any module does something when loading and it is better to consider them separately. We specifically mention support.js, in which immediately upon initialization a lot of tests are performed to determine the capabilities of the browser.
JQuery object
So, what is the jQuery object and why?
Usually the result of
$([- ])
is something like this:
{ 0: , 1: 2, context: length: 2, selector: ' - ' __proto__: ( , - jQuery.fn) }
It is because of the
length
property that many people for some reason are mistaken and think that this is actually an array. In fact, the length property is maintained internally by jQuery manually and is the number of returned elements, which are located in the numeric key-indexes of the object. This is done precisely in order to work with this object as with an array. The
selector
property
selector
string, if we searched for it, and in
context
, the context it was searched for (if not specified, it will be a
document
).
Remember that any jQuery function that is not intended to return any special results always returns an object whose prototype is
jQuery.fn
, so you can build fairly large chains of calls.
jQuery.fn.init
So, what happens when we do something like
$([- ])
? Read carefully? That's right, that “constructor” will be called. To be
more precise ,
new jQuery.fn.init([ ])
will return to us.
First, the function
will check whether the selector is passed to it at all and if it is not passed (or an empty string is passed, null, false, undefined) - in this case, an empty jQuery object will return to us as if we accessed it through window. $.
It will then
check if the selector is a DOM element. In this case, jQuery will return the object directly with this element. Example with
$(document.body)
:
{ 0: <body>, context: <body>, length: 1, __proto__: ... }
If the selector is a string, then relative to the context (if there is no context, then this is a document, see
rootjQuery
above) the
find method of the specified selector will be executed, that is:
$('p', document.body) -> $(document.body).find('p') $('p') -> rootjQuery.find('p')
If the selector is something like
#id
, then the usual
document.getElementById
(hello, man with Canvas from the beginning of the article) will be called to search for the element.
But if instead of a selector html-code is transmitted (this is determined by the presence of tags opening the tag at the beginning of the line and closing - at the end), jQuery will try to
parse it (
parseHTML , which we will look at
in the next section in more detail) and based on it create these elements and return the result already with them. This is what we get as a result of
$(' -- - ')
$(' -- - ')
$(' -- - ')
:
{ 0: <h1> 1: <p> length: 2 __proto__: ... }
Pay attention to the span inside the paragraph - in the results it will also be inside it, in the element with index 1.
For cases when, instead of a selector, a function arrives at the input, jQuery will call it when the document is ready for work. Here used
promise , which should be allocated a separate chapter. Many for some reason use a slightly longer counterpart -
$(document).ready( callback )
(in the comments they say that this is more readable), but in the end it turns out the same.
jQuery.find
To search the document in jQuery, the
Sizzle library is used, so the
find
, as well as the
expr
,
unique
,
text
,
isXMLDoc
and
contains
methods either directly refer to the corresponding methods from Sizzle, or represent simple wrapper methods over them. How selectors work in jQuery has been written repeatedly and you can find it all on Habré. As a result of the
find
operation, we get the same jQuery object with all the found elements.
On a separate line, I’ll decide to say that neither jQuery nor Sizzle cache the results of the
find
method. And why would they do it? Do not jerk the method often without need, if there is an opportunity to find everything in advance - find and put it in a separate variable.
If you are not satisfied with something Sizzle, but it happens, you can use something of your own (or someone else's) instead, see
sizzle-jquery.js , this is where references to methods from Sizzle are created. Do not forget in this case to throw Sizzle out of the build.
Conclusion
jQuery is growing and growing, now the library has grown to almost 10 thousand lines of code (excluding Sizzle). Nevertheless, the source is divided into several files, neatly written and even commented on in some places. Do not be afraid to look there, and even vice versa - if you feel that you do not know how what you want to use works, do not be lazy to look into the library sources. This applies not only jQuery, but in general any library.
Remember that jQuery is a library, the purpose of which is not only to facilitate the developer’s life by the laconism of the code that is obtained with its help, but also to make one interface for working in all possible browsers, including the prehistoric ones, that a certain overhead adds. That is why it is important to know what the library is doing for you. In some cases, you can do without these hundred kilobytes (remember that you still see the Edge badge on your phones) and without an overhead projector on calls and testing the capabilities of the browser. For example, when writing an extension for Chrome or Firefox with a probability of 90%, it will not bring any profit.
The article came out is not as big as I was afraid of and this is very good - it will be easier to read (I hope). In the field of professional web development, I am only about 7 years old, so as a beginner, of course, I may not know something, and know something — not quite (not at all) right or not completely. Feel free to correct me, supplement, criticize, ask.
PS As it turned out, Habré already has an article on this topic from the wonderful author
TheShock -
How jQuery works : we study the sources . I leave my article because someone has already added it to my favorites and it is unlikely that the inscription “the article has been placed in drafts” will be glad.
Content of a series of articles
- Introduction
- Parsing html
- DOM manipulations
- Attributes, properties, data