Pair program with me! profile for carousel at Stack Overflow, Q&A for professional and enthusiast programmers

2/26/2013

jQuery.fn.init constructor - this is where the fun begins

Below you can see a point of entrance into the jQuery API - fn.init constructor:

// jQuery.fn.init constructor

init:function ( selector, context, rootjQuery ) {
  var match, elem, ret, doc;

  // Handle $(""), $(null), $(undefined), $(false)
  if ( !selector ) {
   return this;
  }

  // Handle $(DOMElement)
  if ( selector.nodeType ) {
   this.context = this[0] = selector;
   this.length = 1;
   return this;
  }

  // Handle HTML strings
  if ( typeof selector === "string" ) {
   if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
    // Assume that strings that start and end with <> are HTML and skip the regex check
    match = [ null, selector, null ];

   } else {
    match = rquickExpr.exec( selector );
   }

   // Match html or make sure no context is specified for #id
   if ( match && (match[1] || !context) ) {

    // HANDLE: $(html) -> $(array)
    if ( match[1] ) {
     context = context instanceof jQuery ? context[0] : context;
     doc = ( context && context.nodeType ? context.ownerDocument || context : document );

     // scripts is true for back-compat
     selector = jQuery.parseHTML( match[1], doc, true );
     if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
      this.attr.call( selector, context, true );
     }

     return jQuery.merge( this, selector );

    // HANDLE: $(#id)
    } else {
     elem = document.getElementById( match[2] );

     // Check parentNode to catch when Blackberry 4.6 returns
     // nodes that are no longer in the document #6963
     if ( elem && elem.parentNode ) {
      // Handle the case where IE and Opera return items
      // by name instead of ID
      if ( elem.id !== match[2] ) {
       return rootjQuery.find( selector );
      }

      // Otherwise, we inject the element directly into the jQuery object
      this.length = 1;
      this[0] = elem;
     }

     this.context = document;
     this.selector = selector;
     return this;
    }

   // HANDLE: $(expr, $(...))
   } else if ( !context || context.jquery ) {
    return ( context || rootjQuery ).find( selector );

   // HANDLE: $(expr, context)
   // (which is just equivalent to: $(context).find(expr)
   } else {
    return this.constructor( context ).find( selector );
   }

  // HANDLE: $(function)
  // Shortcut for document ready
  } else if ( jQuery.isFunction( selector ) ) {
   return rootjQuery.ready( selector );
  }

  if ( selector.selector !== undefined ) {
   this.selector = selector.selector;
   this.context = selector.context;
  }

  return jQuery.makeArray( selector, this );
 }
 

 When you call $ function for example:

// call $ function with one argument ( html p element as a string ):

$("p")

You are calling a constructor that will return p element, wrapped in new jQuery object. That returned object is stored in prototype property of init constructor, so all instances will automatically inherit all properties and methods from it.

Main responsibilities of init constructor function are:
  1. to receive argument provided by API (application programming interface ) user ( usually developer )
  2. to handle response based on type of argument. - -for example handle arguments as variables (document.body ) or as strings ("div")
  3. to wrap argument into new jQuery object and return as array ( in javascript arrays are objects too )
  4. to provide subclass relationship and inheritance chain.

Last point is very confusing for many people. There is a question  why jQuery is using external ( static ) fn.init property to enable inheritance part ? If you look at code, you can notice that jQuery.prototype and jQuery.fn.init.prototype are referencinng same object. If you add some properties or methods to $.prototype object, it will be automatically added to all instances of init constructor.  So jQuery has two constructors nested. You can call main function  with keyword new, (new $("selector") or more commonly without keyword ($("selector")).

After that,  chosen element is ready for traversing, manipulation, effects and all other goodies provided by jQuery init prototype object  - which is a property of init constructor function.

It is just one part of jQuery story. Other part includes helper functions like Ajax, extend, each, data... They represent advanced part of library and they are usually called and connected with the rest of the code in jQuery implementation, but can be used as standalone methods.
Each method can be used to iterate over some custom collection ( object or array ), but it can be found as a part of lot of internal functions and methods.

Happy Coding.

No comments:

Post a Comment