模式(一)javascript设计模式-阿里云开发者社区

开发者社区> 夕阳白雪> 正文

模式(一)javascript设计模式

简介: 模式有三种:Architectural Pattern、Design Pattern、Coding Pattern,即:框架模式、设计模式、编程模式。本文主要讲解javascript中的设计模式,好的设计模式能够提高代码的重用性,可读性,使代码更容易的维护和扩展。
+关注继续查看

模式有三种:Architectural Pattern、Design Pattern、Coding Pattern,即:框架模式、设计模式、编程模式。本文主要讲解javascript中的设计模式,好的设计模式能够提高代码的重用性,可读性,使代码更容易的维护和扩展。本文适合有一点javascript基础,对javascript的概念有所了解。

一、单例模式:

 单例模式是javascript中最常用的模式,它是将自己的代码放在一个命名空间下,这样的好处是可以减少使用全局变量,在多人协同开发时也能避免命名冲突等问题。这样的好处是维护起来非常方便,如下例:

1 var m = {
2     name: 'dog',
3     action: function() {
4         console.log(this.name);
5     }
6 };
7 m.action();//调用

 

或者

1 var dog = function() {
2     this.name = 'dog';
3     this.action = function() {
4         return console.log(this.name);
5     };
6     action();
7 };
8 dog(); //调用

 

工厂模式:

工厂模式就是将对象的方法创建交给外部对象,这样的好处就是解决了对象之间的相互影响、即耦合,避免了使用new来实例化对象,有助于创建模块化的代码,维护起来也方便。 工厂模式分为简单工厂模式和抽象工厂模式,下面介绍简单工厂模式:

1 var m = {};
2 m.action = function() {
3     console.log('dog');
4 };
5 var demo = function() {
6     m.action();
7 };
8 demo()//调用

 

抽象工厂模式先设计一个抽象类,这个类不能被实例化,只能用来派生子类,最后通过对子类的扩展实现工厂方法。如下:

 1 var f = function() {};
 2 f.prototype = {
 3     c: function() {
 4         throw new Error('can\'t use this method');//如果调用此方法会报错,因为它是用来派生子类不能实例化
 5     }
 6 };
 7 var e = function() {
 8     f.call(this);
 9 }
10 e.prototype = new f();
11 e.prototype.constructor = e;
12 e.prototype.c = function() {
13     console.log('this method is redefine');
14 }
15 // 调用
16 var demo = new e();
17 demo.c();

 

桥接模式:

桥接模式是将抽象与实现隔离,一遍二者独立变化。在设计一个javascript API的时候,它可以用来弱化类和对象之间的耦合。它还可以用来把多个类联接在一起。例如:

 1 var class1 = function(a,b,c) {
 2     this.a = a;
 3     this.b = b;
 4     this.c = c;
 5 };
 6 var class2 = function(d) {
 7     this.d = d;
 8 };
 9 var demo = function(a,b,c,d) {
10     this.one = new Class1(a,b,c);
11     this.two = new Class2(d);
12 };

 

组合模式:

