Confused by this reference - javascript

(function($)
{
$.vari = "$.vari";
$.fn.vari = "$.fn.vari";
// $.fn is the object we add our custom functions to
$.fn.DoSomethingLocal = function()
{
return this.each(function()
{
alert(this.vari); // would output `undefined`
alert($(this).vari); // would output `$.fn.vari`
});
};
})(jQuery);
// $ is the main jQuery object, we can attach a global function to it
$.DoSomethingGlobal = function()
{
alert("Do Something Globally, where `this.vari` = " + this.vari);
};
$(document).ready(function()
{
$("div").DoSomethingLocal();
$.DoSomethingGlobal();
});
I am confused why in the $.fn.DoSomethingLocal function, $(this).vari is the string "$.fn.vari" and not "$.vari". I thought that the this reference in the each call gives a dom object, so calling $(this) returns a standard jquery object with prototype $, not $.fn.
How does this happen?

The jQuery.fn namespace is inherited by jQuery objects. So if you write $.fn.somewhat you now can access this by $().somewhat. That is why any jQuery plugin has to extend the jQuery.fn object/namespace.
The pattern jQuery.somewhat would just extend the plain object with somewhat. That is as good as window.somewhat or anyobject.somewhat.
$.fn has a prototypal inheritance, that is all the magic.
return this.each(function()
{
alert(this.vari); // would output `undefined`
alert($(this).vari); // would output `$.fn.vari`
});
In this context, this always refers to the object of invocation, that is, a jQuery object. Since all jQuery objects inherit from jQuery.fn you are getting $.fn.vari here.
note
Within the scope of $.fn, this references to a jQuery object !
Within the scope of .each(), this references to a DOM node !
Why?
Javascript's .apply() function allows you to specify the context in which you want to execute a function. That principle is used by "Resig & the boys" to "derefer" this.

when iterating through a jquery object with .each(), this referes to the element NOT wrapped in a jquery object. The method jQuery.vari() is on the jquery object, not the elements, so you have to wrap the element in a jquery object before you can call the $.vari() method.

Related

Why do we use $(this) inside a function acting on a jQuery object?

From what I understand, this inside of a function refers to the object invoking the function. So, for instance, in
myObj = { name : "Charlie", getType : function() { return typeof(this); } };
console.log(myObj.getType());
the object invoking the function getType is myObj and therefore typeof(this) is typeof(myObj).
Why, then, do I always see $(this) inside jQuery functions? For instance, in
$('.not-taller-than-50-pixels').each(function()
{
if ($(this).height() > 50) $(this).height(50);
});
doesn't $('.not-taller-than-50-pixels') return an array of jQuery objects and so inside the each the invoking function is a jQuery object and so this would refer to that jQuery object and I can simply write if (this.height() > 50) this.height(50); ???
From what I understand, this inside of a function refers to the object invoking the function.
that is correct, if the function is being called as a property of that object. However, in your case, your jQuery collection isn't invoking the function as a property of the jQuery collection, instead it's invoking the function using either .apply or .call to set its context to the element currently being iterated over.
https://github.com/jquery/jquery/blob/1.9.1/src/core.js#L628
It works similar to the way Array.prototype.forEach does. I assume that similarity is on purpose so that it seems familiar to most developers who are also familiar with native methods.
Here's an example more inline with your first snippet that WILL act the way you were expecting:
$.fn.alertHeight = function () {
alert(this.height());
}
$('body').alertHeight();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<p>Hello World!</p>
Because the function is stored on the prototype of $, and invoked as a property of a jQuery collection (which is an instance of $,) this refers to the jQuery collection, thus allowing us to use this.height() directly to get the height of the first element in the collection.
We use the $(this) because otherwise the this doesn't have a getter/setter method .height(). $() gives all selected elements access to jquery methods which plain javascript equivalents don't have. try console.log($(this), " VS ", this); remember that $() is a function.
doesn't $('.not-taller-than-50-pixels') return an array of jQuery
objects
No, $('.not-taller-than-50-pixels') does not return an array of JQuery objects. It returns a single JQuery object, which may wrap zero, one, or many DOM elements.
and so inside the each the invoking function is a jQuery
object and so this would refer to that jQuery object and I can simply
write if (this.height() > 50) this.height(50); ???
The callback function passed to .each() is invoked for each of the DOM elements wrapped by the JQuery object, and when the callback function is invoked, the DOM element is used as the context. That is, inside the callback function this will reference the DOM element, not a JQuery object.
This is stated in the JQuery documentation for the .each() function:
When called it iterates over the DOM elements that are part of the
jQuery object. Each time the callback runs, it is passed the current
loop iteration, beginning from 0. More importantly, the callback is
fired in the context of the current DOM element, so the keyword this
refers to the element.

jQuery fn Property

