javascript设计模式-迭代器模式(Iterator)

   1 <!doctype html>

   2 <html lang="en">

   3 <head>

   4     <meta charset="UTF-8">

   5     <title>迭代器模式</title>

   6 </head>

   7 <body>

   8     

   9 <script>

  10 /**

  11  * 迭代器模式

  12  *

  13  * 定义:

  14  * 提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。

  15  *

  16  * 本质:

  17  * 控制访问聚合对象中的元素

  18  *

  19  * 所谓聚合是指一组对象的组合结构。

  20  *

  21  * 一.功能

  22  * 迭代器模式的功能主要在于提供对聚合对象的迭代访问。迭代器就围绕着这个“访问”做文章,延伸出很多的功能。比如:

  23  * 1.以不同的方式遍历聚合对象,比如向前,向后等。

  24  * 2.对同一个聚合同时进行多个遍历。

  25  * 3.以不同的遍历策略来遍历聚合,比如是否需要过滤等。

  26  * 4.多态迭代,含义是:为不同的聚合结构提供统一的迭代接口,也就是说通过一个迭代接口可以访问不同的聚合结构,这就叫做多态迭代。事实上,标准的迭代模式实现基本上都是支持多态迭代的

  27  *

  28  * 二,关键思想

  29  * 聚合对象的类型很多,如果对聚合对象的迭代访问跟聚合对象本身融合在一起的话,会严重影响到聚合对象的可扩展性和可维护性。

  30  * 因此迭代器模式的关键思想就是把对聚合对象的遍历和访问从聚合对象中分离出来,放入单独的迭代器中。这样聚合对象会变得简单一些,而且迭代器和聚合对象可以独立地变化和发展,会大大加强系统的灵活性。

  31  *

  32  * 三,内部迭代器和外部迭代器

  33  * 所谓内部迭代器,指的是由迭代器自己来控制迭代下一个元素的步骤,客户端无法干预。因此,如果想要在迭代的过程中完成工作的话,客户端就需要把操作传递给迭代器。迭代器在迭代的时候会在每个元素上执行这个操作,即回调。

  34  * 所谓外部迭代,指的是客户端来控制迭代下一个元素的步骤,客户端必须显式地调用next来迭代下一个元素。

  35  * 总体来说外部迭代器比内部迭代器更灵活一些。

  36  */

  37 

  38 // 示例代码

  39 

  40 (function(){

  41 /**

  42  * 迭代器实现对象,示意的是聚合对象为数组的迭代器

  43  * 不同聚合对象相应的迭代器实现是不一样的

  44  * @param {Array} aggregate [聚合对象]

  45  */

  46 var Iterator = function(aggregate){

  47     this.aggregate = aggregate;

  48     // 当前索引位置

  49     this.index = -1;

  50 };

  51 Iterator.prototype = {

  52     first: function(){

  53         this.index = 0;

  54     },

  55     next: function(){

  56         if(this.index < this.aggregate.size()) {

  57             this.index++;

  58         }

  59     },

  60     isDone: function(){

  61         return this.index === this.aggregate.size();

  62     },

  63     currentItem: function(){

  64         return this.aggregate.get(this.index);

  65     }

  66 };

  67 

  68 var Aggregate = function(ss){

  69     this.ss = ss;

  70 };

  71 Aggregate.prototype = {

  72     createIterator: function(){

  73         return new Iterator(this);

  74     },

  75     get: function(index){

  76         var retObj = null;

  77         if(index < this.ss.length) {

  78             retObj = this.ss[index];

  79         }

  80 

  81         return retObj;

  82     },

  83     size: function(){

  84         return this.ss.length;

  85     }

  86 };

  87 

  88 new function(){

  89     var names = ['张三', '李四', '王五'];

  90     var aggregate = new Aggregate(names);

  91     var it = aggregate.createIterator();

  92     var obj;

  93 

  94     it.first();

  95     while(!it.isDone()) {

  96         obj = it.currentItem();

  97         console.log('the obj === ' + obj);

  98         it.next();

  99     }

 100 }();

 101 

 102 }());

 103 

 104 (function(){

 105     // 实现实例

 106     

 107     // 工资表数据的整合

 108     /*

 109     项目的客户方收购了一家小公司,这家小公司有自己的工资系统,现在需要整合到客户方已有的工资系统中。

 110     两方的工资系统数据结构可能不同,但用来描述工资的数据模型是差不多的。

 111      */

 112     

 113     var Iterator = function(aggregate){

 114         this.aggregate = aggregate;

 115         this.index = -1;

 116     };

 117     Iterator.prototype = {

 118         first: function(){

 119             this.index = 0;

 120         },

 121         next: function(){

 122             if(this.index < this.aggregate.size()) {

 123                 this.index++;

 124             }

 125         },

 126         isDone: function(){

 127             return this.index === this.aggregate.size();

 128         },

 129         currentItem: function(){

 130             return this.aggregate.get(this.index);

 131         }

 132     };

 133 

 134 

 135     // 工资描述模型对象

 136     var PayModel = function(){

 137         // 支付工资的人员

 138         this.userName;

 139         // 支付的工资数额

 140         this.pay;

 141     };

 142     PayModel.prototype = {

 143         getUserName: function(){

 144             return this.userName;

 145         },

 146         setUserName: function(userName){

 147             this.userName = userName;

 148         },

 149         getPay: function(){

 150             return this.pay;

 151         },

 152         setPay: function(pay){

 153             this.pay = pay;

 154         },

 155         toString: function(){

 156             return 'userName = ' + this.userName + ', pay = ' + this.pay;

 157         }

 158     };

 159 

 160     // 客户方已有的工资管理对象

 161     var PayManager = function(){

 162         this.list = [];

 163     };

 164     PayManager.prototype = {

 165         createIterator: function(){

 166             return new iterator(this);

 167         },

 168         get: function(index){

 169             var ret = null;

 170             if(index < this.list.length) {

 171                 ret = this.list[index];

 172             }

 173 

 174             return ret;

 175         },

 176         size: function(){

 177             return this.list.length;

 178         },

 179 

 180         // 计算工资,其实应该有很多参数,为了演示从简

 181         calcPay: function(){

 182             var pm1 = new PayModel();

 183             pm1.setPay(3800);

 184             pm1.setUserName('张三');

 185 

 186             var pm2 = new PayModel();

 187             pm2.setPay(5800);

 188             pm2.setUserName('李四');

 189 

 190             this.list.push(pm1);

 191             this.list.push(pm2);

 192         }

 193     };

 194 

 195     // 被客户方收购的那个公司的工资管理类

 196     var SalaryManager = function(){

 197         this.pms = [];

 198     };

 199     SalaryManager.prototype = {

 200         // 获取工资列表

 201         getPays: function(){

 202             return this.pms;

 203         },

 204         // 计算工资

 205         calcSalary: function(){

 206             var pm1 = new PayModel();

 207             pm1.setPay(2200);

 208             pm1.setUserName('王五');

 209 

 210             var pm2 = new PayModel();

 211             pm2.setPay(3600);

 212             pm2.setUserName('赵六');

 213 

 214             this.pms.push(pm1);

 215             this.pms.push(pm2);

 216         }

 217     };

 218 

 219     new function(){

 220         var payManager = new PayManager();

 221         payManager.calcPay();

 222         var it = payManager.createIterator();

 223         console.log('集团工资列表:');

 224         var pm;

 225         it.first();

 226         while(!it.isDone()){

 227             pm = it.currentItem();

 228             console.log('ths obj === ' + pm);

 229             it.next();

 230         }

 231 

 232         var salaryManager = new SalaryManager();

 233         salaryManager.calcSalary();

 234         var pms = salaryManager.getPays();

 235         console.log('新收购的公司工资列表:');

 236         for(var i = 0; i < pms.length; i++) {

 237             console.log(pms[i]);

 238         }

 239     }();

 240 

 241 }());

 242 

 243 (function(){

 244     // 带迭代策略的迭代器示例

 245     /*

 246     在实现过滤功能的迭代器中,又有两种常见的需要过滤的情况,一种是对数据进行整条过滤,比如只能查看自己部门的数据;另外一种情况是数据进行部分过滤,比如某些人不能查看工资数据。

 247     带迭代策略的迭代器实现的一个基本思路,就是先把聚合对象的聚合数据获取到,并存储到迭代器中,这样迭代器就可以按照不同的策略来迭代数据了。

 248      */

 249     var Iterator = function(aggregate){

 250         this.pms = [];

 251         this.index = 0;

 252 

 253         // 在这里先对聚合对象的数据进行过滤

 254         var tempCol = [];

 255         var i;

 256         for(i in aggregate) {

 257             if(!aggregate.hasOwnProperty(i)) continue;

 258 

 259             if(aggregate[i].getPay() < 3000) {

 260                 tempCol.push(aggregate[i]);

 261             }

 262         }

 263 

 264         this.pms = [];

 265         for(i = 0; i < tempCol.length; i++) {

 266             this.pms[i] = tempCol[i];

 267         }

 268     };

 269     Iterator.prototype = {

 270         hasNext: function(){

 271             return this.index <= (this.pms.length - 1);

 272         },

 273         next: function(){

 274             var ret = null;

 275             if(this.hasNext()) {

 276                 ret = this.pms[this.index++];

 277             }

 278 

 279             // 在这里对要返回的数据进行过滤,比如不让查看工资数据

 280             if(ret) ret.setPay(0.0);

 281 

 282             return ret;

 283         },

 284         remove: function(){}

 285     };

 286 

 287 /*

 288 谁定义遍历算法的问题

 289 

 290 在迭代器模式的实现中,常见的有两个地方可以来定义遍历算法,一个是聚合对象本身,另外一个就是迭代器负责遍历算法。

 291 

 292 在聚合对象本身定义遍历算法,通常会在遍历过程中,用迭代器来存储当前迭代的状态这种迭代器被称为游标,因为它仅用来指示当前的位置。

 293 

 294 在迭代器中定义遍历算法,会比在相同的聚合上使用不同的迭代器算法容易,同事也易于在不同的聚合上重用相同的算法。比如上面带策略的迭代器示例,迭代器把需要迭代的数据从聚合对象中取出并存放到自己的对象中,然后再迭代自己的数据,除了刚开始创建迭代器的时候需要访问聚合对象外,真正的迭代过程已经跟聚合对象无关了。

 295 

 296 

 297 迭代器健壮程度如何

 298 在遍历一个聚合的同时更改这个聚合可能是危险的。如果在遍历的时候增加或删除该聚合元素,可能会导致两次访问同一个元素或者遗漏掉某个元素。一个简单的解决办法是拷贝该聚合,并对该拷贝实施遍历,但一般来说代价太高。

 299 

 300 一个健壮的迭代器保证插入和删除不会干扰遍历,且不需要拷贝该聚合。有许多方法来实现健壮的迭代器。其中大多数需要向这个聚合注册该迭代器。当插入或删除时,该聚合要么调整迭代器的内部状态,要么在内部的维护额外的信息以保证正确的遍历。

 301 

 302 空迭代器

 303 一个空迭代器是一个退化的迭代器,它有助于处理边界条件。一个NullIterator总是已经完成了遍历。例如:叶节点元素返回NullIterator的一个实例。

 304 

 305  */

 306 

 307 /*

 308 双向迭代器

 309 

 310 可以同时向前和向后遍历数据的迭代器。

 311  */

 312 

 313 }());

 314 

 315 /**

 316  * 迭代器模式的优点

 317  *

 318  * 1.更好的封装性

 319  * 2.迭代器模式可以让你访问一个聚合对象的内容,而无需暴露该聚合对象的内部表示,从而提高聚合对象的封装性。

 320  * 3.可以以不同的遍历方式来遍历一个聚合。

 321  * 4.使用迭代器模式,使得聚合对象的内容和具体的迭代算法分离开。这样就可以通过使用不同的迭代器的实例,不同的遍历方式来遍历一个聚合对象了。

 322  * 5.迭代器简化了聚合的接口。

 323  * 6.简化客户端调用

 324  * 7.同一个聚合上可以有多个遍历。

 325  * 8.每个迭代器保持它自己的遍历状态。

 326  *

 327  *

 328  * 何时选用迭代器模式

 329  *

 330  * 1.如果你希望提供访问一个聚合对象的内容,但是又不想暴露它的内部表示的时候。

 331  * 2.如果你希望有多种遍历方式可以访问聚合对象,可以使用迭代器模式。

 332  * 3.如果你希望为遍历不同的聚合对象提供一个统一的接口。

 333  *

 334  *

 335  * 相关模式

 336  *

 337  * 迭代器模式和组合模式

 338  *

 339  * 这两个模式可以组合使用。

 340  * 组合模式是一种递归的对象结构,在枚举某个组合对象的子对象的时候,通常会使用迭代器模式。

 341  *

 342  * 迭代器模式和工厂方法模式

 343  * 

 344  * 这两个模式可以组合使用。

 345  * 在聚合对象创建迭代器的时候,通常会采用工厂方法模式来实例化相应的迭代器对象。

 346  *

 347  * 备忘模式

 348  * 可使用memento来捕获一个迭代的状态。迭代器在其内部存储memento

 349  */

 350 

 351 // 翻页迭代

 352  (function(){

 353     

 354     // 顺序翻页迭代其示例

 355     

 356     // 工资描述模型对象

 357     var PayModel = function(){

 358         // 支付工资的人员

 359         this.userName;

 360         // 支付的工资数额

 361         this.pay;

 362     };

 363     PayModel.prototype = {

 364         getUserName: function(){

 365             return this.userName;

 366         },

 367         setUserName: function(userName){

 368             this.userName = userName;

 369         },

 370         getPay: function(){

 371             return this.pay;

 372         },

 373         setPay: function(pay){

 374             this.pay = pay;

 375         },

 376         toString: function(){

 377             return 'userName = ' + this.userName + ', pay = ' + this.pay;

 378         }

 379     };

 380 

 381     var SalaryManager = function(){

 382         this.pms = [];

 383     };

 384     SalaryManager.prototype = {

 385         getPays: function(){

 386             return this.pms;

 387         },

 388         calcSalary: function(){

 389             var pm1 = new PayModel();

 390             pm1.setPay(2200);

 391             pm1.setUserName('王五');

 392 

 393             var pm2 = new PayModel();

 394             pm2.setPay(3600);

 395             pm2.setUserName('赵六');

 396 

 397             var pm3 = new PayModel();

 398             pm3.setPay(2200);

 399             pm3.setUserName('王五二号');

 400 

 401             var pm4 = new PayModel();

 402             pm4.setPay(3600);

 403             pm4.setUserName('赵六二号');

 404 

 405             var pm5 = new PayModel();

 406             pm5.setPay(2200);

 407             pm5.setUserName('王五三号');

 408 

 409             this.pms.push(pm1);

 410             this.pms.push(pm2);

 411             this.pms.push(pm3);

 412             this.pms.push(pm4);

 413             this.pms.push(pm5);

 414         },

 415         // Factory Method

 416         createIterator: function(type){

 417             if(type === 'random') {

 418                 return new RandomIterator(this);

 419             }

 420             return new Iterator(this);

 421         }

 422     };

 423 

 424     // 双向迭代器

 425     var Iterator = function(aggregate){

 426         this.pms = aggregate.getPays();

 427         this.index = 0;

 428     };

 429     Iterator.prototype = {

 430         hasNext: function(){

 431             return this.index <= (this.pms.length - 1);

 432         },

 433         hasPrevious: function(){

 434             return this.index > 0;

 435         },

 436         // 返回当前索引到num的集合

 437         next: function(num){

 438             var col = [];

 439             var count = 0;

 440             while(this.hasNext() && count++ < num) {

 441                 col.push(this.pms[this.index++]);

 442             }

 443 

 444             return col;

 445         },

 446         // 把索引退回去num个,然后再取值。

 447         // 事实上有可能有多退回去的数据

 448         previous: function(num){

 449             var col = [];

 450             var count = 0;

 451             this.index = num;

 452             while(this.hasPrevious() && count++ < num) {

 453                 col.push(this.pms[this.index++]);

 454             }

 455 

 456             return col;

 457         }

 458     };

 459 

 460     new function(){

 461         var salaryManager = new SalaryManager();

 462         salaryManager.calcSalary();

 463         var it = salaryManager.createIterator();

 464 

 465         // 获取第一页,每页显示两条

 466         var col = it.next(2);

 467         console.log('第一页数据:');

 468         print(col);

 469 

 470         var col2 = it.next(2);

 471         console.log('第二页数据:');

 472         print(col2);

 473 

 474         var col3 = it.previous(2);

 475         console.log('第三页数据:');

 476         print(col3);

 477 

 478         function print(col){

 479             for(var i =0; i < col.length; i++) {

 480                 console.log(col[i]);

 481             }

 482         }

 483     }();

 484 

 485     // 随机翻页迭代器示例

 486     

 487     var RandomIterator = function(aggregate){

 488         this.pms = aggregate.getPays();

 489         this.index = 0;

 490     };

 491     RandomIterator.prototype = {

 492         hasNext: function(){

 493             return this.index <= (this.pms.length - 1);

 494         },

 495         hasPrevious: function(){

 496             return this.index > 0;

 497         },

 498         getPage: function(pageNum, pageShow){

 499             var col = [];

 500             // 需要在这里先计算需要获取的数据的开始条数和结束条数

 501             var start = (pageNum - 1) * pageShow;

 502             var end = start + pageShow - 1;

 503 

 504             if(start < 0) start = 0;

 505 

 506             if(end > this.pms.length - 1) end = this.pms.length - 1;

 507 

 508             this.index = 0;

 509             while(this.hasNext() && this.index <= end) {

 510                 if(this.index >= start) col.push(this.pms[this.index]);

 511                 this.index++;

 512             }

 513 

 514             return col;

 515         }

 516     };

 517 

 518     new function(){

 519         var salaryManager = new SalaryManager();

 520         salaryManager.calcSalary();

 521         var it = salaryManager.createIterator('random');

 522 

 523         // 获取第一页,每页显示两条

 524         var col = it.getPage(1, 2);

 525         console.log('第一页数据:');

 526         print(col);

 527 

 528         var col2 = it.getPage(2, 2);

 529         console.log('第二页数据:');

 530         print(col2);

 531 

 532         var col3 = it.getPage(1, 2);

 533         console.log('再次获取第一页数据:');

 534         print(col3);

 535 

 536         var col4 = it.getPage(3, 2);

 537         console.log('第三页数据:');

 538         print(col4);

 539 

 540         function print(col){

 541             for(var i =0; i < col.length; i++) {

 542                 console.log(col[i]);

 543             }

 544         }

 545 

 546     }();

 547  }());

 548 

 549 (function(){

 550     /**

 551      * ECMAScript 6的Iterator--------------Generators

 552      *

 553      * 迭代器模式是很常用的设计模式,但是实现起来,很多东西是程序化的;当迭代规则比较复杂时,维护迭代器内的状态,是比较麻烦的。 于是有了generator,何为generator,这里 说的很明确: Generators: a better way to build Iterators.就是实现迭代器的更好的方式,借助 yield 关键字,可以更优雅的实现fib数列。

 554      */

 555 

 556     // 最简单的yield用法

 557     // 创建一个generatorFunction

 558     function* Hello() {

 559         yield 1;

 560         yield 2;

 561     }

 562 

 563     /**

 564      * function* Hello() { // 我习惯用大驼峰命名因为这就好比generator的构造函数 yield 1; yield 2; }

 565         arguments: null

 566         caller: null

 567         length: 0

 568         name: "Hello"

 569         prototype: GeneratorFunctionPrototype

 570             __proto__: GeneratorFunctionPrototype

 571                 constructor: function GeneratorFunctionPrototype() { [native code] }

 572                 next: function next() { [native code] }

 573                 throw: function throw() { [native code] }

 574                 __proto__: Object

 575          __proto__: function GeneratorFunctionPrototype() { [native code] }

 576         < function scope >

 577      */

 578 

 579     var hello = Hello(); // hello 是一个generator

 580     var a = hello.next(); // a: Object {value: 1, done: false}

 581     var b = hello.next(); // b: Object {value: 2, done: false}

 582     var c = hello.next(); // c: Object {value: undefined, done: true}

 583     hello.next();   // Error: Generator has already finished

 584 

 585     /*

 586     以看到hello的原型链中总是有一个next函数, 每次运行都返回yield后面的值, 只是不是单纯的yield后面的值, 而是放在一个对象的value键中, 同时我们注意到对象中还有另一个键done, Hello函数中有两个yield, 因此前两个done的值为false, 表示我还没有结束呐!, 最后一个done为true, 表示我已经结束了! 如果继续运行hello.next()则会报错Uncaught Error: Generator has already finished

 587 

 588 很明显的说yield就是相当于是另一种return, return使用时函数就结束了, 而使用yield的时候, 函数会卡在那个yield的地方, 等待下一个next

 589     */

 590 

 591     // fib示例

 592 

 593     // before

 594     function fib(){

 595         return {

 596             state :0,

 597             cur :0,

 598             prev1:-1,

 599             prev2:-1,

 600             hasNext:function(){

 601                 return true;

 602             },

 603             //fib数列,第一个是0,第二个是1,后面就是统一的迭代公式了

 604             next:function(){

 605                 if(this.state == 0){

 606                     this.cur = 0;

 607                     this.state = 1;

 608                 }else if(this.state == 1){

 609                     this.cur = 1;

 610                     this.prev2 = 0;

 611                     this.state = 2;

 612                 }else{

 613                     this.prev1 = this.prev2;

 614                     this.prev2 = this.cur;

 615                     this.cur = this.prev1 + this.prev2;

 616                 }

 617                 return this.cur;

 618             }

 619             //ignore reset funciton

 620         }

 621     }

 622     //这是无限序列,所以改造了一下,只生成8个数

 623     var fibIter = fib();

 624     for(var i = 0; i < 8; i++){

 625         console.log(fibIter.next());

 626         if(fibIter.hasNext())

 627             continue;

 628     }

 629 

 630     // after

 631      function* fib2(){

 632         yield 0;    // 状态0,第一次调用next,返回0,并改变状态

 633         yield 1;    // 状态1,第二次调用next,返回1,并改变状态

 634 

 635         var p1 = 0;

 636         var p2 = 1;

 637         var cur = p1 + p2;

 638 

 639         while(true) {

 640             yield cur;  // 状态2,后面调用next,返回相应的几个,状态不再改变

 641 

 642             p1 = p2;

 643             p2 = cur;

 644             cur = p1 + p2;

 645         }

 646     }

 647 

 648     var fibIter2 = fib2();

 649     for(var i = 0; i < 8; i++){

 650         console.log(fibIter2.next().value);

 651     }

 652     /*

 653     0  1  1  2  3  5  8  13

 654     */

 655 

 656 

 657     // http://www.html-js.com/article/1716

 658 

 659     (function(){

 660         // 对从1到100的数组,先取出其中的所有偶数,然后再取出所有其中的前10个,

 661         // 然后再计算其平方,然后转成数组。

 662         function* wrap(arr){

 663             for(var i = 0;i<arr.length;i++){

 664                 yield arr[i]; // ---(1)----

 665             }

 666         }

 667 

 668         function iter(arr){

 669             return new Iterator(arr);

 670         }

 671 

 672         function Iterator(arr){

 673             this.gen = wrap(arr);

 674         }

 675 

 676         Iterator.prototype = {

 677             where: function where(f){

 678                 var gen = whereImple(this.gen, f);

 679                 this.gen = gen;

 680                 return this;

 681             },

 682             map: function map(f){

 683                 var gen = mapImpl(this.gen, f);

 684                 this.gen = gen;

 685                 return this;

 686             },

 687             toArray: function toArray(){

 688                 var arr = [];

 689                 var _g;

 690                 var gen = this.gen;

 691                 while (true){

 692                     _g = gen.next();

 693                     if(_g.done) break;

 694                     arr.push(_g.value);

 695                 }

 696                 return arr;

 697             }

 698         };

 699 

 700         function* mapImpl(gen,f){

 701             var _g;

 702             while(true){

 703                 _g = gen.next();

 704                 if(_g.done) break;

 705                 yield f(_g.value);

 706             }

 707         }

 708 

 709         function* whereImple(gen,f){

 710             var index = 0, _g, value;

 711             while(true){

 712                 _g = gen.next();

 713                 if(_g.done) break;

 714                 value = _g.value;

 715                 if(f(value,index++)){

 716                     yield value;

 717                 }

 718             }

 719         }

 720 

 721         var _1to10 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

 722         var arr =iter(_1to10)

 723                 .where(function(v){return v % 2 == 0})

 724                 .map(function(v){return v * v})

 725                 .toArray();

 726         console.log(arr); // [4, 16, 36, 64, 100]

 727     }());

 728 

 729     // http://bg.biedalian.com/2013/12/21/harmony-generator.html

 730 

 731     // 代替回调金字塔

 732 

 733     function delay(time) {

 734         return function(fn) {

 735             setTimeout(function() {

 736                 fn(null, time); // null 表示没有错误..

 737             }, time)

 738         }

 739     }

 740 

 741     function co(GenFunc) {

 742         return function(cb) {

 743             var gen = GenFunc();

 744             void function next(err, args) {

 745                 if (err) {

 746                     cb(err);

 747                 } else {

 748                     if (gen.next) {

 749                         var ret = gen.next(args);

 750                         if (ret.done) {

 751                             cb && cb(null, args);

 752                         } else {

 753                             ret.value(next);

 754                         }

 755                     }

 756                 }

 757             }();

 758         }

 759     }

 760 

 761     co(function* () {

 762         var a;

 763         a = yield delay(200); // 200

 764         a = yield delay(a + 100); // 300

 765         a = yield delay(a + 100); // 400

 766     })(function(err, data) {

 767         if (!err) {

 768             console.log(data); // print 400

 769         }

 770     })

 771 

 772 }());

 773 

 774 (function(){

 775     // http://www.dofactory.com/javascript-iterator-pattern.aspx

 776     

 777     var Iterator = function(items) {

 778         this.index = 0;

 779         this.items = items;

 780     };

 781 

 782     Iterator.prototype = {

 783         first: function() {

 784             this.reset();

 785             return this.next();

 786         },

 787         next: function() {

 788             return this.items[this.index++];

 789         },

 790         hasNext: function() {

 791             return this.index <= this.items.length;

 792         },

 793         reset: function() {

 794             this.index = 0;

 795         },

 796         each: function(callback) {

 797             for (var item = this.first(); this.hasNext(); item = this.next()) {

 798                 callback(item);

 799             }

 800         }

 801     };

 802 

 803     // log helper

 804     var log = (function() {

 805         var log = "";

 806         return {

 807             add: function(msg) { log += msg + "\n"; },

 808             show: function() { console.log(log); log = ""; }

 809         }

 810     })();

 811 

 812 

 813     new function run() {

 814 

 815         var items = ["one", 2, "circle", true, "Applepie"];

 816         var iter = new Iterator(items);

 817 

 818         // using for loop

 819 

 820         for (var item = iter.first(); iter.hasNext(); item = iter.next()) {

 821             log.add(item);

 822         }

 823 

 824         log.add("");

 825 

 826         // using Iterator's each method

 827 

 828         iter.each(function(item) {

 829             log.add(item);

 830         });

 831 

 832         log.show();

 833     }();

 834 }());

 835 

 836 (function(){

 837     /* Title: Iterator

 838      Description: implements a specialized language

 839      */

 840     

 841     var agg = (function () {

 842 

 843         var index = 0,

 844                 data = [1, 2, 3, 4, 5],

 845                 length = data.length;

 846 

 847         return {

 848 

 849             next:function () {

 850                 var element;

 851                 if (!this.hasNext()) {

 852                     return null;

 853                 }

 854                 element = data[index];

 855                 index = index + 2;

 856                 return element;

 857             },

 858 

 859             hasNext:function () {

 860                 return index < length;

 861             },

 862 

 863             rewind:function () {

 864                 index = 0;

 865             },

 866 

 867             current:function () {

 868                 return data[index];

 869             }

 870 

 871         };

 872     }());

 873 

 874     var element;

 875     while (element - agg.next()) {

 876         // do something with the element

 877         console.log(element);

 878     }

 879 

 880     while (agg.hasNext()) {

 881         // do something with the next element...

 882         console.log(agg.next());

 883     }

 884 

 885     // this loop logs 1, then 3, then 5

 886     while (agg.hasNext()) {

 887         console.log(agg.next());

 888     }

 889 

 890     // go back

 891     agg.rewind();

 892     console.log(agg.current()); // 1

 893 

 894     // reference

 895     // http://www.addyosmani.com/resources/essentialjsdesignpatterns/book/#iteratorpatternjquery

 896     // http://shop.oreilly.com/product/9780596806767.do?sortby=publicationDate

 897 }());

 898 

 899 (function(){

 900     var CafeMenu = function(){

 901         Menu.apply(this);

 902         this.nPosition = -1;

 903         this.aMenuItems = [];

 904         this.createIterator = function(){

 905             return new CafeMenuIterator(this.aMenuItems);

 906         };

 907         this.addItem("Express", "Coffee from machine", false, 0.99);

 908         this.addItem("Long with water", "Coffee with a lot of water", false, 1.20);

 909         this.addItem("On the rocks", "Coffee with ice", false, 2.00);

 910     };

 911     CafeMenu.prototype.addItem = function(sName, sDescription, bVegetarian, nPrice){

 912         var oMenuItem = new MenuItem(sName, sDescription, bVegetarian, nPrice);

 913         this.aMenuItems.push(oMenuItem);

 914     };

 915     CafeMenu.prototype.getMenuItems = function(){

 916         return this.aMenuItems;

 917     };

 918 

 919     var CafeMenuIterator = function(aMenuItems){

 920         this.aMenuItems = aMenuItems;

 921         Iterator.apply(this);

 922         this.nPosition = -1;

 923         this.nLength = this.aMenuItems.length;

 924         this.hasNext = function(){

 925             return (this.nPosition + 1) < this.nLength;

 926         };

 927         this.next = function(){

 928             this.nPosition = this.nPosition + 1;

 929             return this.aMenuItems[this.nPosition];

 930         };

 931     };

 932 

 933     var DinnerMenu = function(){

 934         Menu.apply(this);

 935         this.oMenuItems = {};

 936         this.createIterator = function(){

 937             return new DinnerMenuIterator(this.oMenuItems);

 938         };

 939         this.addItem("Vegetarian BLT", "(Fakin') Bacon with lettuce and tomato on whole wheat", true, 2.99);

 940         this.addItem("BLT", "Bacon with lettuce and tomato on whole wheat", false, 2.99);

 941         this.addItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29);

 942         this.addItem("Hotdog", "A hotdog with saurkraut, relish, onions, topped with cheese", false, 3.05);

 943     };

 944     DinnerMenu.MAX_ITEMS = 6;

 945     DinnerMenu.prototype.addItem = function(sName, sDescription, bVegetarian, nPrice){

 946         if(this.length === DinnerMenu.MAX_ITEMS){

 947             throw new Error("Sorry menu is full! Can't add item to menu");

 948         }

 949         this.oMenuItems[sName] = new MenuItem(sName, sDescription, bVegetarian, nPrice);

 950         this.length = this.length + 1;

 951     };

 952     DinnerMenu.prototype.getMenuItems = function(){

 953         return this.oMenuItems;

 954     };

 955 

 956     var DinnerMenuIterator = function(oMenuItems){

 957         this.oMenuItems = oMenuItems;

 958         Iterator.apply(this);

 959         this.nPosition = -1;

 960         this.nLength = 0;

 961         this.hasNext = function(){

 962             return (this.nPosition + 1) < this.nLength;

 963         };

 964         this.next = function(){

 965             this.nPosition = this.nPosition + 1;

 966             return this.oMenuItems[this.aKeys[this.nPosition]];

 967         };

 968         this._getKeys = function(){

 969             var aKeys = [];

 970             var sKey = '';

 971             for(sKey in this.oMenuItems){

 972                 if(this.oMenuItems.hasOwnProperty(sKey)){

 973                     aKeys.push(sKey);

 974                     this.nLength = this.nLength + 1;

 975                 }

 976             }

 977             return aKeys;

 978         };

 979         this.aKeys = this._getKeys();

 980     };

 981 

 982     var Iterator = function(){

 983         this.hasNext = function(){

 984             throw new Error("This method must be overwritten!");

 985         };

 986         this.next = function(){

 987             throw new Error("This method must be overwritten!");

 988         };

 989         this.remove = function(){

 990             throw new Error("This method must be overwritten!");

 991         };

 992     };

 993 

 994     var Mattress = function(aMenus){

 995         this.aMenus = aMenus;

 996     };

 997     Mattress.prototype._printMenu = function(oIterator){

 998         var oMenuItem = null;

 999         while(oIterator.hasNext()){

1000             oMenuItem = oIterator.next();

1001             console.log(oMenuItem.getName() + ": " + oMenuItem.getDescription() + ", " + oMenuItem.getPrice() + "eur.");

1002         }

1003     };

1004     Mattress.prototype.printMenu = function(){

1005         var nMenu = 0;

1006         var nLenMenus = this.aMenus.length;

1007         var oMenu = null;

1008         var oIterator = null;

1009 

1010         for(; nMenu < nLenMenus;)

1011         {

1012             oMenu = this.aMenus[nMenu];

1013             oIterator = oMenu.createIterator();

1014             this._printMenu(oIterator);

1015             nMenu = nMenu + 1;

1016         }

1017     };

1018 

1019     var Menu = function(){

1020         this.createIterator = function(){

1021             throw new Error("This method must be overwritten!");

1022         };

1023     };

1024 

1025     var MenuItem = function(sName, sDescription, bVegetarian, nPrice){

1026         this.sName = sName;

1027         this.sDescription = sDescription;

1028         this.bVegetarian = bVegetarian;

1029         this.nPrice = nPrice;

1030     };

1031     MenuItem.prototype.getName = function(){

1032         return this.sName;

1033     };

1034     MenuItem.prototype.getDescription = function(){

1035         return this.sDescription;

1036     };

1037     MenuItem.prototype.getPrice = function(){

1038         return this.nPrice;

1039     };

1040     MenuItem.prototype.isVegetarian = function(){

1041         return this.bVegetarian;

1042     };

1043 

1044     var PancakeHouseMenu = function(){

1045         Menu.apply(this);

1046         this.nPosition = -1;

1047         this.aMenuItems = [];

1048         this.createIterator = function(){

1049             return new PancakeHouseMenuIterator(this.aMenuItems);

1050         };

1051         this.addItem("K&B's Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99);

1052         this.addItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99);

1053         this.addItem("Blueberry Pancakes", "Pancakes made with fresh blueberries", true, 3.49);

1054         this.addItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.59);

1055     };

