📜 ⬆️ ⬇️

How jQuery works: studying sources


jQuery has definitely become the standard in the web dev industry . There are many great js frameworks that deserve attention, but jQuery impressed everyone with its lightness, elegance, and magic. People write using jQuery, people write plug-ins for jQuery, people even write articles about jQuery, but few people know (especially newbies) how JQuery works.

In this article we will conduct a small digression into the insides of this framework and analyze what is inside.
The article is designed for basic knowledge of Javascript. Think about it and if you know how to write a jQuery clone, then most likely you will not find anything new here. The rest - welcome under the cat


General information


jQuery is a Javascript library.
')

The official website is jquery.com , by John Resig, aka jeresig , a famous guru and former Javascript evangelist at Mozilla Corporation . He has his own blog - ejohn.org , where he wrote a bunch of cool articles and lib for working with Canvas - processing.js , as well as the book JavaScript. Professional programming techniques. Located in the RIT Hall of Fame

The main jQuery repository is located on GitHub , where source codes, unit tests, a collector, js-lint checker, etc. are located.

At this point, I would like to make a retreat and pay attention to GitHub. A huge number of OpenSource Javascript lib - prototype.js , MooTools , node.js , jQuery , Raphael , LibCanvas , YUI and also a large part of the Javascript (and not only Javascript) communities found shelter there, because if you want to lay out your javascript project, GitHub is the best place.

In the / src directory are the source, divided into many files. If you looked at the code.jquery.com/jquery-*.js file and were horrified how to avoid confusion, then you should know that everything is structured and not so awful. Gather by means of the builder on node.js. In it, the source code "@VERSION" and "@DATE" lines are replaced with the corresponding values.

Dive into the source


Coding styles are very familiar and ordinary. I will delight or grieve you. Tabs and Egyptian brackets are used. Only # indentation are repulsed, alignment is not used anywhere.

