user3502374
user3502374

Reputation: 779

Do not understand why return statement is needed

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

Answers (3)

user3502374
user3502374

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

Jonathan Eunice
Jonathan Eunice

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.

  1. If $(...) 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 ....
  2. If 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:

  1. $('p.main') is the original call

  2. $ function checks and finds it's not not dealing with an instance, so recurses, calling new $('p.main')

  3. $ 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.

  4. 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

Sudhir Bastakoti
Sudhir Bastakoti

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

Related Questions