这道js的面试题,是这样的,页面上有一个按钮,一个ul,点击按钮的时候,每隔1秒钟向ul的后面追加一个li, 一共追加10个,li的内容从0开始技术( 0, 1, 2, ....9 ),首先我们用闭包封装一个创建li元素的函数.
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个元素:
<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 }
我们来使用它:
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>
这样封装之后,我们的异步队列就变得通用一点了,把延时和业务逻辑分开处理
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。