4

I always thought that jQuery $ function returns an array with jQuery methods attached to it. I want to provide some examples:

Let's say we have an array

var arr = [1,2,3];

Then we are able to add our own properties:

arr.someProp = 10;
arr.someMethod = function() { ... }

After that arr remains an array despite having custom properties:

arr instanceof Array;   //true

So, I thought jQuery object is something like arr (but more complicated) until recent experiment. I just run this code:

$('div') instanceof Array;  //false (!!!)

But it behaves like an array. It has push method, length property, that works correctly even in such case:

var $jq = $('div');
$jq.length; //3
$jq.push(123);  //wrong code, I know, this is just for test
$jq.length      //4

Also if you execute console.log($('div')), it will output something like this:

[<div></div>, <div></div>, <div></div>]

Besides jQuery object has some methods that are equal Array.prototype methods:

$('div').sort === Array.prototype.sort;     //true
$('div').splice === Array.prototype.splice; //true

My questions is: how this thing is being created?

So if you guys explain me this and provide some code samples, I would be very grateful to you.

cookie monster
  • 10,671
  • 4
  • 31
  • 45
Oleg
  • 22,300
  • 9
  • 68
  • 84
  • 1
    It's a function **and** an object **and** magic all at the same time, but never an array! – adeneo Jan 12 '14 at 16:52
  • 1
    You can always browse through the jQuery source code. It's a little twisty and confusing, but educational. – Pointy Jan 12 '14 at 17:09
  • 1
    FYI, the [ECMAScript specification](http://ecma-international.org/ecma-262/5.1/) explicitly defines most array methods to work on *array-like* objects, [for example](http://ecma-international.org/ecma-262/5.1/#sec-15.4.4.7): *"The `push` function is intentionally generic; it does not require that its `this` value be an Array object. Therefore it can be transferred to other kinds of objects for use as a method. Whether the push function can be applied successfully to a host object is implementation-dependent."* – Felix Kling Jan 12 '14 at 17:48

4 Answers4

4

So, I thought jQuery object is something like arr (but more complicated) until recent experiment. I just run this code:

$('div') instanceof Array;  //false (!!!)

But it behaves like an array. It has push method, length property, that works correctly...

Any mutable object can "behave" like an array in the sense that the built-in Array methods will work on it, as long as it has a proper .length property and the expected numeric indices.

Take this object:

var obj = {
    "3": "buz",
    "0": "foo",
    "2": "baz",
    "1": "bar",
    length: 4
}

It's not an Array, but we can put the Array.prototype.push method on the object, and it'll work just fine.

obj.push = Array.prototype.push;

obj.push("hi");

Now the obj will have a .length of 5, and will contain the "hi" string.

Community
  • 1
  • 1
cookie monster
  • 10,671
  • 4
  • 31
  • 45
  • 1
    thank you for explanation, but I still can't understand how jQuery developers make browser output this object as `[
    ,
    ,
    ]`?
    – Oleg Jan 12 '14 at 17:03
  • 2
    @Zub: There's a trick that the console developers do to determine that an object is an Array. I believe they simply test for a `splice()` property that points to a function and a `.length` property, and if they exist, they print it out using Array syntax. If you take my example and do `obj.splice = Array.prototype.splice`, you should get the same printout. – cookie monster Jan 12 '14 at 17:06
  • 1
    Wow! It seems like you are right about `splice` and `length`. I just tried it and it really outputs in square brackets! – Oleg Jan 12 '14 at 17:09
  • 1
    @Zub: Yeah, just to be clear, that doesn't make it an actual Array. I guess you already know this because of your `instanceof` test. It's just a quick hack the console developers use, and doesn't have anything to do with the language itself. – cookie monster Jan 12 '14 at 17:10
3

The jQuery object is an array-like object. It has some members that have the same purpose as the corresponding array members.

There are other array-like objects in Javascript, for example the object returned from the document.getElementsByTagName method is a HTMLCollection or a NodeList (depending on the browser), which have some members from an array, but not all.

You can create a regular object and add any members you like, and if enough of them work like the corresponding array members so that you can use it as an array in some ways, you can call it an array-like object.

Example:

var myArrayLookAlike = {
  length: 0,
  data: [],
  push: function(value){
    this.data.push(value);
    this.length = this.data.length;
  },
  pop: function(){
    var value = this.data.pop(value);
    this.length = this.data.length;
    return value;
  }
};

The jQuery library has the makeArray method that can be used to create a real array object from an array-like object, which can be used when an array-like object lacks some specific members that a method needs to use it.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
1

My questions is: how this thing is being created?

As a broader introduction, see if this helps understand, a mini jQuery in a few lines. I've been using jQuery for a long time, and it basically all boils down to a prototype with methods that return this for chaining, and a few array helpers to process collections of elements. map is your "core method", and every other method that manipulates elements, uses map in one way or the other. Then you need toArray, flatten and unique and you're set. jQuery builds on these basic concepts, but obviously more complex, but this will give you an overall idea of the inner workings of a library like jQuery, using modern JavaScript (checkout Zepto.js for example):

var $ = (function(){ // jQuery shortcut

  // Helpers to work with collections of elements

  // Converts pseudo-arrays to real arrays
  // such as elements queried with native DOM methods
  var toArray = function(x) {
    return Array.prototype.slice.call(x);
  };
  // Flattens array of arrays, one level
  var flatten = function(xs) {
    return Array.prototype.concat.apply([], xs);
  };
  // Remove duplicates
  var unique = function(xs) {
    return xs.filter(function(x, i) {
      return xs.indexOf(x) == i;
    });
  };
  // Query the DOM on the document or an element
  var query = function(sel, el) {
    return toArray((el || document).querySelectorAll(sel));
  };
  // Helper to access object properties with `map`
  var dot = function(s) {
    return function(x) {
      return x[s];
    };
  };

  // Constructor
  function jQuery(sel) {
    // Elements queried from the DOM
    // as an array
    this.el = query(sel);
    this.length = this.el.length;
  }

  // Public methods that operate on the array
  // of queried elements
  jQuery.prototype = {
    // Return array (collection of elements)
    get: function(idx) {
      return idx == null ? this.el : this.el[idx];
    },
    // Map is the core method,
    // all other methods use `map` in way or the other
    map: function(fn) {
      var f = function(x) {
        var fx = fn.call(this, x);
        // It's a pseudo-array?
        if (fx.length) return toArray(fx);
        return fx;
      };
      this.el = unique(flatten(this.el.map(f)));
      this.length = this.el.length;
      return this; // chain

    },
    parent: function() {
      return this.map(dot('parentNode'));
    },
    children: function() {
      return this.map(dot('children'));
    }
  };

  // Wrapper around construtor
  // to create new instances without `new`
  return function(sel) {
    return new jQuery(sel);
  };
}());
elclanrs
  • 92,861
  • 21
  • 134
  • 171
0

This is an example that I used to submit a form which it may help you with your question.

JS Code

 // process the form
        $('.updateClient').click(function(event) {

            // get the form data
            // there are many ways to get this data using jQuery (you can use the class or id also)
            var formData = {    
                'clientbrandid' :$("input[id=editClientBrand]").val()
            };

            // process the form
            var ajaxResponse = $.ajax({
                type: 'POST', // define the type of HTTP verb we want to use (POST for our form)
                url: 'someURL', // the url where we want to POST
                data: JSON.stringify( formData ),
                contentType :'application/json',
                error: function(data,status,error){
                            console.log(data+': '+status+': '+error);
                                            }
                    }).done(function(apiResponse) {
                            $('.someClass').append('Hello There!'); //This is what creates the <div></div> on the html side and when the function is done it appends the content between those two <div></div>
                    });

    });

HTML Side

..............Some form code on top of this.

    <div class='someClass'></div> <--- this is where the output will show after the code is done
Geo
  • 3,160
  • 6
  • 41
  • 82