如何设计带私有状态且可多次实例化的JS对象?原型式priority-queue实现问询
嘿,针对你提出的两个问题,我来一步步给你拆解解决方案,尤其是基于原型实现带私有状态的优先队列这块~
一、如何设计具备私有状态且可被多次实例化的JavaScript对象?
这里有几种主流方案,各有优缺点,你可以根据场景选择:
1. 工厂函数+闭包(简单直观)
通过工厂函数创建实例,私有状态存于闭包中,外部无法直接访问:
function createCounter() { // 私有状态,仅内部方法能访问 let count = 0; return { increment() { count++; return count; }, getCount() { return count; } }; } // 每个实例拥有独立的私有状态 const counter1 = createCounter(); const counter2 = createCounter(); counter1.increment(); // 返回 1 counter2.getCount(); // 返回 0
优点:封装性强,语法简单;缺点:每个实例的方法都是独立副本,内存开销略大。
2. ES6类+私有字段(原生支持,语法清晰)
ES6+ 原生支持带#前缀的私有字段,是目前最推荐的方案之一:
class Counter { // 私有字段,仅类内部可访问 #count = 0; increment() { this.#count++; return this.#count; } getCount() { return this.#count; } } const counter1 = new Counter(); const counter2 = new Counter();
优点:原生语法,方法共享在原型上(内存高效),封装性强;缺点:需要环境支持ES6+(现在主流环境都没问题)。
3. WeakMap存储私有状态(兼容旧环境,内存友好)
用WeakMap关联实例和私有状态,外部无法访问WeakMap时,状态就是私有的:
// 私有状态容器,外部不可见 const privateState = new WeakMap(); class Counter { constructor() { // 给当前实例绑定私有状态 privateState.set(this, { count: 0 }); } increment() { const state = privateState.get(this); state.count++; return state.count; } getCount() { return privateState.get(this).count; } }
优点:方法共享在原型,内存高效;实例销毁时WeakMap会自动回收对应状态,避免内存泄漏;缺点:需要额外维护WeakMap容器,语法稍繁琐。
二、基于原型实现封装的优先队列(支持多实例)
针对你的需求——用基于原型的设计实现优先队列,同时保证实例状态私有不共享、API封装,这里给你两种可行方案:
方案1:WeakMap+原型+工厂函数(适配暴露模块模式)
完美契合你提到的「暴露模块模式+原型方法共享+私有状态封装」需求:
// 存储每个实例的私有队列状态,外部不可访问 const privateQueue = new WeakMap(); // 原型对象,存放所有实例共享的方法 const PriorityQueueProto = { enqueue(element, priority) { const queue = privateQueue.get(this); // 按优先级插入元素的逻辑 let added = false; for (let i = 0; i < queue.length; i++) { if (priority < queue[i].priority) { queue.splice(i, 0, { element, priority }); added = true; break; } } if (!added) queue.push({ element, priority }); }, dequeue() { return privateQueue.get(this).shift()?.element; }, peek() { return privateQueue.get(this)[0]?.element; }, isEmpty() { return privateQueue.get(this).length === 0; }, size() { return privateQueue.get(this).length; } }; // 工厂函数,负责创建实例并初始化私有状态 function createPriorityQueue() { const instance = Object.create(PriorityQueueProto); privateQueue.set(instance, []); return instance; } // 使用示例 const pq1 = createPriorityQueue(); pq1.enqueue("task1", 2); pq1.enqueue("task2", 1); const pq2 = createPriorityQueue(); pq2.enqueue("taskA", 3); console.log(pq1.dequeue()); // 输出 "task2" console.log(pq2.dequeue()); // 输出 "taskA"
这个方案里:
- 所有实例共享原型上的方法,节省内存;
- 每个实例的队列状态存在WeakMap中,完全封装,不会被外部访问或实例间共享;
- 工厂函数
createPriorityQueue负责实例化,符合你提到的「newObj工厂方法」思路。
方案2:ES6类(简洁直观,现代首选)
如果你的环境支持ES6+,用类实现会更简洁,本质也是基于原型的设计:
class PriorityQueue { // 私有队列字段,每个实例独立拥有 #queue = []; enqueue(element, priority) { let added = false; for (let i = 0; i < this.#queue.length; i++) { if (priority < this.#queue[i].priority) { this.#queue.splice(i, 0, { element, priority }); added = true; break; } } if (!added) this.#queue.push({ element, priority }); } dequeue() { return this.#queue.shift()?.element; } peek() { return this.#queue[0]?.element; } isEmpty() { return this.#queue.length === 0; } size() { return this.#queue.length; } } // 实例化使用 const pq1 = new PriorityQueue(); const pq2 = new PriorityQueue();
类的方法默认挂载在原型上,私有字段#queue保证每个实例的状态独立且封装,语法清晰易维护。
内容的提问来源于stack exchange,提问作者Gishu
相关产品推荐
相关产品推荐