1056     PancakeHouseMenu.prototype.addItem = function(sName, sDescription, bVegetarian, nPrice){

1057         var oMenuItem = new MenuItem(sName, sDescription, bVegetarian, nPrice);

1058         this.aMenuItems.push(oMenuItem);

1059     };

1060     PancakeHouseMenu.prototype.getMenuItems = function(){

1061         return this.aMenuItems;

1062     };

1063 

1064     var PancakeHouseMenuIterator = function(aMenuItems){

1065         this.aMenuItems = aMenuItems;

1066         Iterator.apply(this);

1067         this.nPosition = -1;

1068         this.nLength = this.aMenuItems.length;

1069         this.hasNext = function(){

1070             return (this.nPosition + 1) < this.nLength;

1071         };

1072         this.next = function(){

1073             this.nPosition = this.nPosition + 1;

1074             return this.aMenuItems[this.nPosition];

1075         };

1076     };

1077 

1078     var oMattress = new Mattress([new PancakeHouseMenu(), new DinnerMenu(), new CafeMenu()]);

1079     console.log("---------------------------------------------");

1080     oMattress.printMenu();

1081     console.log("---------------------------------------------");

1082 

1083 }());

1084 

1085 </script>

1086 </body>

1087 </html>

 

你可能感兴趣的:(JavaScript)