【问题标题】:Coffeescript "return" and jQuery plugin authoring - with templatesCoffeescript "return" 和 jQuery 插件创作 - 使用模板
【发布时间】:2014-05-29 13:44:55
【问题描述】:

我发现了我一直在研究的这个CoffeeScript boilerplate for jQuery plugins,并且[试图]在我正在[试图]编写的插件中使用它。我在其他几个问题中引用了相同的样板/模板。我是 JavaScript 的业余爱好者,也是 CoffeeScript 的新手。我正在努力学习和学习,但是当有些事情困扰我并且我无法通过 Google 找到令人满意的答案时,我来到这里......所以请原谅我在此处编写和呈现的任何代码中缺乏知识和潜在错误.

CoffeeScript 代码编译成这样:

(function() {
    (function($, window, document) {
        var $this, methods, _anotherState, _flag, _internals, _settings;
        $this = void 0;
        _settings = {
            "default": 'cool!'
        };
        _flag = false;
        _anotherState = null;
        methods = {
            init: function(options) {
                $this = $(this);
                $.extend(_settings, options || {});
                return $this;
            },
            doSomething: function(what) {
                return $this;
            },
            destroy: function() {
                return $this;
            }
        };
        _internals = {
            toggleFlag: function() {
                return _flag = !_flag;
            },
            computeSomething: function(state, flag) {
                return flag != null ? flag : {
                    state: "No, that's not right."
                };
            }
        };
        return $.fn.foobar = function(method) {
            if (methods[method]) {
                return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
            } else if (typeof method === "object" || !method) {
                return methods.init.apply(this, arguments);
            } else {
                return $.error("Method " + method + " does not exist on jquery.foobar");
            }
        };
    })(jQuery, window, document);

}).call(this);

来自herehere 我了解到(function(){...}).call(this) 包装器是一种CoffeeScript 功能,旨在本地化未明确声明为全局的变量。 我后来才知道,它也可以在编译过程中被抑制。我还了解到,我不需要将 windowdocument 作为 jQuery 闭包的参数。

当我对它进行更多研究(并尝试对其进行编辑)时,我看到在编译的代码中,闭包(它是一个函数)在它定义它的地方返回$.fn.foobar。由于该函数是匿名的并且无论如何都不会被调用,我想返回的值并不重要。但是,如果我像这样在 CoffeeScript 代码中添加 return 语句会怎样:

$.fn.foobar = (method) ->
  if methods[method]
    methods[method].apply this, Array::slice.call(arguments, 1)
  else if typeof method is "object" or not method
    methods.init.apply this, arguments
  else
    $.error "Method " + method + " does not exist on jquery.foobar"
# -- This, right here...
return
# --

它不再编译为return $.fn.foobar = ...,而是编译为$.fn.foobar = ...。我认为这没有任何区别,而是使 JS 输出更多...clean...如果你愿意的话。但我需要确认这一点。这对脚本的执行有何影响?

此外,在 CoffeeScript 代码中,作者说在 methods.init() 内部我需要对 $this.each 执行所有操作,但如果我这样做了

  init: (options) ->
    $.extend _settings, (options or {})
    return $(@).each () -> # I don't really need return, do I?
      # In here @ is one element (out of the array of elements)
      return # This is to suppress any returns inside .each()

就是这样...这是我的问题

  1. 是否有理由不使用 CoffeeScript 代码中的匿名函数return?这与原始 CoffeeScript 代码有何不同?
  2. 在调用插件的 jQuery 数组中迭代所有项目,同时保持可链接性的正确方法是什么。

注意:我没有包含 CoffeeScript 代码以避免帖子过长。但是我提供了一个链接到列出代码的页面。但是,如果有问题,请在 cmets 中告诉我,我将编辑帖子以包含 CoffeeScipt 代码。感谢您的宝贵时间。

