基于一次性的自定义函数模式

On a one-time self-re-defining function pattern

本文关键字:自定义函数 模式 一次性      更新时间:2023-09-26

考虑以下模式:

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_fooinner_foo分别表示上面的外部和内部函数,尽管代码不使用这些标识符。

outer_foo的主体包括三个阶段(后两个阶段各由一条语句组成):

  1. 一些闭包变量的声明和初始化;
  2. 标识符foo的重新定义;
  3. 将最初传递给outer_foo的参数传递给inner_foo,并返回结果。

outer_foo函数体将最多执行一次,即第一次执行名为"foo"的函数。被调用,如果它发生的话。从今以后,outer_foo的函数体将不可访问,随后所有对名为foo的函数的调用都将不可访问。将导致执行inner_foo

一般来说,人们可以设想这种模式的一些变化1,但是我在这里谈论的基本模式的基本约束是:

  1. foo在执行outer_foo 2;
  2. fooinner_foo的执行过程中不会被重新定义。

(如果违反了这些基本约束,所有的赌注都是无效的;这种情况不在这个问题的范围之内。


我已经意识到这个方案的至少一个缺点:一些实践者认为自定义函数令人困惑,不利于代码的可读性,和/或本质上很差,即使重新定义只以完全确定的方式发生一次,就像上面的方案中的情况一样。

我的问题是:

该方案是否有任何额外的缺点,如果有,它们是什么?

我对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的最终值取决于随机过程的结果),所以我把它省略了。

我还能想到两点你应该注意:

  • 。编译器就像你提到的那些人。他们中的一些人会对这种模式感到困惑,他们可能无法像使用不同的模式那样对其进行优化。这可能是一个问题,也可能不是,但你应该确保测试它。
  • <
  • 打破合同/strong>。您说希望outer_foo最多执行一次。事实可能并非如此!有人可能会传递它并在某处放置对它的引用,这样在被调用时它就不会变得不可访问。当然,这是不可能的,但根据您的需求,您可能想要防止它。
    毕竟,这是一种失败的设计,因为仅仅是一个别名 (var f = foo; f())不应该改变功能。确保所有消费者/用户都知道你在做什么,这样他们就不会被绊倒。