Reputation: 779
While going over javascript emulating jquery $, I cannot life of me understand why return
is needed in this scenario.
Here is the code I am looking at
$ = function(selector){
if ( !(this instanceof $) ) {
return new $(selector);
}
var elements = document.querySelectorAll(selector);
Array.prototype.push.apply(this,elements);
};
Can someone please explain to me why return new $(selector) works and not just new $(selector)? When you do a return, are you starting the whole thing over from the beginning with new keyword? I am having a hard time picturing this. Please help.
Sorry, if I didn't make this super clear. My question is what the statement 'return new $(selector) is doing different vs just new $(selector). When I put a debugger inside of this if statement and put a watcher on this
, initially this
is window and when I step through the function, I see that after running through return new $(selector), it goes back to if statement to see if this is instanceof $(since now this
is pointed to $). How did that happen is my question. Can someone please tell me when doing "return new $(selector)", where this is returning to??
Upvotes: 2
Views: 150
Reputation: 779
I think what I am so unsettling about this whole thing is lack of clear official document on this(or I am just not able to find them or I have not studied enough(clearly not).
I found a very good example from below old question and answer.
How does using a return clause before a recursive function call differ from not using one?
It has this example:
function loop(x) {
if (x >= 10)
return x;
loop(x + 1); // the recursive call
}
loop(0);
With my naked and untrained eyeys, it looks like when you recurse up, and eventually when x becomes 10, it says to return x
. Now, I am assuming, this returns to it's caller(which is loop(x + 1) ??). And since there is no return statement, entire thing returns undefined. Is this right??
Upvotes: 0
Reputation: 22473
It's a common JavaScript idiom to make sure you're dealing with an instance object and not a class object.
(JavaScript is technically a prototype-based object system, but the class/instance distinction is the most common/recognizable metaphor for describing this relationship.)
The $ = function (selector) ...
defines how a "class" of objects will behave. By rights every time you called $(...)
to instantiate a new object, you'd actually need to call new $(...)
to return a new instance of the class. $(...)
is not just a function call; in intent, it's a constructor. It defines how new instances of the class will be formed. That little instanceof
test and return new $(selector)
is automating the required new
--something that is incredibly easy to forget, indeed often idiomatic to not use. The guard condition is there so you don't have to call new
manually.
$ = function(selector) {
if ( !(this instanceof $) ) {
// if was called naked, without new, auto-new an instance
return new $(selector);
}
// by time execution arrives here, we have guaranteed that we are
// dealing with an instance of $, not $ itself (i.e. an instance not
// the class)
var elements = document.querySelectorAll(selector);
// use Array class to make this instance an array-like
// object holding the selected DOM elements
Array.prototype.push.apply(this,elements);
};
Update / Clarification The return
inside the guard condition ends the initial $(...)
call immediately, returning the value of new $(...)
. This is basically a recursive definition--albeit a trivial case of recursion that only recurses, at maximum, one time. There are two logical cases to consider.
$(...)
is called, recurse and return value of new $(...)
. The return
is necessary because JavaScript functions return undefined
by default, if no return
is used. So code must explicitly return the value of new ...
.new $(...)
is called (either directly, or through that trivial one-step recursion from the guard condition), then we're dealing with an instance. Use document.querySelectorAll
to find the appropriate nodes and Array.prototype.push
to load them into this instance. No return
is necessary in this case because of the magic inherent in a new
function call, which always returns the instance created. So all that's necessary is to load up the instance as desired. It will be automatically returned.Thus a typical usage goes like this:
$('p.main')
is the original call
$
function checks and finds it's not not dealing with an instance,
so recurses, calling new $('p.main')
$
function in the second, recursive call is now dealing with an instance.
It loads the DOM nodes into this instance, and implicitly returns using
the magic special handling of new
.
The new instance is returned to the guard condition of the original call.
It must explicitly return
the value because it was not originally called
with new, so that outer, original call does not enjoy the new
magic
autoreturn. It explicitly returns the new
instance to the original caller.
Upvotes: 3
Reputation: 100195
This is to avoid the need to do new $("yourSelector")
everytime when its needed to interact with your method. The line, this instanceof $
, checks to see if we are inside an instantiated version of the function, rather than just the function itself.
So if (this instanceof $)
is false
, we need to instantiate the function, by doing return new $(selector)
. The return is important, because we are just inside a normal function at the point. This way, no matter which way we now call the $
function, we will always get an instance back.
Upvotes: 1