Consider the following pattern:
function foo /* aka outer_foo */ () {
// === stage 1 ===
// declaration and initialization of closure variables
var ... <CLOSURE_VARS> ...;
// initialization of CLOSURE_VARS
// (possibly expensive and/or depending on values known only at run-time)
// === stage 2 ===
// one-time redefinition of foo
foo = function /* inner_foo */ ( arg0, ... ) {
var results;
// code computing value of results based on CLOSURE_VARS, arg0, ...
// foo is never modified here
return results;
};
// === stage 3 ===
// invocation of inner function and returning of result
return foo.apply( this, arguments );
}
For convenience, I will use the terms outer_foo and inner_foo to designate, respectively, the outer and inner functions above, even though the code does not use these identifiers.
The body of outer_foo comprises three stages (the last two consisting of a single statement each):
foo;The body of outer_foo will be executed at most once, namely, the first time that "the function named foo" is called, if it ever happens. Henceforth, the body of outer_foo will be unreachable, and all subsequent calls to "the function named foo" will result in the execution of inner_foo.
In general, one may envision some variations on this pattern 1, but the essential constraints on the basic scheme I'm talking about here are:
foo is re-defined exactly once during the execution of outer_foo2;foo is never re-defined during the execution of inner_foo.(If these essential constraints are violated, all bets are off; such a case is outside the scope of this question.)
I am already aware of at least one downside of this scheme: some practitioners consider self-redefining functions confusing, detrimental to code readability, and/or inherently in poor taste, even when the re-definition happens only once in a completely deterministic manner, as is the case in the scheme above.
My question is:
Does this scheme have any additional downsides, and if so, what are they?
I'm particularly interested in those downsides that are specific to JavaScript.
1 For example,
function foo /* aka outer_foo */ () {
var inner_foo, ... <CLOSURE_VARS> ...;
if ( some_deterministic_test() ) {
inner_foo = function ( arg0, ... ) {
// etc.
}
}
else {
inner_foo = function ( arg0, ... ) {
// etc.
}
}
foo = inner_foo;
return foo.apply( this, arguments );
}
In this variant, the function ultimately assigned to foo depends on some test performed at run-time.
2 Here it's tempting to stipulate that foo not only must be re-defined exactly once within outer_foo, but that this be done "deterministically". Of course, any deviation from "determinism" (however one chooses to define it) in final setting of foo can only increase the complexity of the code's run-time behavior. Unfortunately, I don't know how to make this stipulation precise without descending into a maze of lawyerly minutiae. The best I could do would be to add the weasely—and borderline incoherent—phrase "the more deterministic the better", and hope that readers get my point. The only utility of this additional stipulation, however, would be to rule out perverse, but wholly unrealistic, scenarios (e.g. having the final value of foo depend on the outcome of a random process), so I left it out.
I can think of two additional points you should be aware of:
var f = foo; f()) should never alter functionality. Make sure all consumers/users know what you are doing so they don't trip over it.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With