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

如何设计带私有状态且可多次实例化的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:05:12