【问题标题】:Looking for a way to refactor D3.js-style method chaining pattern寻找一种重构 D3.js 风格的方法链模式的方法
【发布时间】:2013-02-18 15:46:39
【问题描述】:

在学习 D3.js 时,我遇到了 blog post,它解释了可重用代码单元背后的主要设计模式。我已经复制了下面的相关代码。下面展示的模式正是它在 D3 代码库和插件中使用的方式 (example)。

我对这段代码的一个问题是它有太多的属性复制粘贴。 JavaScript 作为一种函数式语言,我认为我可以将样板代码重构出来,但我想不出办法来做到这一点。 argumentsvalue 参数很容易传递给通用函数,但我找不到保留对 widthheight 属性的引用的方法。

function chart() {
  var width = 720, // default width
      height = 80; // default height

  function my() {
    // generate chart here, using `width` and `height`
  }

  my.width = function(value) {
    if (!arguments.length) return width;
    width = value;
    return my;
  };

  my.height = function(value) {
    if (!arguments.length) return height;
    height = value;
    return my;
  };

  return my;
}

事实上,这是在实际 D3 代码库中完成的,这让我想知道重构是否可能,但我希望这只是这不是一个高优先级问题的问题(新的贡献者是这样做,因为以前就是这样做的)。

我正在寻找的基本上是将每个访问器的主体替换为:

my.height = function(value) {
  return getSet(arguments, value, whatever);
};

调用仍有一些样板,但至少逻辑是集中的,如果需要,可以只在一个地方更新。

【问题讨论】:

  • 您到底在寻找什么?听起来你想要 php 的“神奇”getter/setter 之类的东西:一个可以照顾或获取/设置任何属性的函数。是这样吗?
  • @bfavaretto 更新了我的问题以澄清。
  • 嗯,你可能需要this之类的东西。

标签: javascript design-patterns d3.js functional-programming refactoring


【解决方案1】:

如果你在chart 的范围内定义getSet,它也可以访问封闭的变量。问题是,您不能通过名称字符串访问这些变量(除非您使用某种eval)。

您可以通过将所有私有变量包装在一个对象中来避免这种情况未测试

function chart() {
    var props = {
        width: 720, // default width
        height: 80 // default height
    }

    function my() {
        // generate chart here, using `width` and `height`
    }

    my.height = function(value) {
        // Call getSet with current my instance as this, 
        // 'height' as the first argument, then value
        return getSet.apply(this, arguments.slice().unshift('height'));
    };

    // Works just like your current accessors, on the props object
    function getSet(property, value) {
        if (arguments.length > 1) return props[property];
        props[property] = value;
        return this;
    }

    return my;
}

问题是这并不比为每个属性编写几个类似的访问器短多少。您当前的访问器使私有变量实际上是公开的,那么为什么不放弃它们而使用公共变量呢?

【讨论】:

  • 它们不仅仅是公共变量的原因在于,这种模式的全部意义在于启用方法链接。使用公共变量,您将拥有类似 chart.height=33 的内容,这将打破链接流程。此外, args.slice() 部分不起作用(没有这种方法),我不确定你的意图是什么。
  • 我现在看到我的代码中有一些错误。但是,我对 D3 不熟悉,所以我现在不知道该建议什么。你能举个例子说明它是如何在 D3 代码库上完成的吗?
  • OP中有一个示例链接,该文档的底部充满了“rollup.whateverXYZ”格式等功能。如您所见,它们都只是复制粘贴,这就是我要解决的问题。
【解决方案2】:

已经提供的另一种解决方案是定义一个“属性”函数,它返回一个函数,例如

function property (v) {
    return function (_) {
    if(!arguments.length)
            return v;
        v = _;
    return this;
};
}

所以你可以说:

function chart() {
    chart.width = property.call(chart, 500);
    chart.height = property.call(chart, 500);

    chart.render = function() {
    //render logic goes here
    };

    return chart;
}

我不能完全相信这一点 - 我认为来源实际上是 Mike Bostock(但我找不到原始帖子的链接)

我在我的许多模块中都使用了这个“属性”功能——它可以省去很多烦人的打字。您可以轻松地扩充它以在传入值更改时发出事件。

【讨论】:

    猜你喜欢
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-09
    • 1970-01-01
    • 2019-02-26
    相关资源
    最近更新 更多