组合模式可以用一条命令在多个对象上激发复杂的或递归的行为。好处是可以用同样的发放处理对象的集合与其中的特定子对象,也可以用来把一批子对象组织成树形结构,并且使整个树都可被遍历。如下:

 1 // DynamicGallery Class
 2   var DynamicGallery =function (id) { // 实现Composite,GalleryItem组合对象类 
 3      this.children = [];
 4      this.element = document.createElement('div');
 5      this.element.id = id;
 6      this.element.className ='dynamic-gallery';
 7   }
 8   DynamicGallery.prototype = {
 9      // 实现Composite组合对象接口 
10      add: function (child) {
11         this.children.push(child);
12         this.element.appendChild(child.getElement());
13      },
14      remove: function (child) {
15         for (var node, i =0; node =this.getChild(i); i++) {
16            if (node == child) {
17               this.children.splice(i, 1);
18               break;
19            }
20         }
21         this.element.removeChild(child.getElement());
22      },
23      getChild: function (i) {
24         returnthis.children[i];
25      },
26      // 实现DynamicGallery组合对象接口 
27      hide: function () {
28         for (var node, i =0; node =this.getChild(i); i++) {
29            node.hide();
30         }
31         this.element.style.display ='none';
32      },
33      show: function () {
34         this.element.style.display ='block';
35         for (var node, i =0; node = getChild(i); i++) {
36            node.show();
37         }
38      },
39      // 帮助方法 
40      getElement: function () {
41         returnthis.element;
42      }
43   }

 

 1 var GalleryImage =function (src) { // 实现Composite和GalleryItem组合对象中所定义的方法 
 2      this.element = document.createElement('img');
 3      this.element.className ='gallery-image';
 4      this.element.src = src;
 5   }
 6   GalleryImage.prototype = {
 7      // 实现Composite接口 
 8      // 这些是叶结点,所以我们不用实现这些方法,我们只需要定义即可 
 9      add: function () { },
10      remove: function () { },
11      getChild: function () { },
12      // 实现GalleryItem接口 
13      hide: function () {
14         this.element.style.display ='none';
15      },
16      show: function () {
17         this.element.style.display ='';
18      },
19      // 帮助方法 
20      getElement: function () {
21         returnthis.element;
22      }
23   }
 1 var topGallery =new DynamicGallery('top-gallery'); 
 2   topGallery.add(new GalleryImage('/img/image-1.jpg')); 
 3   topGallery.add(new GalleryImage('/img/image-2.jpg')); 
 4   topGallery.add(new GalleryImage('/img/image-3.jpg')); 
 5   var vacationPhotos =new DyamicGallery('vacation-photos'); 
 6   for(var i =0, i <30; i++){ 
 7     vacationPhotos.add(new GalleryImage('/img/vac/image-'+ i +'.jpg')); 
 8   } 
 9   topGallery.add(vacationPhotos); 
10   topGallery.show(); 
11   vacationPhotos.hide();

 

门面模式:

门面模式常常是开发人员最亲密的朋友,他几乎是所有javascript库的核心原则。门面模式有两个作用:一是简化类的接口;二是消除类与使用它的客户代码之间的耦合。示例如下:

 1 function a() {
 2 
 3 }
 4 function b() {
 5 
 6 }
 7 function ab() {
 8     a();
 9     b();
10 }

 

适配器模式:

适配器模式可以用来在现有接口和不兼容的类之间进行适配。从表面上看,适配器模式很像门面模式,都对别的对象进行包装并改变其呈现的接口。二者的区别在与它们如何改变接口,门面元素展现的是一个简化的接口,它并不提供额外的选择,而且有时为了方便完成常见任务它还会做出一些假定。而适配器则要把一个接口转换为另一个接口,它并不会过滤某些能力,也不会简化接口。

 1 var str = {
 2     a: 'a',
 3     b: 'b',
 4     c: 'c'
 5 };
 6 function i(s1,s2,s3) {
 7     console.log(s1 + ',' + s2 + ',' + s3);
 8 }
 9 function demo(o) {
10     i(o.a,o.b,o.c);
11 }

 

装饰者模式:

装饰者模式可用来透明地把对象包装在具有同样接口的另一个对象之中,装饰者可以用于为对象添加功能,可以用来代替大量子类。装饰者模式和组合模式有很多共同点,它们都与所包装的对象实现统一的接口并且会把任何方法条用传递给这些对象。可是组合模式用于把众多子对象组织为一个整体,而装饰者模式用于在不修改现有对象或从派生子类的前提下为其添加方法。如下:

1 var m = {};
2 m.child = {};
3 m.child.one = function() {};
4 m.child.two = function() {};

 

 享元模式:

 享元模式最适合于解决因创建大量类似对象而累及性能的问题。通过把大量独立对象转化为少量共享对象,可以降低运行web应用程序所需的资源数量。

javascript设计模式中的示例:

 1 //汽车登记示例
 2   var Car =function(make,model,year,owner,tag,renewDate){
 3     this.make=make;
 4     this.model=model;
 5     this.year=year;
 6     this.owner=owner;
 7     this.tag=tag;
 8     this.renewDate=renewDate;
 9   }
