用栈模拟队列:算法题背后的原型链课

一、一个问题,两个维度

"用两个栈模拟队列"是经典面试题。但它的价值不止于算法------当代码写出来的时候,JavaScript 的原型机制已经隐含在其中。

这道题的独特之处在于:算法的实现过程同时也是理解语言特性的最佳入口。


二、两个栈模拟队列的核心思路

栈是 FILO,队列是 FIFO,方向正好相反。但两个栈配合就能解决:一个专门接收新元素(输入栈),另一个专门处理出队(输出栈)。

关键操作是"倒数据"------当输出栈为空时,把输入栈的所有元素依次弹出并压入输出栈。由于栈的后进先出特性,倒一次之后,最早进入输入栈的元素就到了输出栈的栈顶。

javascript 复制代码
const MyQueue = function () {
    this.stack1 = []; // 输入栈
    this.stack2 = []; // 输出栈
};

MyQueue.prototype.push = function (x) {
    this.stack1.push(x);
};

MyQueue.prototype.pop = function () {
    if (this.stack2.length === 0) {
        while (this.stack1.length > 0) {
            this.stack2.push(this.stack1.pop());
        }
    }
    return this.stack2.pop();
};

MyQueue.prototype.peek = function () {
    if (this.stack2.length === 0) {
        while (this.stack1.length > 0) {
            this.stack2.push(this.stack1.pop());
        }
    }
    return this.stack2[this.stack2.length - 1];
};

MyQueue.prototype.empty = function () {
    return this.stack1.length === 0 && this.stack2.length === 0;
};

push 永远是 O(1),pop 和 peek 均摊 O(1)------每个元素最多被倒两次。


三、prototype 在这段代码里做了什么

上面用 function + prototype 而非 class 来写。这不是怀旧,而是故意暴露 JavaScript 面向对象的底层机制。

MyQueue.prototype 是一个对象,存放所有实例共享的方法。通过 new MyQueue() 创建的每个实例,内部通过 __proto__ 链接到 MyQueue.prototype,从而访问这些方法。

这背后是一个三层三角关系:

  • 构造函数 MyQueueprototype 属性,指向原型对象
  • 原型对象有 constructor 属性,指回构造函数
  • 实例有 __proto__ 属性,指向原型对象

ES6 的 class 只是这个三层关系的语法糖------typeof MyQueue 不管用哪种写法都是 "function"


四、new 操作符的四步流程

new MyQueue() 的执行过程不是魔法,是四步操作:

  1. 创建一个空对象 {}
  2. 将这个对象的 __proto__ 指向构造函数的 prototype
  3. 执行构造函数,this 指向新对象,添加属性
  4. 返回这个对象(除非构造函数显式返回了一个对象)
javascript 复制代码
function myNew(constructor, ...args) {
    const obj = {};
    Object.setPrototypeOf(obj, constructor.prototype);
    const result = constructor.apply(obj, args);
    return result instanceof Object ? result : obj;
}

理解这四步之后,const queue = new MyQueue() 不再是一个黑盒。


五、总结

一道算法题加一个语言特性,二者的交汇点不是巧合:

  1. 用栈模拟队列考察的是数据结构组合思维
  2. prototype 机制是 JavaScript 面向对象的基石
  3. new 操作符 = 创建对象 → 绑定原型 → 初始化 → 返回

当你能从算法实现中看到语言底层的运转时,这两方面的知识就不再是孤立的两本书,而是同一块知识地图的不同区域。

相关推荐
零陵上将军_xdr2 小时前
后端转全栈学习-Day5-JavaScript 基础-3
开发语言·javascript·学习
ssshooter2 小时前
为什么父元素的高度不会包含子元素的 margin?
前端·javascript·面试
Goodbye2 小时前
JavaScript 同步与异步编程深度解析
javascript
Amo Xiang2 小时前
JS 逆向系统进阶路线:专栏总纲与文章导航
javascript·js逆向·前端加密·爬虫逆向·反爬虫
●VON2 小时前
AtomGit Flutter鸿蒙客户端:主题系统
javascript·flutter·华为·跨平台·harmonyos·鸿蒙
烬羽3 小时前
JS 单线程为什么不卡?一文吃透同步异步、Event Loop 和 Promise
javascript·面试
葬送的代码人生3 小时前
JavaScript 数组完全指南:从入门到实战
前端·javascript·算法
用户938515635073 小时前
深入理解 JavaScript 同步与异步:从单线程到事件循环与 Promise
前端·javascript
云水一下4 小时前
Vue.js从零到精通系列(一):初识Vue——背景、环境与第一个应用
前端·javascript·vue.js