Using e.g. Chrome Developer Tools, we can easily inspect JavaScript / jQuery objects. I am trying to understand the following:
$.ajax // defined as 'function ...'
$.fn.ajax // undefined
$.removeClass // undefined
$.fn.removeClass // defined
Since $.removeClass is not defined, how come we can invoke e.g. $('body').removeClass('some-class')? And, this leads to an error $('body').fn.removeClass('some-class')?
You are asking about two different types of objects.
$ is the same as jQuery and is a function that has properties on it. $.ajax is one of those properties.
An actual jQuery object as in what $('body') creates is actually an object that is an instance of jQuery.fn.init not of jQuery.
So, that's the first reason that you see different methods on $ and $('body') because they are different types of objects and thus can have different types of methods.
To understand further, the methods on $ (which is a synonym for jQuery) are the methods that are added directly to the jQuery function itself. In the jQuery code, this is mostly done with jQuery.extend() right on the jQuery object. $.ajax is one of those.
The methods on the jQuery object created by the jQuery function are the methods that are assigned to jQuery.fn.init.prototype which due to some trickery by jQuery are the methods assigned to jQuery.fn. As it turns out jQuery.fn.init.prototype is set to be the same object as jQuery.fn so when anything is assigned to jQuery.fn, it automatically goes to jQuery.fn.init.prototype and anything on that prototype automatically becomes a method of a jQuery.fn.init object which is what is created by the jQuery function such as jQuery('body') or $('body').
You can see this in action in the jQuery code. If you look at the jQuery function, it looks like this (it creates an object of jQuery.fn.init and thus will have the methods from jQuery.fn.init.prototype:
// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
And, then later the .fn is like this:
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
Which is how any method assigned to jQuery.fn is also on the jQuery.fn.init.prototype and becomes a method on a jQuery object.
When you do something like var el = $("#content");, you're dealing with a few different types of objects:
Strings. "#content". 'Nuff said.
Functions. $ (here, a synonym for jQuery) is a function. You're calling it with the string "#content", as we established above.
jQuery objects. The jQuery function creates jQuery objects. el is one of those objects.
The jQuery function and its direct attached properties are mostly things that don't apply to any particular element; they're things you might want to do on their own. Take, for example, jQuery.ajax; that doesn't apply to an element, so it was put directly on the jQuery function.
Other functionality only makes sense when you've got the context of what elements to apply the operation to. Say, for example, removing a class. If I say jQuery.removeClass("selected");, what do I want to remove the class from? You never specified, so you can't do that. Instead, assuming we assigned el as above, el.removeClass("selected"); does indeed make some sense; it removes a class from whatever elements el represents (here, the element with an ID of content).
Whenever we have one of these function names after the dot, it's a property of something before the dot (either directly or indirectly). In the case of functions like ajax where no elements are involved, it's put directly on $/jQuery. In the case of methods like removeClass, it's put on all jQuery objects.
Of course, if you ever want to add a new method that can be used on a set of elements like removeClass, it would be rather tedious to add that property to every single one, let alone the issue of getting a reference to every jQuery object, past, present, and future! For that reason, jQuery.fn is an object that acts as the prototype of all jQuery objects. That means that whenever you create a new jQuery object, it will act minimally like the prototype it was based on, jQuery.fn. If you add a property to jQuery.fn, it will appear on all jQuery objects. In fact, the concept of a prototype is deeply embedded into JavaScript, and modifying jQuery.fn will affect all jQuery objects, whether they're newly created, created in the past, or created in the future.
I'm very new to jQuery, but my understanding is that $.fn is a property that contains all methods that can be invoked on jQuery objects.
That's why when you write a plugin, you extend $.fn by adding your own function definitions.
When you declare a function in JavaScript, it is an object:
function foo() {...}
foo.bar = 'baz'; //set the `bar` property on `foo`
...as well as being a constructor:
var f = new foo();
f instanceof foo; //`true`
f.bar; //undefined
When you construct an object from a function, the instance will inherit properties from the prototype of its constructor function:
function Bar() {...}
Bar.prototype = {
baz: function () {
console.log('baz');
}
}
var b = new Bar();
b.baz(); //logs 'baz'
Bar.baz(); //error
Additionally, objects in JavaScript are passed by reference*.
var fizz,
buzz;
fizz = {};
buzz = fizz;
buzz.foo = 'bar';
console.log(fizz.foo); //logs 'bar'
the jQuery factory function (jQuery or $) is essentially** defined in the following way:
function jQuery(...args...) {
//the map returned is a jQuery init object
return new jQuery.init(...args...);
}
//jQuery.init is a function used as a constructor
jQuery.init = function () {...do stuff...};
//jQuery.init.prototype is an object containing the methods that can be called on
//jQuery.init objects
jQuery.init.prototype = {
addClass: function () {...},
on: function () {...},
removeClass: function () {...}
};
//The jQuery.init.prototype object is exposed via jQuery.fn
jQuery.fn = jQuery.init.prototype;
//functions available on the jQuery namespace are added to the jQuery function object.
jQuery.ajax = function () {};
jQuery.extend = function () {};
* sorta
** gross oversimiplification

How does using 'this' in a simple jquery function work?

I am having trouble understanding this code:
$.functionone = function(){
function setOptions(newOptions){
...
}
this.setOptions = setOptions;
}
what does adding 'this' in 'this.setOptions' for? I understand that its referencing the function setOptions, but does adding the 'this' there make the function get called? I know this refers to the DOM element, but whats the point of having it in this particular scenario. Thanks.
That will simply expose the function from the scope of functionone, to be a property of the $ object.
For example:
$.functionone = function(){
function setOptions(newOptions){
//...
}
this.setOptions = setOptions;
};
typeof $.setOptions; // "undefined", it doesn't exist
$.functionone();
typeof $.setOptions; // "function"
The this value on JavaScript is set implicitly when you make a function call.
If the function is bound as a property of an object (like $.functionone), the this value will refer to the base object ($ in your example).
That's not so useful IMO, it's equivalent to:
$.functionone = function(){
this.setOptions = function (newOptions) {
//...
};
};
Which is at the end, when you invoke functionone, equivalent to:
$.setOptions = function (newOptions) {
//..
};
The difference is that the function is not named, which can be useful for debugging.
Working with the this value on jQuery plugins is more usual when you extend the jQuery.fn object, in that case the this value refers to the jQuery object that contains the matched elements, and not to the jQuery constructor itself.
The code creates a function and adds it as a "setOptions" property of the DOM element.
Presumably, some other part of the code will know to look for a "setOptions" function on the DOM element and execute it.
this refers to the particular DOM element that called the function.
The following line:
this.setOptions = setOptions;
Means the function "setOptions" is assigned to the "setOptions" property of the DOM Element.

Why does jQuery do this in its constructor function implementation?

If we look at the latest jQuery source at http://code.jquery.com/jquery-latest.js we see the following:
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
}
My understanding of the new keyword in Javascript is essentially JavaScript passes the function an empty object {} and the function sets stuff on it via this.blah.
Also from my understanding new differs from .call/.apply etc.. in that the return object also has the prototype set to that of the function. So the return value should have a prototype that the same as jQuery.prototype.init.prototype (or jQuery.fn.init.prototype). However from what I see its prototype is set to jQuery.prototype thus all the commands available to work on the set.
Why is this? What am I missing in my understanding?
If you look deeper into jQuery's code, you'll notice this line:
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
This is for readability/structure purposes so the constructor can have its own method.
There's no real "magic" being done here, just standard JavaScript, albeit in a slightly less commonly used way, perhaps. It's useful in jQuery's case since the library is pretty lengthy and this adds good structure/readability to it.
In that source file, search for the string "Give the init function the jQuery prototype for later instantiation" :-)
The code sets the prototype reference of jQuery.fn.init to jQuery.prototype (which is the same as jQuery.fn I think).

