2

I am working on underscore.js and I got stuck in the very first function.

// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };

// Test Examples
var obj = {
    name : 'Francis',
    gender: 'male'
}
var test = _(obj);
console.log(test); // _ {_wrapped: Object}
                   // Why statement this._wrapped = obj; still got executed?

var Man = function() {
    this.age = 30;
    this.people = "John";
    return this;
    this.man = "me";
}
var me = new Man();
console.log(me) // Man {age: 30, people: "John"}
                // There is no "man" property because function Man returns
                // before executing to the last statement.

I am trying to figure out what _ (underscore) does in here: I think it serves as a constructor and returns an instance with all the functions defined in underscore.js. So it'll be more convenient to invoke the functions with simple syntax.

Fox example:

var anotherList = [1, 2];
_.each(anotherList, alert); // alert "1", "2"

var anotherObj = {"name": "H"};
var anotherObj = _(anotherObj);
anotherObj.each(alert); // alert "H"

But what's the mechanism behind all this?

How this._wrapped = obj; works?

Sigi
  • 1,784
  • 13
  • 19
francisfeng
  • 704
  • 1
  • 8
  • 22
  • The body of your question is entirely different from the title. Which question do you want answered? Oh, `_` is just a valid name for an identifier. – Mr Lister Jun 09 '15 at 11:04
  • Actually it's sort of like this question: http://stackoverflow.com/questions/16378628/what-is-the-underscore-function-for . But after reading a lot of related questions, I still can't figure out how it works. – francisfeng Jun 09 '15 at 11:33

2 Answers2

6

The function at the top acts "sort of like" a constructor for something that is "sort of like" a class in JavaScript (a very loose description: search prototype, class and javascript for more accuracy).

In this case, the "class" has one member (._wrapped) which is set to the object specified (obj). (Elsewhere, in underscore.js, all the functions of the "class" will be added using .prototype).

The two tests at the beginning are to prevent incorrect use:

if (obj instanceof _) return obj;

checks that you are not passing an instance of the _ class into the constructor (if you do, it just returns that instance). This will stop:

var a = {...};
var b = new _(a);
var c = new _(b);

creating a doubly-wrapped object (c will be the same as b).

The second test:

if (!(this instanceof _)) return new _(obj);

checks that you've correctly used new when creating an instance (or, conversely, allows you not to have to remember to use new all the time).

When called as in your code:

var test = _(obj);

the value of this inside the function that is _ will be the global scope, and hence not an instance of the _ class. In this case, it will use new for you to create a new instance of the _ class and return it. (If it didn't do this, and you didn't use new, then you wouldn't get a new object and would end up polluting the global object).

If it is called as in my example above (using new), then this inside the function will be the newly-created _ object. This will be an instanceof _ and so will proceed to the main part of the constructor:

this._wrapped = obj;

which saves a copy of the object being wrapped in the new instance. Thus:

var a = {...};
var b = _(a);
var c = new _(a);

will create both b and c as new instances of the _ class. Whether you should use the new, or rely on constructors to do it for you is a question for another time.

TripeHound
  • 2,721
  • 23
  • 37
4

The _ function is recursive. You call it, then it calls itself a second time. Let's take a look.

var _ = function(obj) {
    if (obj instanceof _) return obj;
    if (!(this instanceof _)) return new _(obj);
    this._wrapped = obj;
  };

When you do _(obj) with obj={...}, your object fulfills the second if condition -- this is not an instance of _, as it was not called as a constructor (this is the global object). Then, it returns new _(obj), calling _ as a constructor. This second time around, this is indeed an instance of _, so neither condition is fulfilled. Now, it reaches the final line. Being a constructor, it implicitly returns this to the first time you called it.

You now have a _ object.

Scimonster
  • 32,893
  • 9
  • 77
  • 89
  • It explains a lot! But how does `test.each(alert)` work? In underscore.js source code, `_.each(obj, iteratee, [context])` takes at least two arguments, while here we just pass one `iteratee` argument to it and it works anyway. – francisfeng Jun 09 '15 at 11:56
  • Because you have a wrapped underscore object, which gets all those properties set on it. – Scimonster Jun 09 '15 at 11:57