【问题标题】:Creating a jQuery-like fluent interface创建类似 jQuery 的流畅界面
【发布时间】:2022-08-19 22:43:47
【问题描述】:

有哪些有用的方法可以创建类似 jQuery 的流畅界面?

有人recently asked 提出了类似的问题,但花更多的时间与那些试图提供帮助的人争论和侮辱,而不是改进问题。当那个关闭时,用户只是did it again。 (这两个问题都已关闭,查看它们需要一定的声誉等级。抱歉。)

然而,那里有一个非常有趣的问题。对于那些不熟悉的人来说,jQuery 是一个 JavaScript DOM 脚本库,曾经几乎无处不在,现在仍然很受欢迎。它有各种各样的功能,很多与此无关。但其核心是抽象 NodeLists 的选择并在其中的所有节点上进行 DOM 更改的概念。 jQuery 通常以名称$ 公开,典型代码可能如下所示1

$ (\'.nav li:first-child a\') 
  .color (\'red\')
  .size (2, \'em\')
  .click ((evt) => {
    evt .preventDefault (); 
    console .log (`\"${evt .target .closest (\'a\') .textContent}\" link clicked`)
  })

请注意那里的流畅界面。节点被选中一次,然后函数colorsizeclick 对每个匹配的节点执行。那么,问题是我们如何构建这样一个系统,允许我们编写colorsizeclick 的简单版本,它们绑定在一个公共的链式接口中?

在其中一个问题结束之前,我编写了自己的版本,我将把它作为答案包含在内,用户@WillD 链接到a Fiddle,我希望它会成为另一个答案。

对于这个 pass,我建议我们不要试图模仿 jQuery 的其他部分功能,当然不是像 .ajax$(document).ready(...) 这样的东西,但也不要打扰插件机制(除非它是免费的)或能够从当前的NodeList 生成一个新的NodeList。主要问题是如何将函数集合转换为从选择器生成节点集合的函数,并允许您将这些函数链接为该集合上的方法。

我会用我自己的方法添加一个答案,但我很好奇其他人会如何处理这个问题。


1这个库早于document.querySelector/querySelectorAll,我假设它是 DOM 方法的很大一部分灵感。

  • ping @WillD。您可以发布您的 Fiddle 代码作为答案吗?

标签: javascript jquery fluent


【解决方案1】:

这是我第一次尝试解决这个问题:

const $ = ((fns = {
    color: (color) => (node) => node .style .color = color,
    size: (size, units = '') => (node) => node .style .fontSize = size + units,
    click: (fn) => (node) => node .addEventListener ('click', fn)
    // more here
  }, 
  nodes = Symbol(),
  proto = Object .fromEntries (
    Object .entries (fns) .map (([k, fn]) => [
      k, 
      function (...args) {this [nodes] .forEach ((node) => fn (...args) (node)); return this}
    ])
  )
) => (selector) => 
  Object .create (proto, {[nodes]: {value: document .querySelectorAll (selector)}})
) ()

$ ('.nav li:first-child a') 
  .color ('red')
  .size (2, 'em')
  .click ((evt) => {
    evt .preventDefault (); 
    console .log (`"${evt.target.closest('a').textContent}" link clicked`)
  })
div {display: inline-block; width: 40%}
<div>
  <h3>First nav menu</h3>
  <ul class="nav">
    <li><a href="#">foo</a></li>
    <li><a href="#">bar</a></li>
    <li><a href="#">baz</a></li>
  </ul>
</div>
<div>
  <h3>Second nav menu</h3>
  <ul class="nav">
    <li><a href="#">qux</a></li>
    <li><a href="#">corge</a></li>
    <li><a href="#">grault</a></li>
  </ul>
</div>

最重要的是,功能非常简单:

{
  color: (color) => (node) => node .style .color = color,
  size: (size, units = '') => (node) => node .style .fontSize = size + units,
  click: (fn) => (node) => node .addEventListener ('click', fn),
}
  

我们可以添加任意数量的它们。这些被转化为方法一个对象——我们将用作原型——通过创建接受相同参数的函数,然后对我们集合中的每个节点,我们用相同的参数调用这个函数,并最终用节点调用结果函数返回我们相同的对象。

整个过程返回一个函数,该函数接受一个选择器,用它调用querySelectorAll,并用我们给定的原型将生成的NodeList 包装在一个对象中。

虽然我没有尝试过,但我认为这很容易扩展以允许插件架构。允许可能改变 NodeList 或返回完全不同的函数会更加困难。那时,我们可能会从 jQuery 源代码中寻找灵感。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-01
    • 2010-09-19
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多