【问题标题】:What's up with this JavaScript pattern?这个 JavaScript 模式是怎么回事?
【发布时间】:2011-08-25 06:52:01
【问题描述】:

我看到了这种模式:

Money = (function() {
    function Money(rawString) {
        this.cents = this.parseCents(rawString);
    }
});

在这个CoffeeScript screencast preview。 (截屏主页是here。)

现在,我不明白这种模式。有一个 Money 函数包含一个 Money 函数。那是怎么回事?

谁能解释一下?

【问题讨论】:

    标签: javascript coffeescript


    【解决方案1】:

    我是上述截屏视频的作者,也是 sn-p 的来源。一些澄清:

    • 提到 sn-p 的上下文是在 JavaScript 和 CoffeeScript 语法的动画比较中。
    • 有意对其进行了简化,以免在视频中那个确切时刻教授的 CoffeeScript 概念的上下文中增加额外的混乱(视频不是试图教授 JavaScript 构造函数或类语法)。
    • 您可以通过 CoffeeScript 编译器运行任何 CoffeeScript sn-p 的完整 JavaScript 文本,如截屏视频所示,或通过在 CoffeeScript 官方网站上运行它。

    我将在上面提到的视频和预览中添加说明。

    否则,这里关于 Stack Overflow 的其他解释都是正确的。如果您正在构建一个 JavaScript 类,您应该返回当前对象并调用上面显示的匿名函数。但这不是 CoffeeScript 的重点。 ;-)

    【讨论】:

      【解决方案2】:
      Money = (function() {
          var uid = 0;
          function Money(rawString) {
              this.cents = this.parseCents(rawString);
              this.uid = uid++;
          }
          return Money;
      })();
      

      这种模式的另一个用例是让局部变量的行为就像静态绑定到函数一样。

      这与模块模式略有不同,因为您将静态私有信息添加到函数中。而不是打包数据并返回一个在范围内具有一些局部变量的对象。

      实现此目的的另一个选项是使用Money.uid,但这将是公开的。

      【讨论】:

        【解决方案3】:

        这里发生了三件事:

        首先,正如其他回答者所指出的,PeepCode 截屏视频中给出并在问题中引用的代码有几个错误。有return,调用了外层函数。

        其次,作为 T.J.注意,这是一个模块模式。您可以在 CoffeeScript class 块中执行任意代码,并且变量遵循与其他函数相同的范围规则。因此,例如,您可以编写

        class HashedPassword
          salt = Math.random()
          constructor: (password) ->
            @value = hash password, salt
        

        在这种情况下,salt 仅在 HashedPassword 类定义中可见。

        最后,应该注意的是,这是 CoffeeScript 唯一使用“命名”函数的地方(那些用function foo() 而非foo = function() 声明的函数)。命名函数非常适合堆栈跟踪等,但它们会导致 IE (除非在这样的模块范围内(请参阅CoffeeScript FAQ,标题“有什么办法吗?命名函数,用于反射和递归?”)。因此,class 语法的次要用途是安全地声明命名函数。

        我希望这能回答你的问题,Šime。

        【讨论】:

          【解决方案4】:

          外部 Money 函数不接受任何参数。内部 Money 函数通过闭包捕获 rawString。这里的优点是您不会使用内部 Money 函数定义污染全局命名空间。

          编辑:我同意 TJ 的观点,即目前的模式是无用的。它不做任何事情,并且外部函数仅用于范围界定。如果没有看到截屏作者的完整示例,很难判断他的意图。

          【讨论】:

          • 是的,但是我们正在使用外部 Money 函数定义污染(最外层)命名空间。那么如何不污染内部命名空间是一种优势呢?
          • 您正在封装函数 Money(rawString) 从而阻止其他人调用它。
          • 但是您似乎正在使用外部无参数 Money() 定义污染全局命名空间。我仍然不明白你是如何获得优势的。你能澄清一下吗?另外,rawString 变量存储在哪里?是否必须在全局命名空间的其他地方定义?
          • 另外,这根本无法解释它在做什么。这是一个什么都不返回的函数,无法调用内部的Money函数。
          • @Reensis 我猜这使它变得无用(= 该模式不起作用)。
          【解决方案5】:

          使用视频声称是正确转换的 CoffeeScript 代码...

          class Money
              constructor: (rawString) ->
                  @cents = @parseCents rawString
          

          ...CoffeeScript 将生成以下内容,这与@T.J. 基本相同。克劳德的回答:

          var Money;
          Money = (function() {
            function Money(rawString) {
              this.cents = this.parseCents(rawString);
            }
            return Money;
          })();
          

          我发布这个只是为了展示 CoffeeScript 实际上 做了什么,并且视频并不代表现实。

          visit the site 并点击“Try CoffeeScript”按钮即可看到转换。

          不要“接受”这个答案。


          编辑:

          要添加一些利用范围的私有变量用法,您可以这样做:

          class Money
              priv=0
              constructor: (rawString) ->
                  @cents = @parseCents rawString
                  @id = priv++
          

          ...呈现为:

          var Money;
          Money = (function() {
            var priv;
            priv = 0;
            function Money(rawString) {
              this.cents = this.parseCents(rawString);
              this.id = priv++;
            }
            return Money;
          })();
          

          顺便说一句,我对 CoffeeScript 一无所知。它的语法让我感到困惑,但也许只是因为我不习惯它。

          我喜欢 JavaScript 的本来面目(尤其是新的和即将到来的变化)。

          【讨论】:

          • 不错的一个。因此,正如我所提到的,它设置了一个私有范围,但没有使用它。 (而且它通过使用var 来防止删除,但是,嘿,无论如何这是一个前卫的用例......)
          • 那就大错特错了。制作人忘记包含return Money; 语句和结尾的()
          • @T.J.克劳德:是的,我认为这只是一个简短的例子。实际上使用范围会更有意义。
          • @Šime:是的,这些都是相当大的遗漏。
          • @Šime:联合起来对付我吧? ;)
          【解决方案6】:

          它看起来不像一个真实的例子,“外部”函数的分组运算符是没有意义的,正如 TJ 所说,它绝对什么都不做。作为构造函数调用,它将返回一个空对象。

          @TJ - 引用正确,您需要观看大约 40 秒的视频。

          【讨论】:

          • 我认为它在视频中被错误引用了。
          • @Crowder 我想我最好联系那个截屏视频的制作人然后:)
          【解决方案7】:

          正如引用的那样,除了可以从window 对象中删除外部Money 符号之外,该模式没有任何意义(IE7 及以下版本除外,但这是另一回事),因为它是一个普通(隐式)属性window (与 var 或从函数声明派生的符号相反)。但即便如此,外部的Money 符号也会收到一个完全不执行任何操作的函数。会不会引用错误?

          例如,这是一个相当标准的模式:

          Money = (function() {
              var someCompletelyPrivateVariable;
          
              function doSomethingCompletelyPrivate() {
              }
          
              function Money(rawString) {
                  this.cents = this.parseCents(rawString);
              }
          
              return Money;
          })();
          

          这就是模块模式,它让您拥有完全私有的变量和函数(均已说明),而只有一个公共符号。但我不得不编辑相当多的内容来创建它(最重要的编辑是最后的 return Money; 和在匿名函数之后添加的 () 所以我们调用它而不仅仅是定义它。

          【讨论】:

          • @Crowder 你能详细说明一下吗?为什么可以删除?
          • 构造函数返回this以外的东西并没有多大意义。
          • @RobG:我添加了一个 return 的东西不是构造函数,它是一个作用域函数。它的目的纯粹是为了使私有范围。它返回一个可能是构造函数的函数。没有更多的范围,我们无法知道。
          猜你喜欢
          • 1970-01-01
          • 2013-07-28
          • 1970-01-01
          • 2011-06-28
          • 2020-04-03
          • 2018-06-20
          • 1970-01-01
          • 2022-07-18
          • 1970-01-01
          相关资源
          最近更新 更多