From: Scott Sauyet on
What techniques to people use to hide implementation details in shared
code, especially to keep helper variables reasonably private?

Several threads on FAQ questions had me randomly reading the FAQ, and
the entry on checking for an open child window [1] made me pause. The
suggestion not to use pop-ups is fine, and there is nothing exactly
wrong with the code. But it introduces a global variable as well as
the function. This might well be what's wanted here, but often it's
just unwanted clutter, and I'm wondering what suggestions the experts
here have to avoid such clutter.

For example, the following adds the unwanted global "count":

var count = 0;
function test() {
alert("#" + (++count) + " run at " + new Date());
}

There are several ways to deal with that. I often use a closure:

var test = (function() {
var count = 0;
return function() {
alert("#" + (++count) + " run at " + new Date());
}
})();

Or we could make "count" a property of the function:

function test() {
test.count = test.count || 0;
alert("#" + (++test.count) + " run at " + new Date());
}

I know that there are performance concerns having to do with the
overuse of closures, but I don't really know any details. Obviously
that is an argument against the first technique. Another argument
against it is the additional syntactic complexity.

But the closure technique also has the advantage that the
implementation doesn't leak at all, whereas in the other one, the
implementation is exposed and could be manipulated by malicious or
clueless use, via, say:

test.count = 42;

Obviously, in many situations that is simply not an issue. ("Doc, it
hurts when I do *this*." "So don't do that!") But when writing
reusable code for a wide user base, it might be at least one
consideration.

There are also OO techniques to handle this such as this:

function Thingy() {
this.count = 0;
}
Thingy.prototype.test = function() {
alert("#" + (++test.count) + " run at " + new Date());
}
var thingy = new Thingy();

But this is no longer a drop-in replacement for the original function,
and it requires the object "thingy" in the global scope. And
sometimes, there's the stupid problem of simply not having a good noun
(hence "Thingy"); this can actually be an important consideration to
me. In general, unless I'm already coding all my relevant script in
an OO manner, this holds little appeal. Are there others for whom
this is obviously the best solution?

So how do you choose between these methods, or what other techniques
do you use to keep your implementation details hidden and especially
keep helper variables out of the global scope?

-- Scott
______________________
[1] Section 9.4 <http://jibbering.com/faq/#isWindowOpen>
From: Evertjan. on
Scott Sauyet wrote on 02 feb 2010 in comp.lang.javascript:

> var test = (function() {
> var count = 0;
> return function() {
> alert("#" + (++count) + " run at " + new Date());
> }
> })();

KISS:

function test() {
alert("#1 run at " + new Date());
};

--
Evertjan.
The Netherlands.
(Please change the x'es to dots in my emailaddress)
From: Scott Sauyet on
On Feb 2, 4:32 pm, "Evertjan." <exjxw.hannivo...(a)interxnl.net> wrote:
> Scott Sauyet wrote on 02 feb 2010 in comp.lang.javascript:
>
>>    var test = (function() {
>>         var count = 0;
>>         return function() {
>>             alert("#" + (++count) + " run at " + new Date());
>>         }
>>     })();
>
> KISS:
>
> function test() {
>   alert("#1 run at " + new Date());
>
> };

And the second time you call it?...

-- Scott
From: Garrett Smith on
Scott Sauyet wrote:
> What techniques to people use to hide implementation details in shared
> code, especially to keep helper variables reasonably private?
>

All Function objects have a scope chain.

Based on the code examples below, you already knew that.

> Several threads on FAQ questions had me randomly reading the FAQ, and
> the entry on checking for an open child window [1] made me pause. The
> suggestion not to use pop-ups is fine, and there is nothing exactly
> wrong with the code. But it introduces a global variable as well as
> the function. This might well be what's wanted here, but often it's
> just unwanted clutter, and I'm wondering what suggestions the experts
> here have to avoid such clutter.
>

Function rewriting?

var openWin = function(aURL) {
var myWin;
return (openWin = function(aURL) {
if (!myWin || myWin.closed ) {
return window.open(aURL,'myWin');
} else {
myWin.location.href = aURL;
myWin.focus();
return myWin;
}
})(aURL);
};

openWin("http://example.com");

> For example, the following adds the unwanted global "count":
[snip example]

>
> There are several ways to deal with that. I often use a closure:
>
[snip example]

There you go.

> Or we could make "count" a property of the function:
>
> function test() {
> test.count = test.count || 0;
> alert("#" + (++test.count) + " run at " + new Date());
> }
>
> I know that there are performance concerns having to do with the
> overuse of closures, but I don't really know any details. Obviously
> that is an argument against the first technique. Another argument
> against it is the additional syntactic complexity.
>

Scope Chain and Identifier Resolution in ECMA-262 clearly states what
the observable behavior is.

In most cases, closures help performance. This is going to be even more
true in future releases of Tracemonkey.
https://bugzilla.mozilla.org/show_bug.cgi?id=517164

Tracemonkey has optimized global access, so global access is a bit
faster for now.

Closure access is generally faster than global access. Closure access is
almost always faster than property access.
http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/566395fc193eb4f1/1120d91202f005cd?show_docid=1120d91202f005cd&fwc=2

In most implementations, accessing a global property up a long scope
chain will be slower than accessing off the containing scope.

Accessing the global `test` property, then resolving the `count`
property is more involved than resolving a `count` property in
containing scope.

An added benefit to hidden identifiers is munging. An optimizer such as
YUI Compressor will rename a private `longMeaningfulIdentifier` to `B`,
for example.

> But the closure technique also has the advantage that the
> implementation doesn't leak at all, whereas in the other one, the
> implementation is exposed and could be manipulated by malicious or
> clueless use, via, say:
>
> test.count = 42;
>

Right. The problem with that is that it makes it harder to change. If
the interface does not expose a count property, then renaming that
hidden identifier can be a self-contained change.

> Obviously, in many situations that is simply not an issue. ("Doc, it
> hurts when I do *this*." "So don't do that!") But when writing
> reusable code for a wide user base, it might be at least one
> consideration.
>

When writing code for your own use later, it can be a consideration. By
defining a simple interface, it is clearer what variables are public and
which are hidden.

> There are also OO techniques to handle this such as this:
>
> function Thingy() {
> this.count = 0;
> }
> Thingy.prototype.test = function() {
> alert("#" + (++test.count) + " run at " + new Date());
> }
> var thingy = new Thingy();
>
> But this is no longer a drop-in replacement for the original function,
> and it requires the object "thingy" in the global scope.

Identifier `thingy` can be restricted and used in a closure, too.

(function(){
var thingy = new Thingy();
})();
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/
From: Evertjan. on
Scott Sauyet wrote on 02 feb 2010 in comp.lang.javascript:

> On Feb 2, 4:32�pm, "Evertjan." <exjxw.hannivo...(a)interxnl.net> wrote:
>> Scott Sauyet wrote on 02 feb 2010 in comp.lang.javascript:
>>
>>> � �var test = (function() {
>>> � � � � var count = 0;
>>> � � � � return function() {
>>> � � � � � � alert("#" + (++count) + " run at " + new Date())
> ;
>>> � � � � }
>>> � � })();
>>
>> KISS:
>>
>> function test() {
>> � alert("#1 run at " + new Date());
>>
>> };
>
> And the second time you call it?...

You are right.

--
Evertjan.
The Netherlands.
(Please change the x'es to dots in my emailaddress)