考虑以下模式:
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 );
}
为方便起见,我将使用术语 outer_foo 和 inner_foo 分别指定上面的外部函数和内部函数,即使代码不使用这些标识符。
outer_foo 的主体包括三个阶段(最后两个阶段各包含一个语句):
- 一些闭包变量的声明和初始化;
- 标识符
foo
的重新定义; - 将最初传递给 outer_foo 的参数传递给 inner_foo,并返回结果。
outer_foo 的主体将最多执行一次,即第一次调用“名为 foo
的函数”(如果有的话)。此后,outer_foo 的主体将无法访问,所有对“名为 foo
的函数”的后续调用将导致 inner_foo 的执行。
一般来说,人们可能会设想这种模式的一些变体1,但我在这里谈论的基本方案的基本限制是:
foo
在outer_foo
2 的执行过程中恰好被重新定义一次;foo
在inner_foo
执行期间永远不会被重新定义。
(如果违反了这些基本约束,则所有赌注都将取消;这种情况超出了本问题的范围。)
我已经意识到这个方案的至少一个缺点:一些从业者认为自重定义函数令人困惑,不利于代码的可读性,和/或固有的低趣味,即使重新定义在一个完整的过程中只发生一次确定性方式,如上述方案中的情况。
我的问题是:
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 例如,
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 );
}
在这个变体中,最终分配给 foo
的函数取决于运行时执行的一些测试。
2 这里很容易规定 foo
不仅必须在 outer_foo 中恰好重新定义一次,而且这“确定性地”完成。当然,在 foo
的最终设置中与“确定性”(无论选择如何定义它)的任何偏差只会增加代码运行时行为的复杂性。不幸的是,我不知道如何在不陷入律师细节的迷宫的情况下使这个规定精确。我能做的最好的事情就是添加一个狡猾的——而且几乎是不连贯的——短语“越确定性越好”,并希望读者明白我的意思。然而,这个附加规定的唯一用途是排除不正当但完全不现实的场景(例如,foo
的最终值取决于随机过程的结果),所以我离开了
最佳答案
我可以想到您应该注意的另外两点:
- 表现。编译器就像你提到的那些人。他们中的一些人会对这种模式感到困惑,并且他们可能无法像使用其他模式时那样对其进行优化。这可能是也可能不是问题,但您应该确保对其进行测试。
- 违约。你说你希望 outer_foo 最多执行一次。事实可能并非如此!有人可能会传递它并在某处放置对它的引用,这样它在被调用时就不会变得无法访问。当然,这不太可能,但根据您的要求,您可能想要防范它。
毕竟,这是一种损坏的设计,作为一个简单的别名 (var f = foo; f()
) 永远不应该改变功能。确保所有消费者/用户都知道您在做什么,这样他们就不会被它绊倒。
关于javascript - 关于一次性自重定义函数模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32038503/