jQuery插件在CoffeeScript中创作,具有传统的“私有”对象和“原型”调用

时间:2013-08-09 13:50:45

标签: design-patterns jquery-plugins coffeescript

背景

所以,几个月前我发现了一篇文章Essential jQuery Plugin Patterns。我试图编写一个简单的jQuery插件,我发现这篇文章非常有用。它包含大量信息,其中大部分信息超出了我对JavaScript或jQuery的专业知识。最感兴趣的是第一个插件模板,名为 jQuery Lightweight Plugin Boilerplate ,几个月前我在another question中引用了该模板。

最近,我开始使用CoffeeScript并查找 CoffeeScript jQuery插件模板,我找到了here

这两者都实现了几乎相同的概念。这里有一些重要的不同之处是:

  • 轻量级锅炉
  • 中没有的私有对象
  • CoffeeScript模板中不同的命名空间块。 (我完全不知道他对methods[method].apply this, Array::slice.call(arguments, 1)做了什么,虽然我有点理解 Lightweight Boilerplate 的命名空间块。
  • Lightweight Boilerplate 中,可以在命名空间块中处理对象的可链接性和迭代,而在 CoffeeScript模板 method.init()中需要return $(this)以及$(this).each()

研究

所以我继续拼接这两个。我写了自己的模板。在这里:

(($, window, document) ->

  # ---------------------------------------------------------------------------
  # Conventionally private variables
  # ---------------------------------------------------------------------------
  _PluginName = "FooBar"

  _Defaults   =
    property: 'value' # etc. etc.

  # ---------------------------------------------------------------------------
  # Private methods
  # ---------------------------------------------------------------------------
  _Debug      = (msg) ->
    window.console.log(msg)
    return

  # ---------------------------------------------------------------------------
  # Plugin Constructor
  # ---------------------------------------------------------------------------
  Plugin = (element, options) ->
    @element = element
    @options = $.extend true, {}, _Defaults, options

    # TODO: Call methods to do stuff
    return

  # ---------------------------------------------------------------------------
  # Plugin Methods
  # ---------------------------------------------------------------------------
  Plugin.prototype.init = () ->
    # TODO: Plugin initializiation logic
    return

  Plugin.prototype.destroy = () ->
    # TODO: Cleanup, unbind and eject
    return

  # ---------------------------------------------------------------------------
  # Actual plugin body
  # ---------------------------------------------------------------------------
  $.fn[_PluginName] = (options) ->
    return @.each () ->
      ($.data @, 'plugin_' + _PluginName
      new Plugin @, options
      ) unless $.data @, 'plugin_' + _PluginName
  return

) jQuery, window, document

我不是要求两个插件之间进行比较。而我的插件甚至还不完美。我从答案another question中发现,使用($, window, document)闭包是一种矫枉过正的行为。可能还有其他错误,绝对欢迎您指出并讨论它们。

尽管如此,我还有一些非常具体的问题。

问题

  1. 这些传统的“私人”对象/方法有多安全。如果有人故意打电话给Plugin()而不是以正确的方式使用插件怎么办?可以在除此闭包之外的任何范围内访问_Defaults吗?

  2. 这些“私人”物品的范围究竟是什么?它们是否会在jQuery名称空间中无所事事?他们会干涉什么吗?如何使用这些“私有”变量来帮助改善代码?

  3. 我不完全理解prototypeinit()本身的destroy()Plugin()属性是?为什么init()destroy()options的{​​{1}}属性称为Plugin(),就好像this.optionsinit()在内部定义一样destroy()本身的上下文......如果我将对象定义为Plugin()怎么办? Plugin.prototype.methods对象里面的函数this是什么?


  4. 注意:如果任何人都难以关注链接以查看代码,请告诉我们。我将编辑此问题以包含代码。我没有包括因为那时问题会变得太长。

1 个答案:

答案 0 :(得分:0)

当你做这样的事情时:

(->
    Pancakes = ...
    ...
)()

JavaScript版本如下所示:

(function() {
  var Pancakes;
  Pancakes = ...
  ...
})();

特别是,Pancakes是匿名自执行函数的本地,因此无法从函数外部访问它。另请注意,CoffeeScript编译器会将每个.coffee文件包装在self-executing function to avoid scope creep中,即:

Pancakes = 6

最终为(或多或少):

(function() {
    var Pancakes = 6;
})();

因此,即使您不手动添加某种范围包装器,CoffeeScript也会为您添加一个。当然,如果有人自己编译CoffeeScript,他们可以通过运行coffee --bare来编译CoffeeScript来抑制外部函数包装器。 OTOH,每个人都有权使用脚枪,如果需要的话,欢迎他们自己射击。

应该回答 1 2

就你的原型问题而言,你应该写:

Plugin::init = -> ...

而不是直接引用prototype,它们只是:: is more CoffeeScripty而是{。}}。

回到手头的问题。当你这样说:

class Pancakes

Pancakes::m = (where_is) -> 'house?'

与说法相同:

class Pancakes
    m: (where_is) -> 'house?'

所以为prototype分配内容只是在“类”中添加一个方法。这意味着@(AKA this)将(通常)成为对象:

pancakes = new Pancakes
pancakes.m() # `@` will be `pancakes` inside `m`

当然,任何CoffeeScript函数中@的值取决于(就像在JavaScript中一样)函数的调用方式以及函数是否已绑定到对象。

您可以使用simple example验证这一点:

class C
    constructor: ->
        @p = Math.random()
        console.log(@p)
C::m = -> console.log(@p)
(new C).m()

这将使您在控制台中获得与预期相同的随机数的两个副本。

希望能够处理 3