Why jQuery do this: jQuery.fn.init.prototype = jQuery.fn?

Little extended question is why jQuery do
jQuery.fn = jQuery.prototype = {
init: function() {...},
f1: function() {...},
...
};
jQuery.fn.init.prototype = jQuery.fn;
Why not simply add f1() etc into init.prototype? Is it only aesthetic or there are some deep ideas?
The function jQuery.fn.init is the one that is executed when you call jQuery(".some-selector") or $(".some-selector"). You can see this in this snippet from jquery.js:
jQuery = window.jQuery = window.$ = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
}
So, in fact, the line you mention is critical to how jQuery allows the addition of functionality to jQuery objects, both inside jQuery itself and from plugins. This is the line:
jQuery.fn.init.prototype = jQuery.fn;
By assigning jQuery.fn as the prototype of this function (and because the first snippet uses 'new' to treat jQuery.fn.init as a constructor), this means the functionality added via jQuery.fn.whatever is immediately available on the objects returned by all jQuery calls.
So for example, a simple jQuery plugin might be created and used like this:
jQuery.fn.foo = function () { alert("foo!"); };
jQuery(".some-selector").foo();
When you declare 'jQuery.fn.foo' on the first line what you're actually doing is adding that function to the prototype of all jQuery objects created with the jQuery function like the one on the second line. This allows you to simple call 'foo()' on the results of the jQuery function and invoke your plugin functions.
In short, writing jQuery plugins would be more verbose and subject to future breakage if the implementation details changed if this line didn't exist in jQuery.
The jQuery.fn is just an alias for jQuery.prototype. I suppose it is defined for aesthetic and less typing reasons.
So
jQuery.fn.init.prototype = jQuery.fn;
is actually
jQuery.prototype.init.prototype = jQuery.prototype;
As why this needs to be done, this forum post is helpful:
It gives the init() function the same
prototype as the jQuery object. So
when you call init() as a constructor
in the "return new jQuery.fn.init(
selector, context );" statement, it
uses that prototype for the object it
constructs. This lets init()
substitute for the jQuery constructor
itself.
What you achieve is that the object returned from a jQuery.fn.init constructor has access to jQuery methods.

Categories

Resources