There are two files - intro.js and outro.js , which are placed at the beginning and end of the compiled source, respectively.
(function( window, undefined ) { var document = window.document, navigator = window.navigator, location = window.location; [...] //    window.jQuery = window.$ = jQuery; })(window); 


Core


The main interest for us is the core.js file, in which all the “meat” is located.

The source looks like this. We see that the code has dropped another nesting level, which makes it easier to control the scope of variables:
 var jQuery = (function () { var jQuery = function ( selector, context ) { return new jQuery.fn.init( selector, context, rootjQuery ); }; // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, // A central reference to the root jQuery(document) rootjQuery, [...] rootjQuery = jQuery(document); [...] return jQuery; })(); 


In the copied section, you can see the jQuery object's constructor, the current jQuery and $ saved values ​​(you will need to continue to implement jQuery.noConflict() ) as well as some rootjQuery - a jQuery object with a link to the document (cache of the frequently encountered $(document) , optimization)

Just below are the RegExp series, which are necessary for implementing jQuery.browser, jQuery.trim, parsing json, etc. Modern browsers support the methods ''.trim and [].indexOf , because jQuery retains references to them and uses native implementations in its jQuery.trim and jQuery.inArray .

 trim = String.prototype.trim, indexOf = Array.prototype.indexOf, 


Object construction


We select jQuery “holy-holy” - $ -functions. This part is the hardest piece for an unaccustomed person, so we approach it with a fresh head;) The magic of jQuery prototypes is hidden here, I will not go into details of why it works this way , I will tell only HOW it works.

We have already seen the above jQuery constructor code:

 var jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context, rootjQuery ); }, 


That is, when the jQuery function is called, the entity " jQuery.fn.init " is created and returned. This place uses Javascript magic. Just below the code we can find something like the following:

 jQuery.fn = jQuery.prototype = { constructor: jQuery, init: function( selector, context, rootjQuery ) { [...] } [...] } // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; 


From now on, we know that jQuery.fn is nothing more than a jQuery prototype and this knowledge will help us to figure out something lower. Also, jQuery.fn.init.prototype points to the jQuery prototype, and the constructor jQuery.fn.init.prototype points to jQuery . This approach gives us a very interesting result. Open the jQuery Chrome console and enter:
 $(document) instanceof jQuery; // true $(document) instanceof jQuery.fn.init; // true 


So that you understand the essence of this behavior, I will give you another example:
 var Init = function () { console.log('[Init]'); }; var jQuery = function () { console.log('[jQuery]'); return new Init(); }; Init.prototype = jQuery.prototype = { constructor: jQuery }; var $elem = jQuery(); // [jQuery] , [Init] console.log( $elem instanceof jQuery ); // true console.log( $elem instanceof Init ); // true 


Thus, all design is in the jQuery.fn.init object jQuery.fn.init , and jQuery is the jQuery.fn.init object jQuery.fn.init

Parsim arguments


There are a bunch of options for using the jQuery function:
 $(function () { alert('READY!') }); // ,      DOM $(document.getElementById('test')); //    $('<div />'); //    $('<div />', { title: 'test' }); //      //       css-: $('#element'); //    "element" $('.element', $previous ); //      element  $previous $("div[name=city]:visible:has(p)"); //  ,     

For a detailed description of selectors, read the article by AntonShevchuk " jQuery for beginners. Part 4. Selectors "

We climb into the constructor, which, as we already know jQuery.fn.init . I will give here pseudocode :
 init: function( selector, context, rootjQuery ) { if ( !selector ) return this; // Handle $(DOMElement) if ( selector.nodeType ) return this = selector; // The body element only exists once, optimize finding it if ( selector === "body" && !context ) return this = document.body; if ( jQuery.isFunction( selector ) ) { return rootjQuery.ready( selector ); } // Handle HTML strings if ( typeof selector === "string" ) { // Verify a match, and that no context was specified for #id if ( selector.match(quickExpr) ) { if ( match[1] ) { return createNewDomElement( match[1] ); } else { return getById( match[2] ) } } else { return jQuery( context ).find( selector ); } } }, 


The first four pieces are quite understandable - there is a processing of cases when an empty selector is passed, a DOM element as a selector, or a 'body' line — to get the document body more quickly, as well as processing a function for DomReady.

An interesting moment with the case when we pass a string. First of all, it parses it with a “fast regular expression”. In it, the left side is responsible for finding the tags in the string, and the second is for searching the element by ID:
quickExpr = /^(?: [^<]*(<[\w\W]+>)[^>]*$ | #([\w\-]*)$ )/;

And only if the request is more complex, then the find method is called from the current context, which searches for an element using the search engine (also JResig authorship) Sizzle (rights belong to The Dojo Foundation ).

Plugin development


Many Javascript professionals know that a class created with prototypes can be easily extended.

 var MyClass = function () { // constructor }; MyClass.prototype = { // prototype }; var instance = new MyClass(); //            ,    MyClass.prototype.plugin = function () { console.log("He's alive!"); }; instance.plugin(); // He's alive! 


In the same way, we can extend the standard jQuery prototype:

 jQuery.prototype.plugin = function () { // Here is my plugin }; 


But, as we noted above, fn is a short link to jQuery.prototype , so you can write shorter:

 jQuery.fn.plugin = function () { // Here is my plugin // this    jquery-,     }; 


And this plugin will appear in all already created and those that are created entities. Adding properties directly to an object we implement static properties:

 jQuery.plugin = function () { // Here is my plugin }; 


Thus, the best template for small plugins:

 new function (document, $, undefined) { var privateMethod = function () { // private method, used for plugin }; $.fn.myPlugin = function () { }; // ,   ,    dom-: $.myPlugin = function () { }; }(document, jQuery); 


This approach can be seen in most jQuery plugins, for example, DatePicker .

Conclusion


In my opinion, the reason why jQuery is popular is the external simplicity and ease, as well as the brevity of the names: css vs. setStyles , attr vs. setAttributes , etc. The idea was simply beautiful and captivated the minds of many. Very often jQuery clones are found or ideas are transferred to other languages. But simplicity is deceptive. And it is not always good, so always think three times, before shortening the clear name of your method, it may come out sideways for you;)

I hope you enjoyed this article and it did not prove too abstruse. If there is a desire, it will be possible to continue the cycle and go deeper into jQuery even deeper, or study other frameworks.

Source: https://habr.com/ru/post/118564/


All Articles