One of the most mysterious things around jQuery is the expression
$(this)
especially in context of the jQuery loops
$.each()
$(cssSelector).each()
I had a closer look at that, and wrote some test code for this Blog.
jQuery is a function
jQuery is a normal JS function. It has $ as alternate name. So calling
$(".myCssClass")
is exactly the same as calling
jQuery(".myCssClass")
jQuery accepts up to two parameters, see API description.
Mind that you can pass a lot of different things as first parameter!
this
is a JS constant
The JavaScript "this" keyword represents a constant pointing to the caller of the currently executed function. That means when you have an object "bar" with a function "foo" ...
var bar = { foo: function() { return this === bar; } }; alert(bar.foo());
... you would see true
on the alert-dialog, because the object bar
is the caller of foo()
.
Some also say that "this" is what is left of the dot.
When you have nothing on the left side of the dot, it will be the window
object,
as window it is the default-context for JS interpreters running in a browser.
Mind that "this" is JavaScript, not jQuery. So the expression
$(this)
is a call to jQuery with the JS this
pointer as parameter!
jQuery each()
The each()
loop requires a function as parameter.
jQuery ensures that you can access the looped item by the this
keyword within that function.
That loop-body function can have no parameter, one parameter, or two parameters.
If you decide to have no parameter, you still can access the looped item by this
.
Declaring one parameter will let you receive the index of the item within the loop-body.
The second parameter will be the item itself.
(Only God and the jQuery programmers know why the item is the second and not the first parameter.)
$(".someCssClass").each(function(index, item) { var bareThis = this; var jQueryThis = $(this); isThisAllTheSame(bareThis, jQueryThis, item); });
In this source code, jQuery would read all DOM-elements from current document that have an attribute
class = "someCssClass"
. Then it would call the anonymous function that was passed to each()
with each of those DOM-elements as second parameter.
Now the interesting question is what's the difference between the three opportunities to access the loop item:
- the bare JS
this
, - the jQuery
$(this)
, - and the parameter
item
.
Test Page
Here is a HTML test page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>jQuery each() test</title> <script src="https://code.jquery.com/jquery-2.2.4.js" integrity="sha256-iT6Q9iMJYuQiMWNd9lDyBUStIq/8PuOW33aOqmvFpqI=" crossorigin="anonymous"></script> </head> <body> <div id="one" class="loop">One</div> <div id="two" class="loop">Two</div> <hr> <div id="output"></div> <script type="text/javascript"> var outputElement = function(bareThis, jqueryThis, element) { // ... }; var loopBody = function(index, element) { var bareThis = this; var jqueryThis = $(this); outputElement(bareThis, jqueryThis, element); }; $(".loop").each(loopBody); </script> </body> </html> |
Copy & paste this into some file and load that file into your browser.
Currently nothing will happen, because the outputElement
function is not yet implemented.
var outputElement = function(bareThis, jqueryThis, element) { var output = $("#output"); var outputText = "<b>id = "+jqueryThis.attr("id")+"</b><br>"; outputText += "bareThis = "+bareThis+", element = "+element+", jqueryThis = "+jqueryThis+"<br>"; outputText += "typeof bareThis = "+(typeof bareThis)+", typeof element = "+(typeof element)+", typeof jqueryThis = "+(typeof jqueryThis)+"<br>"; outputText += "bareThis == element: "+(bareThis == element)+", jqueryThis == element: "+(jqueryThis == element)+"<br>"; outputText += "bareThis === element: "+(bareThis === element)+", jqueryThis === element: "+(jqueryThis === element); output.html(output.html()+"<br><br>"+outputText); };
Looping DOM Elements
Having the output-function added, this is what we see now for the first element with id "one":
id = one bareThis = [object HTMLDivElement], element = [object HTMLDivElement], jqueryThis = [object Object] typeof bareThis = object, typeof element = object, typeof jqueryThis = object bareThis == element: true, jqueryThis == element: false bareThis === element: true, jqueryThis === element: false
It is never easy to get clearness about JS objects, so that output is somehow complicated.
To summarize it, we see that the bareThis
and element
parameter are identical,
it is the DOM-node, and the jQueryThis
is the usual jQuery wrapper around such a DOM-node.
What we learn here is that this
points to the item of the loop, not to the caller of the function.
How can this be?
There is a built-in JS function
Function.call(context, parameters)
that lets set the this
pointer for the called function as first parameter.
jQuery uses exactly that to call the loop-body function.
So the this
keyword does not really have a constant semantic, it is more a read-only variable
that can point to any object the caller considered to be useful.
Looping a String Array
Here is JS code that loops over a String array.
var array = [ "#one", "#two" ]; $(array).each(loopBody);
Resulting output for the first element is:
id = undefined bareThis = #one, element = #one, jqueryThis = [object Object] typeof bareThis = object, typeof element = string, typeof jqueryThis = object bareThis == element: true, jqueryThis == element: false bareThis === element: false, jqueryThis === element: false
The bareThis
and element
parameters are equal, but not identical.
The bareThis
is a String-object that was built by $(array),
while element
is the real String from the array.
Amazingly I could not trick jQuery to read the DOM-node with id "one":
$("#one")
!
What we learn is that the this
pointer is not what we should use when we really want to work
with the array item, because here it was built by jQuery that deep-cloned the array.
But as long as identity === is not required, this
will be sufficient.
jQuery Source Code
To understand parts of this Blog better, here is the original code of jQuery each()
,
which is executed by both variants of that function.
var each = function( obj, callback ) { var length, i = 0; if ( isArrayLike( obj ) ) { length = obj.length; for ( ; i < length; i++ ) { if ( callback.call( obj[i], i, obj[i] ) === false ) { break; } } } else { for ( i in obj ) { if ( callback.call( obj[i], i, obj[i] ) === false ) { break; } } } return obj; };
Keine Kommentare:
Kommentar veröffentlichen