在一次性自我重新定义功能模式

时间:2015-08-16 18:28:52

标签: javascript closures

考虑以下模式:

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 的主体包含三个阶段(最后两个阶段由每个单个语句组成):

  1. 声明和初始化一些闭包变量;
  2. 重新定义标识符foo;
  3. 传递给 inner_foo 最初传递给 outer_foo 的参数,并返回结果。
  4. outer_foo 的主体最多会被执行一次,即第一次&#34;名为foo&#34;的函数被叫,如果它发生了。此后, outer_foo 的主体将无法访问,并且所有后续调用&#34;名为foo&#34;的函数将导致执行 inner_foo

    一般来说,人们可能会想到这种模式的一些变化 1 ,但我在这里讨论的基本方案的基本限制是:

      在执行foo 2 期间,
    1. outer_foo只被重新定义一次;
    2. 在执行foo期间,
    3. inner_foo永远不会重新定义。
    4. (如果违反了这些基本限制,所有投注都会被取消;这种情况超出了本问题的范围。)

      我已经意识到这个方案至少有一个缺点:一些从业者认为自我重新定义的功能令人困惑,不利于代码可读性,和/或本质上很差,即使重新定义只发生一次完全确定性方式,如上述方案中的情况。

      我的问题是:

        

      此方案是否有任何附加缺点,如果是,那么它们是什么?

           

      我对那些特定于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 内完全重新定义一次,而且这可以确定性地完成&#34;。当然,任何偏离&#34;决定论&#34; (但是选择定义它)在foo的最终设置中只能增加代码运行时行为的复杂性。不幸的是,我不知道如何使这个规定准确无误,而不会陷入律师般的细枝末节。我能做的最好的事情就是添加狡猾和边缘不连贯的短语&#34;越确定越好,#34;并希望读者明白我的观点。然而,这个额外规定的唯一用途是排除不正当但完全不切实际的情景(例如,foo的最终值取决于随机过程的结果),所以我把它排除在外。功能

1 个答案:

答案 0 :(得分:1)

我可以想到你应该注意的两点:

  • 性能即可。编译器就像你提到的那些人。他们中的一些人会对这种模式感到困惑,他们可能无法像使用不同的模式那样优化它。这可能是也可能不是问题,但你应该确保测试它。
  • 合同破裂。你说你希望 outer_foo 最多执行一次。情况可能并非如此!有人可能会传递它并在某个地方引用它,以便在被调用时它不会变得无法访问。当然,这不大可能,但根据您的要求,您可能希望防范它 毕竟,这是一种破碎的设计,仅仅别名var f = foo; f())永远不应改变功能。确保所有消费者/用户都知道你在做什么,这样他们就不会绊倒它。