10   Car.prototype = {
11     getMake:function(){
12       returnthis.make;
13     },
14     getModel:function(){
15       returnthis.model;
16     },
17     getYear:function(){
18       returnthis.year;
19     },
20     transferOwner:function(owner,tag,renewDate){
21       this.owner=owner;
22       this.tag=tag;
23       this.renewDate=renewDate;
24     },
25     renewRegistration:function(renewDate){
26       this.renewDate=renewDate;
27     }
28   }
29   //数据量小到没多大的影响,数据量大的时候对计算机内存会产生压力,下面介绍享元模式优化后
30   //包含核心数据的Car类
31   var Car=function(make,model,year){
32     this.make=make;
33     this.model=model;
34     this.year=year;
35   }
36   Car.prototype={
37     getMake:function(){
38       returnthis.make;
39     },
40     getModel:function(){
41       returnthis.model;
42     },
43     getYear:function(){
44       returnthis.year;
45     }
46   }
47   //中间对象,用来实例化Car类
48   var CarFactory=(function(){
49     var createdCars = {};
50     return {
51       createCar:function(make,model,year){
52         var car=createdCars[make+"-"+model+"-"+year];
53         return car ? car : createdCars[make +'-'+ model +'-'+ year] =(new Car(make,model,year));
54       }
55     }
56   })();
57   //数据工厂,用来处理Car的实例化和整合附加数据
58   var CarRecordManager = (function() {
59     var carRecordDatabase = {};
60     return {
61       addCarRecord:function(make,model,year,owner,tag,renewDate){
62         var car = CarFactory.createCar(make, model, year);
63         carRecordDatabase[tag]={
64           owner:owner,
65           tag:tag,
66           renewDate:renewDate,
67           car:car
68       }
69     },
70       transferOwnership:function(tag, newOwner, newTag, newRenewDate){
71         var record=carRecordDatabase[tag];
72         record.owner = newOwner;
73         record.tag = newTag;
74         record.renewDate = newRenewDate;
75       },
76       renewRegistration:function(tag,newRenewDate){
77         carRecordDatabase[tag].renewDate=newRenewDate;
78       },
79       getCarInfo:function(tag){
80         return carRecordDatabase[tag];
81       }
82     }
83   })();

 

代理模式:

代理是一个对象,它可以用来控制对另一个对象的访问。它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象。代理模式适合处理实例化比较费时的本体,也适合处理那些需要较长时间才能把数据载入用户界面的类。

javascript设计模式中的示例:

 1 var Publication =new Interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'setAuthor', 'display']);
 2   var Book =function(isbn, title, author) {
 3       //...
 4   } 
 5   // implements Publication
 6   implements(Book,Publication);
 7 
 8   /* Library interface. */
 9   var Library =new Interface('Library', ['findBooks', 'checkoutBook', 'returnBook']);
10 
11   /* PublicLibrary class. */
12   var PublicLibrary =function(books) {
13       //...
14   };
15   // implements Library
16   implements(PublicLibrary,Library); 
17 
18   PublicLibrary.prototype = {
19       findBooks: function(searchString) {
20          //...
21       },
22       checkoutBook: function(book) {
23           //...
24       },
25       returnBook: function(book) {
26           //...
27       }
28   };
29 
30   /* PublicLibraryProxy class, a useless proxy. */
31   var PublicLibraryProxy =function(catalog) { 
32       this.library =new PublicLibrary(catalog);
33   };
34   // implements Library
35   implements(PublicLibraryProxy,Library);
36 
37   PublicLibraryProxy.prototype = {
38       findBooks: function(searchString) {
39           returnthis.library.findBooks(searchString);
40       },
41       checkoutBook: function(book) {
42           returnthis.library.checkoutBook(book);
43       },
44       returnBook: function(book) {
45           returnthis.library.returnBook(book);
46       }
47   };

 

观察者模式:

观察者模式是一种管理人与其任务之间的关系的得力工具。观察者模式中存在两个角色:观察者和被观察者。这种模式的好处是你可以对程序中某个对象的状态进行观察,并且在其发生改变时能够得到通知。

 1   var f1 =function(){
 2      //code
 3   }
 4   var f2 =function(){
 5      //code
 6   }
 7   addEvent(element,'click',f1);
 8   addEvent(element,'click',f2)
 9 
10 
11   element.onclick = f1;
12   element.onclick = f2;

 

命令模式:

命令模式可以用来对方法调用进行参数化处理和传送,经这样处理过的方法调用可以在任何需要的时候执行。好处是可以用来消除调用操作的对象和实现操作的对象之间的耦合,使对象间的互动方式更高的模块化。这为各种具体的类更换带来了极大的灵活性。

 1 car Calculator={
 2      add:function(x,y){
 3         return x+y;
 4      },
 5      substract:function(x,y){
 6         return x-y;
 7      },
 8      multiply:function(x,y){
 9         return x*y;
10      },
11      divide:function(x,y){
12         return x/y;
13      }
14   }
15   Calculator.calc =function(command){
16      return Calculator[command.type](command.op1,command.opd2)
17   };
18   Calculator.calc({type:'add',op1:1,op2:1});
19   Calculator.calc({type:'substract',op1:5,op2:2});
20   Calculator.calc({type:'multiply',op1:5,op2:2});
21   Calculator.calc({type:'divide',op1:8,op2:4});

 

职责链模式:

职责链模式是通过实现一个由隐式地请求进行处理对象组成的链而做到的。可以用来消除请求的发送者和接收者之间的耦合

javascript内部就使用了这种模式来处理事件捕获和冒泡的问题。

职责链由多个不同类型的对象组成:发送者是发出请求的对象,而接收者则是接收请求并且对其进行处理或传递的对象,请求本身有时也是一个对象,它封装着与操作有关的所有数据。其典型的流程大致是:

  1. 发送者知道链中第一个接收者,它向这个接收者发出请求。
  2. 每一个接收者都对请求进行分析,然后要么处理它,要么将其往下传。
  3. 每一个接收者知道的其他对象只有一个,即它在链中的下家。
  4. 如果没有任何接收者处理请求,那么请求将从链上离开,不同的实现对此也有不同的反应,一般会抛出一个错误。

小结:

  每种模式都有自己的优点,选择一种适合自己业务的模式非常重要,能够提高代码的可读性、可维护性等等,希望本文能够对你有所帮助。

 

本文参考电子书《javascript设计模式》

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
javascript设计模式--封装和信息隐藏(下)
  今天讲解的内容是高级模式(Advanced Patterns),如何实现静态方法和属性,常量还有其他一些知识点。   1.静态方法和属性   其实要实现这一功能还是要借助于闭包。在上一讲中的第三种实现方式也使用了闭包,但通过那种实现,内部属性和方法是实例级别的。
696 0
《JavaScript设计模式》——1.8 方法还可以这样用
没错,但是你发现没,你调用了3个方法,但是你对对象a书写了3遍。这是可以避免的,那就要在你声明的每一个方法末尾处将当前对象返回,在JavaScript中this指向的就是当前对象,所以你可以将它返回。例如我们开始写的第一个对象还记得么?改动它很简单,像下面这样就可以。
986 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
4512 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
7808 0
《JavaScript设计模式》——2.2 包装明星——封装
闭包是有权访问另外一个函数作用域中变量的函数,即在一个函数内部创建另外一个函数。我们将这个闭包作为创建对象的构造函数,这样它既是闭包又是可实例对象的函数,即可访问到类函数作用域中的变量,如bookNum这个变量,此时这个变量叫静态私有变量,并且checkBook()可称之为静态私有方法。
1141 0
javascript设计模式--继承(下)
  本章的主题是继承,在javascript中要实现继承比其他面相对象语言要复杂的多,他主要使用原型实现继承。下面就介绍几种常用的实现继承的方式。   1.经典继承(Classical Inheritance)   我们首先创建一个Person类。
586 0
《JavaScript设计模式》——第2章 写的都是看到的——面向对象编程 2.1两种编程风格——面向过程与面向对象
面向对象编程就是将你的需求抽象成一个对象,然后针对这个对象分析其特征(属性)与动作(方法)。这个对象我们称之为类。面向对象编程思想其中有一个特点就是封装,就是说把你需要的功能放在一个对象里。比如你大学毕业你来公司携带的行李物品没有一件一件拿过来,而是要将他们放在一个旅行箱里,这样不论携带还是管理都会更方便一些。
1229 0
+关注
夕阳白雪
前端开发工程师
125
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
文娱运维技术
立即下载
《SaaS模式云原生数据仓库应用场景实践》
立即下载
《看见新力量:二》电子书
立即下载