在JavaScript开发中,组合模式常用来描述具有树形结构的对象群体,例如文件系统中的文件夹与文件、页面中的容器组件与基础组件。它的核心思想是让单个对象和组合对象实现同一组接口,使调用方无需关心当前处理的是叶子还是容器。

什么是组合模式中的组合结构
组合结构由两种角色构成:叶子节点和容器节点。容器节点可以包含子节点,子节点既可以是叶子,也可以是另一个容器,从而形成递归的树形关系。两者都暴露相同的方法,例如operation()或getSize(),调用方统一调用即可。
基础接口设计
我们先定义一个抽象组件,规定所有节点都要实现的方法。在JS中可以用父类或接口约定来模拟。
// 抽象组件:规定叶子与容器都要实现的方法
class Component {
constructor(name) {
this.name = name;
}
// 子类必须实现具体操作
operation() {
throw new Error('请实现operation方法');
}
// 容器才可能用到,叶子可留空或抛错
add(child) {
throw new Error('叶子节点不支持add');
}
remove(child) {
throw new Error('叶子节点不支持remove');
}
}
叶子节点结构
叶子节点不包含子元素,它的operation直接完成自己的任务。
// 叶子节点:文件
class Leaf extends Component {
operation() {
return '文件: ' + this.name;
}
}
容器节点结构
容器节点维护子组件列表,并在自身操作中递归调用子节点,这就是组合结构的关键。
// 容器节点:文件夹
class Composite extends Component {
constructor(name) {
super(name);
this.children = [];
}
add(child) {
this.children.push(child);
}
remove(child) {
const idx = this.children.indexOf(child);
if (idx > -1) this.children.splice(idx, 1);
}
operation() {
let result = '文件夹: ' + this.name + 'n';
for (let child of this.children) {
result += child.operation() + 'n';
}
return result;
}
}
组合结构的运行示例
下面演示如何把叶子和容器拼成树,并用统一方式触发操作。
const root = new Composite('根目录');
const file1 = new Leaf('a.txt');
const folder1 = new Composite('子目录');
const file2 = new Leaf('b.txt');
folder1.add(file2);
root.add(file1);
root.add(folder1);
console.log(root.operation());
组合结构的优势与注意点
- 调用方代码简洁,不需要用if判断对象是叶子还是容器。
- 新增节点类型时,只要遵循同一接口就不会破坏遍历逻辑。
- 如果树层级过深,递归操作要注意调用栈溢出风险。
组合模式并不是要消灭类型区分,而是把区分隐藏在结构内部,对外提供一致的表面。
小结
JS实现组合模式的重点在于设计统一的组件接口,让容器与叶子以相同方式被使用。组合结构本质是一棵可递归的对象树,借助add与operation的递归,就能用少量代码表达复杂的层级关系。
组合模式JavaScript树形结构修改时间:2026-07-27 08:36:18