当栈决定排队:论数据结构的“易容术”与JavaScript的“无类哲学”
当栈决定排队:论数据结构的“易容术”与JavaScript的“无类哲学”
在这个世界上,有些事情是注定要反过来的。比如,你越想睡着,脑子就越清醒;你越想用栈(Stack)来实现队列(Queue),就越觉得计算机科学家们在故意为难你。栈是“先进后出”的倔老头,队列是“先进先出”的守序员,让一个喜新厌旧的家伙去模仿一个念旧重情的家伙,这听起来像是让一个吃货把最后一块蛋糕留给明天——简直是在挑战生物本能。
然而,这正是编程的魅力所在:我们总是在用有限的工具,去模拟无限的可能。今天,我们就来聊聊如何用两个“死脑筋”的栈,去模拟一个“按部就班”的队列,顺便揭开JavaScript那套“万物皆对象,唯独没有类”的神秘面纱。
一、用两个“倔老头”演一出“守序剧”
想象一下,你有两个箱子(栈),只能从顶部放东西和拿东西。现在,你的任务是让东西从第一个箱子进去,却能按照进入的顺序从第二个箱子出来——这就好比你把一串糖葫芦塞进第一个箱子,却希望它们从第二个箱子按顺序一个个冒出来,而不是倒着来。
聪明的你可能会想:“那我把第一个箱子倒进第二个箱子,不就反过来了?”恭喜你,答对了!这正是“用栈模拟队列”的核心智慧。
具体操作如下:
入队(push):别动第二个箱子,把所有新来的元素都塞进第一个箱子。此时,第一个箱子像个贪吃蛇,把所有东西都吞了进去,顺序是反的。
出队(pop):如果第二个箱子空了,就把第一个箱子的所有东西一个个拿出来,塞进第二个箱子。这一倒腾,顺序就正过来了!然后,从第二个箱子顶部拿走一个,就是最早进来的那个。
偷窥(peek):同上,只是不拿走,只看看。
判空(empty):两个箱子都空了,才算真空。
这就像你有两个秘书,一个只负责记录(输入栈),一个只负责汇报(输出栈)。当老板问你要最早的记录时,负责记录的秘书把笔记倒着交给汇报秘书,汇报秘书再正着念出来。虽然绕了点,但结果是对的。这告诉我们:有时候,解决问题的方法不是硬碰硬,而是找个“中间商”转一手。
二、JavaScript:一个“没有类”的江湖
如果说数据结构的模拟是“易容术”,那JavaScript的面向对象就是“无招胜有招”。在其他语言里,你要先画个图纸(类),再盖房子(实例)。而在JavaScript的世界里,根本没有“图纸”这回事——它信奉的是“原型式面向对象”,一切皆对象,没有类。
这听起来有点像“先有鸡还是先有蛋”的哲学问题。在JavaScript里,函数也是对象,而且是“一等公民”。你可以把函数当参数传,当返回值用,甚至给它加属性。它不像Java那样端着架子,而是随性得很。
当你用new关键字调用一个构造函数时,JavaScript在背后默默做了三件事:
创建一个空对象,把this指向它。
执行构造函数,给this加属性。
把这个对象的__proto__指向构造函数的prototype,然后返回它。
这个__proto__就是传说中的“原型链”。它像一条脐带,连接着实例和原型对象。当你访问一个对象的属性时,它先在自己身上找,找不到就顺着__proto__往上找,直到找到Object.prototype,再往上就是null——宇宙的尽头。
所以,JavaScript的继承不是“复制图纸”,而是“建立链接”。它没有“类”的概念,只有“对象”的传承。这就像一个家族,没有族谱(类),只有长辈(原型)和晚辈(实例),晚辈遇到不懂的事就问长辈,长辈不懂就问祖宗,直到问到老祖宗Object。
三、结语:在反常中寻找正常
回到最初的问题:为什么要用栈模拟队列?为什么要理解JavaScript的原型链?
因为编程的本质,不是记住规则,而是理解规则背后的逻辑。栈和队列的转换,教会我们“逆向思维”;JavaScript的原型链,教会我们“动态链接”。
在这个看似混乱的世界里,我们总能找到秩序。就像用两个反向的栈,能模拟出正向的队列;用一个没有类的语言,能构建出复杂的世界。这或许就是计算机科学的浪漫:在有限的规则里,创造出无限的可能。
所以,下次当你看到__proto__和prototype绕得头晕时,不妨想想那两个倒腾糖葫芦的箱子——一切反常,终将归于正常。
更多推荐

所有评论(0)