如何让自定义类在Pug中支持each语法直接迭代
问题原因
Pug 的 each 迭代器不依赖ES6的Symbol.iterator迭代协议,也不会调用自定义的forEach方法,它的内部遍历逻辑是固定的:
- 先判断迭代目标是否包含
length属性,如果有就按类数组处理,遍历索引0到length-1,直接通过目标[索引]的方式取对应元素 - 如果没有
length属性,就按普通对象处理,遍历所有可枚举的键名,取对应键值
你的自定义类只有items、Symbol.iterator、forEach三个可访问成员,没有length属性,也不支持索引访问,所以Pug会把它当成普通对象遍历键名,自然不会得到你想要的结果。
解决方案
只要给你的自定义类实现类数组接口即可,不需要修改原有逻辑,有两种常用实现方式:
方案1:手动实现类数组属性和访问器
在原有类基础上添加length getter,同时实现索引访问代理:
class MyPaginator<T>{ items: T[] // 新增:length getter,返回items的长度 get length() { return this.items.length } // 新增:TS索引签名定义,可选 [index: number]: T constructor(items: T[]) { this.items = items // 如果items初始化后不会动态变化,可以直接把元素挂载到实例索引上 items.forEach((item, index) => { this[index] = item }) // 如果items会动态更新,用Proxy做动态代理更合适,替换上面的循环即可 // return new Proxy(this, { // get(target, prop) { // if (typeof prop === 'string' && /^\d+$/.test(prop)) { // return target.items[Number(prop)] // } // return Reflect.get(target, prop) // } // }) } [Symbol.iterator](){ return this.items[Symbol.iterator](); }; forEach(callback: (item: T, index: number) => void): void { return this.items.forEach(callback); } }
方案2:直接继承Array类(更简单)
如果你的分页器没有和Array原型冲突的自定义逻辑,直接继承Array是成本最低的方案,天生自带类数组的所有特性,原有迭代逻辑也可以正常保留:
class MyPaginator<T> extends Array<T>{ // 可自定义分页相关扩展属性 pageNum: number pageSize: number total: number constructor(pageNum: number, pageSize: number, total: number, items: T[]) { // 把分页数据传给父类Array构造函数 super(...items) this.pageNum = pageNum this.pageSize = pageSize this.total = total } // 其他自定义方法不需要修改 }
两种方案实现后,你就可以直接在Pug中用each element in paginator直接迭代实例,不需要加.items后缀。
内容的提问来源于stack exchange,提问作者Levy Barbosa
相关产品推荐
相关产品推荐