【问题讨论】:

  • 你心目中的“可链接性”是什么样的?
  • @hpaulj 链接性,如$('#id.class').plugin(args).someOtherjQueryFunction(args)。这只有在plugin 返回$('#id.class') 时才有可能。如果$('#id.class') 对应于一个元素数组而不是一个元素,则应该返回所有这些元素,而不仅仅是其中一个。

标签: design-patterns jquery-plugins coffeescript


【解决方案1】:

这是更复杂的样板之一。他谈到遵循指导方针,但随后通过实现一个foobar 插件使事情复杂化,并将事情委托给多个methods。这掩盖了返回的内容以及链接应该如何工作。我在他引用的“高级插件”页面中没有看到这种复杂程度 (http://learn.jquery.com/plugins/advanced-plugin-concepts/)

将其与更简单的进行比较可能会有所帮助

http://coffeescriptcookbook.com/chapters/jquery/plugin

https://github.com/jquery-boilerplate/jquery-boilerplate/blob/master/src/jquery.boilerplate.coffee

https://stackoverflow.com/a/18049128/901925 一个咖啡脚本脚本,它创建 2 个插件,并将它们连接到 flot 图。它是对flot javascript 示例的改编。请注意$ ->$.fn.pluginname ->$('element').plugin() 的使用。这些是关键的 jQuery 部分。

coffeescript 函数只返回最后一项。如果调用者不使用返回值,它返回函数的事实并不重要。创建插件时重要的是 $.fn 获得一个新属性(有些使用extend 来添加几个属性)。

如果插件使用@each,并使用$("li").myplugin() 之类的名称调用,则使用每个元素

$.fn.myplugin = (options) ->
  @each ()->
    console.log 'adding plugin to ',@

样板在评论中提到了这一点

#  $this.each (index, el) ->
#    # do something with el

整合这些想法的更好方法可能是浏览 learn.jquery 页面,并在 Coffeescript 中编写脚本。它们足够短,您可以使用基于浏览器的解释器,并排显示咖啡脚本和 javascript。

【讨论】:

  • 你怎么看这个:Lightweight jQuery Plugin Boilerplate。如果我将它转换为 CoffeeScript,那就是......
  • 我看到您提供的第二个链接(this one)实际上实现了我在上面评论中提到的那个。虽然我还没有遇到过 CoffeeScript,但我在 JavaScript 中使用了这个作为我的默认插件模板。它很好,很坚固。但它不提供$(element).plugin('method', methodOpts) 功能。因为一旦在一个元素上调用了插件,就不能再次调用它。您能否建议一种使用此模板实现动态更新能力的方法?
  • if !$.data(@, "plugin_#{pluginName}") 行阻止插件第二次安装。我会尝试将其更改为 if true 或省略它。
【解决方案2】:

我是这样做的(see the whole source in GitHub 包括如何使用 QUnit 对 Coffeescript jQuery 插件进行单元测试,以及如何使用 sourceMap 将其构建为缩小版本的示例。)

throw "Expected jQuery to have been loaded before this script."  if typeof jQuery isnt "function"
(($) ->

  # Main jQuery Collection method.
  $.fn.myPlugin = (options) ->
    self = this
    opts = $.extend true, {}, $.fn.myPlugin.options
    @options = if typeof options is "object"
      $.extend(true, opts, options)
    else
      opts
    @each ->
      $this = $(this)

      # TODO: do pluginy stuff with $this

      return $this # because it's chainable.

  # defaults
  $.fn.myPlugin.options =
    whatever: true
) jQuery

对应的QUnit测试看起来像

(($) ->

  module "basic tests",

    setup: ->
      this.elems = $("#qunit-fixture").children(".qunit-container")

  # all jQuery plugins must be chainable.
  test "is chainable", ->
    expect(1)
    strictEqual(this.elems.myPlugin(), this.elems, "should be chainable")

) jQuery

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 2013-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 1970-01-01
    • 2011-06-30
    相关资源
    最近更新 更多