前言
队列和栈很像,只是用了不同的原则。队列是遵循先进先出(FIFO)原则的一组有序的的项,队列在尾部添加新元素,从顶部移除元素。最新添加的元素必须必须排队在队列的,末尾。可以想象食堂排队买饭的样子。
一、创建队列
1、创建一种数据结构来保存队列里面的数据,这里选择数组
2、声明一些栈的方法
enqueue(element(s)) : 添加一个或一些元素到队列的末尾
dequeue() : 移除队列第一个的元素(就是排队在最前面的),同时返回被移除的元素。
front() : 返回队列第一个的元素(最先被添加的),仅仅是返回,不做任何修改。
isEmpty() : 如果队列里面没有任何元素就返回true,否者为false。
clear() : 清除队列里面的元素。
size() : 返回队列里面的个数。
function Queue(){ var items = []; this.enqueue= function(element){ items.push(element); } this.dequeue = function(){ return items.shift(); } this.front = function(){ return items[0]; } this.isEmpty = function(){ return items.length == 0; } this.clear = function(){ items = []; } this.size = function(){ return items.length; } this.print = function(){ console.log(items.toString()); } } var queue = new Queue(); console.log(queue.isEmpty()); queue.enqueue('leaf'); queue.enqueue('jack') console.log(queue.size()); console.log(queue.dequeue()); console.log(queue.isEmpty()); console.log(queue.size());
二、循环队列 --击鼓传花游戏
var nameList = ['leaf', 'jack', 'jef', 'rose', 'red', 'mandy', 'hardy', 'mark' ]; function cyclicGame(nameList){ var queue = new Queue(), len = nameList.length; for(var i=0 ; i<len; i++){ queue.enqueue(nameList[i]); } var weedOutName = ''; while(queue.size()>1){ for(var i=0; i<Math.floor(Math.random()*len); i++){ queue.enqueue(queue.dequeue());//把第一个删除了添加后面 } weedOutName = queue.dequeue(); console.log(weedOutName + '第'+ (len-queue.size()) +'轮被淘汰了!'); } return console.log(queue.dequeue() + '是最后的胜利者!');//最后一个元素,胜利者 } cyclicGame(nameList);
三、事件队列管理
JS的执行环境是单线程的,一次只能完成一个任务,其任务的调度方式就是排队,这就在医院挂号一样,前面的那个人没有搞定,你就只能站在后面排队等着。在事件队列中加一个延时,这样的问题便可以得到缓解,下面用代码模拟这个情景。
var Queue = { //保存队列信息 items : [], //添加到队列 enqueue : function(executeQueue){ //添加到队列,如果不是函数或者数字的不处理 if(!/function|number/.test(typeof executeQueue)){ return; } Queue.items.push(executeQueue); //返回自身的引用 return Queue; }, //执行队列 executeQueue : function(){ //删除队列第一个元素并返回它 var dequeue = Queue.items.shift(); //如果队列为空的,直接返回 if(!dequeue){ return; } //如果是函数,直接执行,然后继续执行executeQueue if(typeof dequeue === "function"){ dequeue(); Queue.executeQueue(); return; } //如果是数字,该数字作为延迟的时间, 延迟executeQueue setTimeout(function(){ Queue.executeQueue(); //console.log(dequeue); }, dequeue); } }; //测试 Queue //添加事件 .enqueue(function(){ console.log('3秒之后执行第1个'); }) .enqueue(3000) .enqueue(function(){ console.log('3秒之后执行第2个'); }) .enqueue(3000) .enqueue(function(){ console.log('3秒之后执行第3个'); }) //执行事件 .executeQueue();
四、看一个栗子--实现layMan的功能
实现:
lazyMan('leaf').eat('苹果').sleep(2).eat('雪梨').sleep(3).eat('香蕉')
采用事件队列的思想,根据队列的先后循序执行;
function LazyMan(name) { if(this.constructor !== LazyMan) { return new LazyMan(name) } //保存队列 this.tasks = []; var self = this; var fn =(function(n){ var name = n; return function(){ console.log("Hi! This is " + name + "!"); self.next(); } })(name); this.tasks.push(fn); //执行队列里面第一个事件 setTimeout(function(){ self.next(); }, 0); } /* 事件调度函数 */ LazyMan.prototype.next = function() { var fn = this.tasks.shift(); fn && fn(); } LazyMan.prototype.eat = function(name) { var self = this; var fn =(function(name){ return function(){ console.log("Eat " + name + "~"); self.next() } })(name); this.tasks.push(fn); return this; // 实现链式调用 } LazyMan.prototype.sleep = function(time) { var self = this; var fn = (function(time){ return function() { setTimeout(function(){ console.log("Wake up after " + time + "s!"); self.next(); }, time * 1000); } })(time); this.tasks.push(fn); return this; } LazyMan.prototype.sleepFirst = function(time) { var self = this; var fn = (function(time) { return function() { setTimeout(function() { console.log("Wake up after " + time + "s!"); self.next(); }, time * 1000); } })(time); this.tasks.unshift(fn); return this; } /* 封装 */ function lazyMan(name){ return new LazyMan(name); } //调用 lazyMan('leaf').eat('苹果').sleep(2).eat('雪梨').sleep(3).eat('香蕉')