Best ways to hide implementation details

S

Scott Sauyet

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>
 
E

Evertjan.

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());
};
 
S

Scott Sauyet

Scott Sauyet wrote on 02 feb 2010 in comp.lang.javascript:


KISS:

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

};

And the second time you call it?...

-- Scott
 
G

Garrett Smith

Scott said:
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...91202f005cd?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();
})();
 
N

nick

Scott Sauyet wrote:
...
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);

};

Why not just:

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


....BTW Scott, most popup blockers seem to intercept this, but I guess
that's beside the point :)

-- Nick
 
G

Garrett Smith

nick said:
Scott Sauyet wrote:
...
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 ) {

That should be:
myWin = window.open(aURL,'myWin');
return myWin;
Why not just:

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

That will create a new function and execute that new function each time
openWin is called.

In contrast, the function re-writing replaces the identifier of the
containing scope to point to the function in the nested scope, but only
after executing it the first time.
 
N

nick

nick wrote:
...
Why not just:
Code:
[/QUOTE]

That will create a new function and execute that new function each time
openWin is called.

In contrast, the function re-writing replaces the identifier of the
containing scope to point to the function in the nested scope, but only
after executing it the first time.[/QUOTE]

Ah, I see what you did there. So the "openWin = function ..." is where
the re-writing happens, missed that the first time around. That's a
pretty neat trick.
 
N

nick

That will create a new function and execute that new function each time
openWin is called.

Thinking about this a little more, would this also cause the inner
function to be created every time the outer is called (I'm not sure
how to test this)?

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

If f() does only get created once this might be a simpler syntax to
use.

-- Nick
 
L

Lasse Reichstein Nielsen

nick said:
Thinking about this a little more, would this also cause the inner
function to be created every time the outer is called (I'm not sure
how to test this)?

Yes it would, but the outer function will only be called once. After
that, the reference to the outer function would be overwritten.

Personally I would do it like:

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


/L
 
D

Dmitry A. Soshnikov

[snip]
whereas in the other one, the
implementation is exposed and could be manipulated by malicious or
clueless use, via, say:

The first (and main) thing to understand about encapsulation should be
that encapsulation is an increasing of an abstraction and a helper
(firstly) for the programmer. It should not be treated as a security
technique, as it's not. That is for "malicious".

About the "clueless" - yeah, encapsulation will help to manage the
code more carefully. Simple (public) API for export - is a good way to
concentrate on the tasks for those this API was deigned and do not
think (much ;)) about the helper functionality which is encapsulated
(read - abstracted) from the user of that API.

Simple function - is an encapsulation (abstraction): when you use
Math.round(...) - it doesn't matter, what does this abstraction does
inside. But for the outside it returns acceptable mathematical result.

And access control ability such as "public", "private" and "protected"
is a useful syntactic sugar provided by some implementations. this
"sugar" is really useful in main encapsulation goal - to help the
programmer itself, to build this public API, abstracting
(encapsulating) "non-interesting" internal (private) things for the
end user. But it's not about protection from malicious access (as
examples: Python or Ruby, where there're all that "public" and
"privates" but there're still abilities to get the access to
encapsulated data if needed).

And for the security reasons the completely different techniques are
used.
But the closure technique also has the advantage that the
implementation doesn't leak at all

Yeah, simple additional (helper) context for encapsulation (usually as
an (anonymous) function expression) - is a good decision for avoiding
polluting the outside scope. Although, in some (versions of)
implementations there's ability to get access to internal activation
object using `eval' and modify "private" var's.

/ds
 
S

Scott Sauyet

Personally I would do it like:

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

This is the method I use most often, and from what Garrett said, there
don't seem to be any performance issues in using it. There is
something to be said for Garrett's function re-writing, though, too,
which I might write a little more explicitly like this:

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

One advantage to this is that before this function is called, there is
no function call performed to define it and no variable declaration
made. Certainly this is not an issue with such minor initialization,
but for a function that possibly would never be called and that
requires substantial initialization, I can easily see an advantage to
this. On the other hand, if this initialization is time-consuming,
perhaps waiting for a user-initiated event to run it would be
problematic too.

As to speed, the two techniques would have similar scope chains,
correct?

Thanks everyone for your input!

-- Scott
 
S

Scott Sauyet

The first (and main) thing to understand about encapsulation should be
that encapsulation is an increasing of an abstraction and a helper
(firstly) for the programmer. It should not be treated as a security
technique, as it's not. That is for "malicious".

Perhaps you're right. I'm not convinced, though, that there is no
good reason to try to protect my code as best I can against even
intentional misuse.

About the "clueless" - yeah, encapsulation will help to manage the
code more carefully. Simple (public) API for export - is a good way to
concentrate on the tasks for those this API was deigned and do not
think (much ;)) about the helper functionality which is encapsulated
(read - abstracted) from the user of that API.

This is of course the main point. A clean API is much more important
than a clean implementation.

Yeah, simple additional (helper) context for encapsulation (usually as
an (anonymous) function expression) - is a good decision for avoiding
polluting the outside scope. Although, in some (versions of)
implementations there's ability to get access to internal activation
object using `eval' and modify "private" var's.

Really, I've not seen that. Can you point me to further information?

Thanks,

-- Scott
 
D

Dmitry A. Soshnikov

[...]
Really, I've not seen that.  Can you point me to further information?

For example, in Spidermonkey version 1.7:

var a = (function () {
var x = 10; // "private"
return function () { // public "API"
alert(x);
};
})();

a(); // 10
eval('x = 20', a);
a(); // 20

Another encapsulation technique in ECMAScript is a simple anonymous
object (not a function object) which runs its initialization method
right after creation, which in its turn makes all the job by creating
public/global properties. This technique is like an anonymous function
context (and actually it is - here .initialize method play this role),
but also let to organize some additional helper data not only in this
helper context:

({

... // other stuff

initialize: function () {
var y = 20;
global.publicMethod = function () {
alert(y);
};
}

... // other helper stuff

}).initialize();

publicMethod(); // 20

/ds
 
D

Dmitry A. Soshnikov

[...]


Really, I've not seen that.  Can you point me to further information?

For example, in Spidermonkey version 1.7:

var a = (function () {
  var x = 10; // "private"
  return function () { // public "API"
    alert(x);
  };

})();

a(); // 10
eval('x = 20', a);
a(); // 20

Addition: it is fair for any implementation which allows access to the
activation object (and its properties) of a function.

/ds
 
S

Scott Sauyet

Addition: it is fair for any implementation which allows access to the
activation object (and its properties) of a function.

Thanks for all the pointers. Very interesting stuff, there.

-- Scott
 
R

RobG

 There is
something to be said for Garrett's function re-writing, though, too,
which I might write a little more explicitly like this:

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

One advantage to this is that before this function is called, there is
no function call performed to define it and no variable declaration
made. Certainly this is not an issue with such minor initialization,
but for a function that possibly would never be called and that
requires substantial initialization, I can easily see an advantage to
this. On the other hand, if this initialization is time-consuming,
perhaps waiting for a user-initiated event to run it would be
problematic too.

Peter Michaux has a blog entry on what he calls "lazy function
definition" that seems relevant:

<URL: http://peter.michaux.ca/articles/lazy-function-definition-pattern
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,479
Members
44,899
Latest member
RodneyMcAu

Latest Threads

Top