You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

创建类jQuery链式调用(Fluent Interface)接口的核心技术有哪些?

实现类jQuery链式调用接口的几种方案

咱们来一步步拆解怎么实现类jQuery的链式调用接口。核心逻辑其实很清晰:把选中的DOM节点集合封装起来,让每个操作方法执行完都返回当前的封装对象/实例,这样就能一直链式调用下去了。下面给你几种不同的实现方案,从经典到现代都有:

1. 经典构造函数+原型链方案(jQuery早期思路)

这是jQuery最初采用的实现方式,利用原型链共享方法,能有效节省内存开销:

// 构造函数:处理选择器,初始化节点集合
function $(selector) {
  // 自动补全new,避免用户忘记写new关键字
  if (!(this instanceof $)) {
    return new $(selector);
  }
  // 把NodeList转成数组,方便后续遍历操作
  this.nodes = Array.from(document.querySelectorAll(selector));
}

// 在原型上挂载操作方法,所有实例共享这些方法
$.prototype.color = function(color) {
  this.nodes.forEach(node => {
    node.style.color = color;
  });
  return this; // 关键:返回当前实例,支持链式调用
};

$.prototype.size = function(size, unit = 'px') {
  this.nodes.forEach(node => {
    node.style.fontSize = `${size}${unit}`;
  });
  return this;
};

$.prototype.click = function(callback) {
  this.nodes.forEach(node => {
    // 先移除旧事件,避免重复绑定(可选优化)
    node.removeEventListener('click', callback);
    node.addEventListener('click', callback);
  });
  return this;
};

使用方式和你给出的示例完全一致:

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

2. ES6 Class方案(现代简洁写法)

用ES6的Class语法封装,结构更清晰,可读性更强,本质和原型链方案逻辑一致:

class DOMWrapper {
  constructor(selector) {
    this.nodes = Array.from(document.querySelectorAll(selector));
  }

  color(color) {
    this.nodes.forEach(node => node.style.color = color);
    return this;
  }

  size(size, unit = 'px') {
    this.nodes.forEach(node => node.style.fontSize = `${size}${unit}`);
    return this;
  }

  click(callback) {
    this.nodes.forEach(node => {
      node.removeEventListener('click', callback);
      node.addEventListener('click', callback);
    });
    return this;
  }
}

// 封装成工厂函数,和jQuery的调用方式对齐
const $ = selector => new DOMWrapper(selector);

这个方案的优势是语法直观,类的结构让方法组织更清晰,适合需要扩展更多功能的场景。

3. 工厂函数直接返回对象(轻量简洁方案)

如果你的需求比较简单,不需要大量扩展方法,这个方案最直观,不需要理解原型链或者类的概念:

const $ = selector => {
  // 获取并存储节点集合(转成数组)
  const nodes = Array.from(document.querySelectorAll(selector));
  
  // 返回包含所有操作方法的对象,每个方法通过闭包访问nodes
  return {
    color: function(color) {
      nodes.forEach(node => node.style.color = color);
      return this; // 返回当前对象,实现链式调用
    },
    size: function(size, unit = 'px') {
      nodes.forEach(node => node.style.fontSize = `${size}${unit}`);
      return this;
    },
    click: function(callback) {
      nodes.forEach(node => {
        node.removeEventListener('click', callback);
        node.addEventListener('click', callback);
      });
      return this;
    },
    // 可选:添加获取原始节点的方法
    get: function(index) {
      return index === undefined ? nodes : nodes[index];
    }
  };
};

这个方案的优点是简单易懂,不需要复杂的原型或类逻辑,但缺点是每个实例都会创建一套新的方法副本,内存开销比前两种方案高,适合功能较少的小型工具。


核心要点总结

不管用哪种方案,实现链式调用的关键都是两点:

  1. 封装节点集合:把选中的DOM节点(转成数组)存储起来,方便后续批量操作
  2. 方法返回自身:每个DOM操作方法执行完成后,返回当前的实例/对象,这样就能继续调用下一个方法

你还可以根据需求做一些优化,比如:

  • 支持直接传入DOM元素或元素数组,而不只是选择器字符串
  • 处理选择器为空的情况,避免遍历空集合时报错
  • 事件绑定的优化,比如使用事件委托减少绑定次数

内容的提问来源于stack exchange,提问作者Scott Sauyet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 17:48:13