Showing posts with label jquery. Show all posts
Showing posts with label jquery. Show all posts

August 12, 2011

jQuery.type

In my last post I wrote about how to detect a JavaScript array, so I thought it would be a good idea to see how jQuery does it.

Here's the jQuery.isArray() method (in 1.6.2):
isArray: Array.isArray || function( obj ) {
    return jQuery.type(obj) === "array";
}
It uses the native Array.isArray() method if it exists, if not it will find out the type of whatever is passed in using the jQuery.type() method and see if that equals the string "array".

But come on, that's not much of a learning experience not to mention it makes for a pretty short blog post.

So instead, I looked into the jQuery.type() method to see how that works.

jQuery.type()
It looks something like this:
...
// Save a reference to some core methods
toString = Object.prototype.toString
...
// [[Class]] -> type pairs
class2type = {};
...
type: function( obj ) {
    return obj == null ?
        String( obj ) :
        class2type[ toString.call(obj) ] || "object";
}
...
// Populate the class2type map
jQuery.each(
    "Boolean Number String Function Array Date RegExp Object".split(" "),
    function(i, name) {
	class2type[ "[object " + name + "]" ] = name.toLowerCase();
})
(Distant pieces of code are separated with three dots.)

Breaking it down
  • Line 3 - A reference to the toString() method of Object
  • Line 6 - An internal object (map) that will contain all types as strings.
  • Line 15-19 - For each member in the array we're passing in, we'll create a property in the object and set its value to the name of the array member in lowercase.
  • Line 8-12 - The method will check if the argument passed in is null or undefined, if so, it will return the string "null" or "undefined" (by calling the String constructor without the new keyword, which is allowed for some constructors). If it's something else, we'll call the toString() method on the argument and look to see if the class2type object has a value for it, if not, we'll just return the string "object".

To give you a better overall picture, here's how the class2type object would look after page load:
var class2type = {
    "[object Array]": "array",
    "[object Boolean]": "boolean",
    "[object Date]": "date",
    "[object Function]": "function",
    "[object Number]": "number",
    "[object Object]": "object",
    "[object RegExp]": "regexp",
    "[object String]": "string"
};

Epiphany
That's why we call the toString() method of Object when we're looking for the property name. Remember how this method returns this sort of "[object Constructor]" string pattern?

So, if we did jQuery.type("hello") we'd actually be doing something like class2type["[object String]"], which would return the corresponding value "string"

And that's how the jQuery.type() method works.

It's not that complicated when you have an "Epiphany" section, huh?

Sources:
jQuery.type() - jQuery API Documentation
jQuery 1.6.2 Source Code

April 28, 2011

jQuery.trim

Do you want to know how the trim method works in jQuery? Today is lucky day.

Here's the code from the jQuery 1.5.2 source, I separated distant lines of code with three dots:
...
// Check if a string has a non-whitespace character in it
rnotwhite = /\S/,

// Used for trimming whitespace
trimLeft = /^\s+/,
trimRight = /\s+$/,
...
// Save a reference to some core methods
...
trim = String.prototype.trim,
...
// Use native String.trim function wherever possible
trim: trim ?
    function( text ) {
        return text == null ?
            "" :
            trim.call( text );
    } :

    // Otherwise use our own trimming functionality
    function( text ) {
        return text == null ?
            "" :
            text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
    },
...
// IE doesn't match non-breaking spaces with \s
if ( rnotwhite.test( "\xA0" ) ) {
    trimLeft = /^[\s\xA0]+/;
    trimRight = /[\s\xA0]+$/;
}
...

Line 14 is where the ternary action to determine how $.trim will behave begins, all the way through line 26. Another way to look at it is this:
trim: trim ?
    function( text ) {
        // native trimming
    } :
    function( text ) {
        // jQuery trimming
    },

You might find trim: trim hard to understand, but not when you look at the trim variable in line 11. Notice how jQuery stores an internal reference to the native trim method of strings. So, if we have native support for trimming, i.e. checking for truthy value in the trim variable, we go ahead and do it that way but not before checking for null or undefined.

If the browser doesn't have support, we'll check if the passed string is not null or undefined, if it is, we'll return a blank string (just like in the first case), otherwise we'll trim it. Let's see how.

We're calling the toString method on the passed string to, and this is a total guess, make sure we have a string to work with before attempting any operation.

Then we'll trim the left side of the string and after that the right side. The regular expressions for those (in lines 6 & 7) match, pretty much, every type of space you wouldn't want in your string. So, we make sure that the passed string doesn't have those, and if it does, we get rid of them by replacing them with a blank string.

We're just missing lines 2-3 and 28-32. rnotwhite matches a single character other than white space and lines 28-32 check if we should be checking for non-breaking spaces (A0 in hexadecimal) in our trimming, mostly because of IE like the comment says. If so, we need to redefine our trimLeft and trimRight to include matching the non-breaking space character.

Hopefully I'm right about some of this but if not, let me know in the comments.

Thanks for reading.

Sources:
jQuery 1.5.2 source code
jQuery API - jQuery.trim()

April 21, 2011

jQuery.parseJSON

The other day I was re-watching (haha, learning through repetition) "11 More Things I Learned from the jQuery Source" and this time I was quite amazed when Paul Irish talked about parseJSON. And the reason was the way jQuery makes the JSON object, it's clever, it's very clever.

Let me show you. Here's jQuery's 1.5.2 parseJSON method:
parseJSON: function( data ) {
    if ( typeof data !== "string" || !data ) {
        return null;
    }

    // Make sure leading/trailing whitespace is removed (IE can't handle it)
    data = jQuery.trim( data );

    // Make sure the incoming data is actual JSON
    // Logic borrowed from http://json.org/json2.js
    if ( rvalidchars.test(data.replace(rvalidescape, "@")
        .replace(rvalidtokens, "]")
        .replace(rvalidbraces, "")) ) {

        // Try to use the native JSON parser first
        return window.JSON && window.JSON.parse ?
            window.JSON.parse( data ) :
            (new Function("return " + data))();

    } else {
        jQuery.error( "Invalid JSON: " + data );
    }
},

Let's dissect that:
  • Lines 2-4 are just checking you pass a string and its not empty.
  • Line 7 explains itself.
  • Lines 11-13 is a regular expression evaluation to make sure we have valid JSON.

The interesting part, in this case, is the return statement:
return window.JSON && window.JSON.parse ?
    window.JSON.parse( data ) :
    (new Function("return " + data))();

If the browser has native JSON support, use that. If not, and this is the interesting part, we'll do (new Function("return " + data))()

But what does that mean? I'm glad you asked.
  • We're calling the Function constructor, which will return a Function object (function reference) and immediately invoke it.
  • The Function constructor takes an "n" number of arguments that will become the function's parameters, being the last one the body of the function. We're passing only one parameter, meaning "return " + data will become the function's body.
  • What does the constructed function do then? Considering data is a JSON string it becomes rather obvious now. It returns a JavaScript object.
See? I told you it was clever, no need for eval() this way.

Thanks for reading and let me know your comments.

Source:
11 More Things I Learned From The jQuery Source by Paul Irish
Function - MDN