1

Can someone explain why this occurs in Javascript?

var singleton = window.singleton || {};
singleton.methods = (function () {
    var _private = function() {
        console.log('outer private function');
    }

    return {
        _private: function() {
            console.log('inner private');
        },

        public: function() {
            _private();
        }
    }
})();

singleton.methods.public();

My intuition leads me to believe that calling .public() should log 'inner private'. It doesn't. If I change the public() definition to this._private() I get what I would expect. Why?

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
Clev3r
  • 1,568
  • 1
  • 15
  • 28

4 Answers4

2

You have a scope problem : _private in your public function refers to the one defined in the constructing closure (a closure is defined by a function call, the block you return doesn't define a scope).

If you want to log the _private method of the object you return, change the definition to

return {
    _private: function() {
        console.log('inner private');
    },

    public: function() {
        this._private();
    }
}

Here's what the MDN says about closures :

In short, variables from the parent function of the closure remain bound from the parent's scope.

To resolve _private in the public function, you have to search in order

  1. in the public function itself
  2. in the immediately outer scope, that is the closure you use to build the methods object. The function is found there
  3. if it weren't found the outer scope (which might be the global one) would be searched in turn
Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • I said exactly that at the bottom of my question. WHY does this happen? Where are the closures bound, to which function scopes do they belong? – Clev3r Jan 10 '14 at 17:53
  • @Clever So I'm not sure I get what you don't understand. Is it clearer with my edit ? – Denys Séguret Jan 10 '14 at 17:55
  • I think so. So the returned object doesn't create a new scope, and the `this._private()` on that object is referring to an entirely separate (object) parameter, not a parameter bound to the scope of the function call? – Clev3r Jan 10 '14 at 17:57
  • Yes, an object doesn't make a scope per se. – Denys Séguret Jan 10 '14 at 17:59
  • The object doesn't make a scope, but it provides a "this" property if necessary? Where the "this" points at the object (itself). – Clev3r Jan 10 '14 at 18:03
  • See my answer, or @Guffa's answer, for an alternative way of explaining this. – David P. Caldwell Jan 10 '14 at 18:03
  • Aha, thanks so much. The "search order" is exactly what I was looking for. – Clev3r Jan 10 '14 at 18:06
  • Just remember, the properties of an object are *never* in the "search order" in JavaScript (except that the properties of the *global* object -- `window` in the browser -- are always in the "search order.") – David P. Caldwell Jan 10 '14 at 18:12
0

My guess is that you're coming from a language like Java where this. is implied inside a method of an object when referring to an unqualified member name. In JavaScript, it's not, and that's the root of your problem.

The only exception in JavaScript is that the top-level scope -- window in the browser -- is treated specially. If I declare var foo = "bar" at the top level, it creates a property of window called foo with the value "bar" and hence will appear to behave the way you're expecting:

this.foo = "bar";
alert(foo); // shows bar
alert(this.foo); // also shows "bar"

But in general, outside the top-level scope, this is not true. So your function named public is looking for a scope variable called _private, and the property of the same object called _private -- this._private -- is not a scope variable in that or any other scope.

David P. Caldwell
  • 3,394
  • 1
  • 19
  • 32
0

The function that logs inner private is a property in an object. The property can't be reached by only using its name, you have to specify that it's in the object.

In object oriented languages like C# and Java the scope of a method contains the identifiers from the object, but it doesn't work like that in Javascript. It only has function scope (and global scope), so you can't access object members without specifying which object they belong to.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
0

What you have is a closure. When you return a function in a closure, that function(s) becomes exposed, hence "public". Every function inside the closure preserve its true private scope. So when you call _private(); from public function, you are actually calling the inner function. Your public _private() belongs to the constructor, to call that you should apply a proper chain to it. Like so:

this._private()
Alex Shilman
  • 1,547
  • 1
  • 11
  • 15