如何不使用继承为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
相关产品推荐
相关产品推荐

