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?
4 Answers 4
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
- in the
publicfunction itself - in the immediately outer scope, that is the closure you use to build the
methodsobject. The function is found there - if it weren't found the outer scope (which might be the global one) would be searched in turn
8 Comments
this._private() on that object is referring to an entirely separate (object) parameter, not a parameter bound to the scope of the function call?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.
Comments
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.
Comments
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()