Reputation: 85
var utils = function() {
function getMyPrivateName() {
return "Caoimhin";
}
return {
messages: {
getMyPublicName: function getMyPublicName() {
return "Kevin";
},
sayHello: function() {
document.writeln("hello " + getMyPublicName() + "<br/>");
document.writeln("hello " + getMyPrivateName() + "<br/>");
}
}
};
} ();
utils.messages.sayHello();
I am playing around with javascript namespaces and have encountered unexpected behaviour. I develop mostly in IE as that is the target browser for our intranet application.
In IE the above, when included on a blank page, outputs:
hello Kevin
hello Caoimhin
In FF the script encounters an error:
getMyPublicName is not defined
If I comment out the offending line:
//document.writeln("hello " + getMyPublicName() + "<br/>");
FF outputs:
hello Caoimhin
So I know it can access the private function...
Can anyone explain why this is happening? And what I need to do in order to have a cross browser solution similar to the above..
I know I could write something like:
document.writeln("hello " + utils.messages.getMyPublicName() + "<br/>");
but would prefer not to....
Thanks in advance, Kevin
Upvotes: 0
Views: 115
Reputation: 536489
You have stumbled across a bug in the JScript language as used by IE.
getMyPublicName: function getMyPublicName() {
...
},
The function getMyPublicName()
value here is an inline function expression that has been given an optional identifier getMyPublicName
that is the same as its property name in the owner. (sayHello
omits this identifier.)
What the optional identifier should do according to the ECMAScript standard is make a reference to the function visible in the scope of the function body itself, under the identifier name getMyPublicName
. This could be used for making an anonymous inline function that referred to itself (for recursion).
What it actually does in IE, incorrectly, is make the function visible under the name getMyPublicName
in the parent scope (the utils
function). Because it's visible in that scope it becomes visible to the child scope of the sayHello
function too, making your code work when it shouldn't, really.
You can use this.
to get the reference to getMyPublicName
properly, as suggested by Dustin. Alternatively if you want to avoid the issues of this
-binding in JavaScript (eg. because you're going to pass the sayHello
function as a delegate to a timeout or event), you might prefer to put the public functions in parent scope too:
var utils = function() {
function getMyPrivateName() {
return "Caoimhin";
}
function getMyPublicName() {
return "Kevin";
}
function sayHello() {
document.writeln("hello " + getMyPublicName() + "<br/>");
document.writeln("hello " + getMyPrivateName() + "<br/>");
}
return {
messages: {
getMyPublicName: getMyPublicName,
sayHello: sayHello,
}
}
}();
Upvotes: 5
Reputation: 1346
One way of doing this is:
var utils = function() {
var getMyPrivateName = function () {
return "Caoimhin";
};
var self = {
getMyPublicName: function () {
return "Kevin";
},
sayHello: function() {
document.writeln("hello " + getMyPublicName() + "<br/>");
document.writeln("hello " + self.getMyPrivateName() + "<br/>");
};
return self;
}();
Upvotes: 0
Reputation: 169633
Firefox' behaviour is conforming, as a function definition within an object initialiser is a function expression and not a function declaration.
ECMA-262, 3rd edition, section 13:
The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.
I suggest moving the definition out of the object initialiser:
var utils = (function() {
function getMyPrivateName() {
return "Caoimhin";
}
function getMyPublicName() {
return "Kevin";
}
return {
messages: {
getMyPublicName: getMyPublicName,
sayHello: function() {
document.writeln("hello " + getMyPublicName() + "<br/>");
document.writeln("hello " + getMyPrivateName() + "<br/>");
}
}
};
})();
utils.messages.sayHello();
Upvotes: 4
Reputation: 622
It's a matter of scope:
var utils = function() {
function getMyPrivateName() {
return "Caoimhin";
}
return {
messages: {
getMyPublicName: function() {
return "Kevin";
},
sayHello: function() {
document.writeln("hello " + this.getMyPublicName() + "<br/>");
document.writeln("hello " + getMyPrivateName() + "<br/>");
}
}
};
} ();
utils.messages.sayHello();
Upvotes: 2