這道js的面試題,是這樣的,頁面上有一個按鈕,一個ul,點擊按鈕的時候,每隔1秒鐘向ul的後面追加一個li, 一共追加10個,li的內容從0開始技術( 0, 1, 2, ....9 ),首先咱們用閉包封裝一個建立li元素的函數.javascript
1 var create = (function(){ 2 var count = 0; 3 return function(){ 4 var oLi = document.createElement( "li" ); 5 oLi.innerHTML = count++; 6 return oLi; 7 } 8 })();
頁面上的2個元素:java
<input type="button" value="點我">
<ul id="box"></ul>
js代碼:面試
1 var oBtn = document.querySelector( "input" ); 2 var oBox = document.querySelector( "#box" ); 3 4 var create = (function(){ 5 var count = 0; 6 return function(){ 7 var oLi = document.createElement( "li" ); 8 oLi.innerHTML = count++; 9 return oLi; 10 } 11 })(); 12 13 oBtn.onclick = function(){ 14 setTimeout(function(){ 15 oBox.appendChild( create() ); 16 setTimeout( function(){ 17 oBox.appendChild( create() ); 18 setTimeout( function(){ 19 oBox.appendChild( create() ); 20 }, 1000 ); 21 }, 1000 ); 22 }, 1000 ); 23 }
點擊按鈕的時候,用回調函數嵌套方式,這裏我加入3個li,就已經快受不了了,這就是javascript著名的回調地獄,那麼在這裏,我用循環簡化一下:算法
1 var oBtn = document.querySelector("input"); 2 var oBox = document.querySelector("#box"); 3 var timer = oNode = null; 4 var create = (function () { 5 var count = 0; 6 return function () { 7 var oLi = document.createElement("li"); 8 oLi.innerHTML = count++; 9 return oLi; 10 } 11 })(); 12 function add(){ 13 oNode = oBox.appendChild( create() ); 14 if ( oNode.innerHTML < 9 ) { 15 timer = setTimeout( add, 1000 ); 16 }else { 17 clearTimeout( timer ); 18 } 19 } 20 oBtn.onclick = function () { 21 add(); 22 }
恩,確實簡化了,可是這種面向過程的方式,耦合性太強,下面呢,我就把這個封裝成一個通用隊列數據結構
第一步:封裝一個隊列,包含( 入列,出列),隊列的特色(先進先出,若是你不懂這個,須要去補下基本的數據結構與算法內容)閉包
1 var Queue = function () { 2 this.list = [] 3 } 4 Queue.prototype = { 5 constructor: Queue, 6 enQueue: function ( fn ) { 7 this.list.push( fn ); 8 return this; 9 }, 10 deQueue: function () { 11 var fn = this.list.shift() || function () {}; 12 fn.apply( this, arguments ); 13 } 14 }
咱們來使用它:app
1 var oQ = new Queue(); 2 oQ.enQueue( function(){ 3 console.log( 'ghostwu1' ); 4 }).enQueue( function(){ 5 console.log( 'ghostwu2' ); 6 }).enQueue( function(){ 7 console.log( 'ghostwu3' ); 8 }).deQueue(); 9 while( oQ.list.length ){ 10 oQ.deQueue(); 11 }
第二步、雖然咱們如今實現了一個隊列,可是,這玩意是同步的,接下來繼續改形成異步的:異步
1 var oQ = new Queue(); 2 oQ.enQueue( function(){ 3 var _this = this; 4 console.log( 'ghostwu1' ); 5 setTimeout( function(){ _this.deQueue(); }, 1000 ); 6 }).enQueue( function(){ 7 var _this = this; 8 console.log( 'ghostwu2' ); 9 setTimeout( function(){ _this.deQueue(); }, 1000 ); 10 }).enQueue( function(){ 11 var _this = this; 12 console.log( 'ghostwu3' ); 13 setTimeout( function(){ _this.deQueue(); }, 1000 ); 14 }).deQueue(); 第三步、這樣就實現了一個異步隊列, 這裏有個小東西要注意下,把this保存下來,由於定時器的this指向的是window.另外在封裝deQueue(出列)函數時,必定要給個空函數,不然出列完了以後,會報錯,可是這玩意仍是有耦合性,繼續改造: 1 <input type="button" value="點我"> 2 <ul id="box"></ul> 3 <script> 4 var Utils = { 5 isFunction: function (a) { 6 return Object.prototype.toString.call(a) === '[object Function]'; 7 }, 8 isNumber: function (a) { 9 return typeof a === 'number'; 10 } 11 }; 12 var Queue = function () { 13 this.list = [] 14 } 15 Queue.prototype = { 16 constructor: Queue, 17 enQueue: function (fn) { 18 this.list.push(fn); 19 return this; 20 }, 21 delay: function (time) { 22 this.list.push(time); 23 return this; 24 }, 25 deQueue: function () { 26 var _this = this; 27 var cur = this.list.shift() || function () { }; 28 if (Utils.isFunction(cur)) { 29 cur.apply(_this, arguments); 30 if (_this.list.length) _this.deQueue(); 31 } else if (Utils.isNumber(cur)) { 32 setTimeout(function () { 33 _this.deQueue(); 34 }, cur); 35 } 36 } 37 } 38 39 var oBtn = document.querySelector("input"); 40 var oBox = document.querySelector("#box"); 41 var create = (function () { 42 var count = 0; 43 return function () { 44 var oLi = document.createElement("li"); 45 oLi.innerHTML = count++; 46 return oLi; 47 } 48 })(); 49 oBtn.onclick = function () { 50 var oQ = new Queue(); 51 function add() { 52 for (var i = 0; i < 10; i++) { 53 oQ.enQueue(function () { 54 oBox.appendChild(create()); 55 }).delay(1000); 56 } 57 } 58 add(); 59 oQ.deQueue(); 60 } 61 </script>
這樣封裝以後,咱們的異步隊列就變得通用一點了,把延時和業務邏輯分開處理函數