【问题标题】:A function uses an object to initiate and destroy a resource. How can I rewrite this as HOF that provides a resource?函数使用对象来启动和销毁资源。如何将其重写为提供资源的 HOF?
【发布时间】:2020-05-28 20:15:46
【问题描述】:

这是我要开始的。 convert 使用 svgInjector 来启动和销毁资源。

export async function convert(
  serializedSvg: string,
  svgSourceId: string,
  containerId: string
): Promise<string> {
  const svgInjector = new SvgInjector(serializedSvg, containerId).inject();
  if (!svgInjector.injectedElement) {
    throw new Error("Svg not injected");
  }

  const doc = new TargetDocument({});
  const xml = convertRecursively(
    svgInjector.injectedElement,
    doc,
    {
      svgSourceId,
    }
  );

  svgInjector.remove();

  return doc.saveXML();
}

我怎样才能改写它来让一个更高阶的函数启动、提供和销毁资源svgInjector.injectedElement 给转换函数?

编辑:

这是一个最小的可重现示例:

var svg = '<svg xmlns="http://www.w3.org/2000/svg"><text x="20" y="20">I am made available in DOM</text></svg>'

function convert(
  serializedSvg,
  containerId
) {
  // make resource available (cross-cutting convern)
  var container = document.getElementById(containerId);
  var resource = new DOMParser().parseFromString(serializedSvg, "image/svg+xml").documentElement;
  container.appendChild(resource);

  // core convert functionality does things with resource
  console.log(resource.getBBox())
  
  // clean up resource (cross-cutting concern)
  resource.remove()
}

convert(svg, "container")
<!DOCTYPE html>
<html>
<head>
  <title>Minimal</title>
</head>
<body>
<div id="container">
</div>
</body>
</html>

编辑 2

这是之前编辑中 JavaScript 的 TypeScript 版本

var svg = '<svg xmlns="http://www.w3.org/2000/svg"><text x="20" y="20">I am made available in DOM</text></svg>'

function convert(
  serializedSvg: string,
  containerId: string
) {
  // make resource available (cross-cutting convern)
  var container = document.getElementById(containerId);
  if (!(container instanceof HTMLDivElement)) {
    throw new Error("Extpected a div element")
  }
  var resource = new DOMParser().parseFromString(serializedSvg, "image/svg+xml").documentElement;
  if (!(resource instanceof SVGSVGElement)) {
    throw new Error("Extpected a svg element")
  }
  container.appendChild(resource);

  // core convert functionality does things with resource
  console.log(resource.getBBox())

  // clean up resource (cross-cutting concern)
  resource.remove()
}

convert(svg, "container")

【问题讨论】:

  • 您能否将此代码设为适合放入独立 IDE 的 minimal reproducible example?现在SvgInjectorTargetDocumentconvertRecursively,以及任何依赖于它们的东西都没有定义,所以即使开始研究这个,我也需要删除它们或定义它们。下面的答案也是如此。祝你好运!
  • @jcalz:感谢您查看我的问题!我添加了一个最小的可重现示例。如果您认为缺少任何内容,请告诉我。
  • 如果我将该代码放入The TypeScript Playground,我会遇到几个错误;你是在问那些错误吗?如果没有,你能修复它们吗?
  • @jcalz:见我的第二次编辑。再次感谢你!我不是在问那些错误。我认为错误是因为第一次编辑有 JavaScript 版本。

标签: javascript typescript functional-programming higher-order-functions


【解决方案1】:

我不确定这是否是您正在寻找的那种东西,但我倾向于反转控制流,以便convert() 使用或传递一个“资源管理器”来处理资源的创建、提供和删除。 ResourceManager 可能只是一个函数,例如:

type ResourceManager<T, I> = <R>(initProps: I, cb: (resource: T) => R) => R;

所以ResourceManager&lt;T, I&gt; 是一个函数,它接受一些I 类型的初始属性包来指定需要哪个T 类型的资源,以及一个在资源可用之后和之前执行实际工作的回调函数它被摧毁了。如果回调函数返回结果,那么资源管理器也会返回结果。

这个ResourceManager&lt;T, I&gt; 是通用合约,可以重复用于不同类型的资源。当然,不同类型的资源需要自己的实现。例如,我会像这样从您的convert() 函数中取出ResourceManager&lt;SVGSVGElement, { serializedSvg: string, containerId: string }&gt;

const svgManager: ResourceManager<SVGSVGElement, { serializedSvg: string, containerId: string }> =
  (initProps, cb) => {

    // make resource available)
    var container = document.getElementById(initProps.containerId);
    if (!(container instanceof HTMLDivElement)) {
      throw new Error("Extpected a div element");
    }
    var resource = new DOMParser().parseFromString(initProps.serializedSvg, "image/svg+xml").documentElement;
    if (!(resource instanceof SVGSVGElement)) {
      throw new Error("Extpected a svg element")
    }
    container.appendChild(resource);

    // core functionality
    const ret = cb(resource);

    // clean up resource
    resource.remove()

    // return returned value if we have one
    return ret;
  }

注意“核心功能”是如何被推迟到回调的,它的返回值被保留以备不时之需。那么convert()就简化为:

function convert(
  serializedSvg: string,
  containerId: string
) {
  svgManager({ serializedSvg, containerId }, (resource => console.log(resource.getBBox())));
}

resource =&gt; console.log(resource.getBBox()) 是在不关心如何获取或处置resource 的情况下完成工作的函数。


希望对您有所帮助或给您一些想法。祝你好运!

Playground link to code

【讨论】:

  • 感谢您试一试。我赞成你的回答。我会再考虑一下,希望很快再次回到这个问题
【解决方案2】:

这是迄今为止我最好的尝试。我希望更聪明的人发布更好的解决方案。

我看到的以下解决方案的两个弱点是:

  • 增强器的类型不通用,阻碍重用
  • 增强子需要绑定,阻碍了增强子的合成
    type Props = {
      svg: SVGSVGElement;
      svgSourceId: string;
      containerId: string;
    };

    async function convertBase(props: Props): Promise<string> {
      const doc = new TargetDocument({});
      const xml = convertRecursively(props.svg, doc, {
        svgSourceId: props.svgSourceId,
      });

      return doc.saveXML();
    }

    type EnhancerProps = {
      serializedSvg: string;
      svgSourceId: string;
      containerId: string;
    };

    type EnhancerPropsLight = {
      svgSourceId: string;
      containerId: string;
    };

    function enhancer(fn: Function, props: EnhancerProps) {
      const rest = omit(["serializedSvg"])(props) as EnhancerPropsLight;
      const svgInjector = new SvgInjector(
        props.serializedSvg,
        props.containerId
      ).inject();
      if (!svgInjector.injectedElement) {
        throw new Error("Svg not injected");
      }

      const res = convertToTgmlBase({ ...rest, svg: svgInjector.injectedElement });

      svgInjector.remove();

      return res;
    }

    const convert = enhancer.bind(null, convertBase);
    export { convert };

【讨论】:

    猜你喜欢
    • 2020-12-27
    • 2014-09-05
    • 2011-07-15
    • 2021-06-01
    • 2022-07-13
    • 2021-02-15
    • 2017-12-18
    • 2016-07-23
    • 1970-01-01
    相关资源
    最近更新 更多