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

如何不使用继承为JavaScript类扩展其他类的分页方法?

复用分页逻辑的非继承实现方案

问题背景

现有播放列表管理应用中,Paginated类通过继承ApiPlaylist实现了分页加载逻辑,但现在需要让ApiPlaylist、ApiPost、ApiPage等多个类复用该分页机制,且不使用继承方式。


方案1:Mixin(混入)注入分页逻辑

Mixin是JS/TS中实现跨类行为复用的常用方案,它可以将分页相关方法注入到任意目标类中,无需继承关系。

实现代码

首先定义封装分页逻辑的Mixin函数:

// 分页逻辑Mixin:接收目标类作为参数,返回带有分页方法的子类
function paginatedMixin(Base) {
  return class extends Base {
    #iterations = 0;
    #endReached = false;

    async loadAll() {
      while (!this.#endReached) {
        await this.loadNext();
      }
    }

    async loadNext() {
      this.#iterations++;
      // 调用目标类的loadPage方法,传递当前分页参数
      await this.loadPage({ page: this.#iterations });
      // 依赖目标类实现hasMore方法,判断是否还有数据
      this.#endReached = !await this.hasMore();
    }
  };
}

将Mixin应用到各个API类:

// 基础Playlist类
class Playlist {
  tracks = [];
}

// ApiPlaylist通过Mixin获得分页能力
class ApiPlaylist extends paginatedMixin(Playlist) {
  async loadPage(paginationSettings) {
    // 实际API请求逻辑
    const pageTracks = await fetch(`/api/tracks?page=${paginationSettings.page}`).then(res => res.json());
    this.tracks = [...this.tracks, ...pageTracks];
  }

  async hasMore() {
    // 自定义判断逻辑:示例为总条数小于100时继续加载
    return this.tracks.length < 100;
  }
}

// ApiPost类同样应用Mixin
class ApiPost {
  posts = [];

  async loadPage(paginationSettings) {
    const pagePosts = await fetch(`/api/posts?page=${paginationSettings.page}`).then(res => res.json());
    this.posts = [...this.posts, ...pagePosts];
  }

  async hasMore() {
    return this.posts.length < 50;
  }
}
const PaginatedApiPost = paginatedMixin(ApiPost);

// 使用示例
(async () => {
  const playlist = new ApiPlaylist();
  await playlist.loadAll();

  const posts = new PaginatedApiPost();
  await posts.loadAll();
})();

方案2:组合模式(委托)

将分页逻辑封装为独立的Paginator类,其他API类通过持有该类的实例,将分页方法委托给它实现复用。

实现代码

// 独立分页管理器:封装所有分页逻辑
class Paginator {
  #iterations = 0;
  #endReached = false;
  #target; // 持有目标API类实例

  constructor(target) {
    this.#target = target;
  }

  async loadAll() {
    while (!this.#endReached) {
      await this.loadNext();
    }
  }

  async loadNext() {
    this.#iterations++;
    await this.#target.loadPage({ page: this.#iterations });
    this.#endReached = !await this.#target.hasMore();
  }
}

// ApiPlaylist类通过组合使用分页功能
class Playlist {
  tracks = [];
}

class ApiPlaylist extends Playlist {
  #paginator;

  constructor() {
    super();
    this.#paginator = new Paginator(this);
  }

  // 委托分页方法给Paginator实例
  loadAll() {
    return this.#paginator.loadAll();
  }

  loadNext() {
    return this.#paginator.loadNext();
  }

  async loadPage(paginationSettings) {
    const pageTracks = await fetch(`/api/tracks?page=${paginationSettings.page}`).then(res => res.json());
    this.tracks = [...this.tracks, ...pageTracks];
  }

  async hasMore() {
    return this.tracks.length < 100;
  }
}

// ApiPost类同理
class ApiPost {
  posts = [];
  #paginator;

  constructor() {
    this.#paginator = new Paginator(this);
  }

  loadAll() {
    return this.#paginator.loadAll();
  }

  loadNext() {
    return this.#paginator.loadNext();
  }

  async loadPage(paginationSettings) {
    const pagePosts = await fetch(`/api/posts?page=${paginationSettings.page}`).then(res => res.json());
    this.posts = [...this.posts, ...pagePosts];
  }

  async hasMore() {
    return this.posts.length < 50;
  }
}

// 使用示例
(async () => {
  const playlist = new ApiPlaylist();
  await playlist.loadAll();

  const posts = new ApiPost();
  await posts.loadAll();
})();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 19:07:19