javascript 采用设计模式主要有下面的三方面原因:

  1. 可维护性:设计模式有助于降低模块之间的耦合程度。这使代码进行重构和换用不同的模块变得容易,也使程序员在大型项目中合作变得容易。
  2. 沟通:设计模式为处理不同类型的对象提供了一套通用的术语。程序员可以简洁的描述自己系统的工作方式。
  3. 性能:采用一些优化性能的模式,可以大幅度提高程序的执行效率,如享元模式和代理模式等

同时,滥用设计模式也会带来一些后果:

  1. 复杂性:代码变得复杂,新手难以理解
  2. 性能:多数设计模式会或多或少的降低代码的性能

实现容易,合理使用才是难点。一个建议就是:尽量选用最合适的那种,但不要过度的牺牲性能。

接口:接口是用来说明该对象具有哪些方法的手段,尽管其表明这些方法的语义,但是它不规定实现。

在Javascript中模仿接口

一:用注释描述接口  

/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem{
function save();
}
*/ var CompositeForm = function(id,method,action){ }
CompositeForm.prototype.add = function(){ }
CompositeForm.prototype.remove = function(){ }
CompositeForm.prototype.getChild = function(){ }
CompositeForm.prototype.save = function(){ }

这种模仿只是停留在文档阶段。没有对是否实现正确的方法进行检查,也不会抛出错误,完全依靠自觉。

二:用属性检查模仿接口:

/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem{
function save();
}
*/ var CompositeForm = function(id,method,action){
this.implementsInterfaces = ['Composite','FormItem']; } function addForm(formInstance){
if(!implements(formInstance,'Composite','FormItem'){
throw new Error("Object does not implement a required interface.");
})
} function implements(object){
for(var i=1;i<arguments.length;i++){
var interfaceName = argument[i];
var interfaceFound = false;
for(var j=0;j<object.implementsInterfaces.length;j++){
if(object.implementsInterfaces[j]==interfaceName){
interfaceFound = true;
break;
}
}
//有接口没有找到
if(!interfaceFound){
return false;
}
}
//找到了所有的接口
return true;
}

这里,CompositeForm 宣称自己实现了'Composite','FormItem'这俩个接口,其做法是把这俩个接口的名称加入到一个对象的数组中,显式的声明自己支持的接口。

任何一个要求其参数属于特定类型的函数都可以对这个属性进行检查,并在找不到实现方法的时候,抛出异常。

三、鸭式辨型模仿接口

它把对象实现的方法集作为判断它是不是某个类的唯一标准,这种方法背后的观点很简单:如果对象具有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。

Interface 类的定义

/*
* 定义接口方法
*/
var Interface = function(name,methods){
if(arguments.length!=2){
throw new Error("Interface constructor called with " + arguments.length +
"arguments, but expected exactly 2.");
}; this.name = name ;
this.methods = []; for (var i = 0,len = methods.length - 1; i < len; i++) {
if(typeof methods[i]!=='string'){
throw new Error("Interface constructor expects method names to be "
+ "passed in as a string.");
}
this.methods.push(methods[i]);
};
}; /*
* 给Interface扩展静态验证方法
*/ Interface.ensureImplements = function(obj){
if(arguments.length<2){
throw new Error("Function Interface.ensureImplements called with " +
arguments.length + "arguments, but expected at least 2.");
}; for (var i = 0,len = methods.length - 1; i < len; i++) {
var interface = arguments[i];
if(interface.constructor!==interface){
throw new Error("Function Interface.ensureImplements expects arguments "
+ "two and above to be instances of Interface.");
}; for (var j = 0,methodsLen = interface.methods.length; j<methodsLen; j++) {
var method = interface.methods[j];
if(!object[method]||typeof object[method] !== 'function'){
throw new Error("Function Interface.ensureImplements: object "
+ "does not implement the " + interface.name
+ " interface. Method " + method + " was not found.");
}; };
};
}

继承

链式继承的实现

function extend(subClass,superClass){
var F = function(){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.peototype.constructor = subClass; subClass.superclass = superClass.prototype;
if(superClass.prototype.constructor == Object.prototype.constructor){
superClass.prototype.constructor = superClass;
}
}

superclass 用来弱化子类和超类之间的耦合。同时确保超类的constructor被正确设置。

原型继承的实现,实际上是拷贝继承,实现方式如下。

function clone(object){
function F(){};
F.prototype = object;
return new F;
}

实际上返回的是一个以给定对象为原生对象的空对象。

掺元类的实现。有一种重用的方法不需要用到严格的继承,如果想把一个函数用到多个类中,可以扩充的方式让这些类共享该函数,做法:先创建一个包含各种通用方法的类,然后再用它扩充其他的类,这种包含通用方法的类称为掺元类。

掺元类的实现方法:

function augment(receivingClass,givingClass){
//if has the third arg
if(arguments.length[2]){
for(var i=2,len = arguments.length;i<len;i++){
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
}
}else{
for(methodName in givingClass.prototype){
if(!receivingClass.prototype[methodName]){
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
}
}
}

封装

封装就是对对象内部的数据表现形式和实现细节进行隐藏。如果想访问封装过的对象,只能使用已定义的操作这一种方式。

在javascript中,我们没有关键字,只能使用闭包的概念来创建私有的属性和方法。

javascript 想创建对象的基本模式有三种。

门户大开式,用下划线表示私有方法和属性,用闭包来创建真正的私有成员。

var Book = function(newIsbn,newTitle,newAuthor){

    //私有属性
var isbn,title,anthor;
function checkIsbn(isbn){
...
}; //特权方法,可以访问私有变量
this.getIsbn = function(){
return isbn;
};
this.setIsbn = function(){
if(!checkIsbn(newIsbn)) throw new Error('Book:Invalid ISBN.');
isbn = newIsbn;
};
this.getTitle = function(){
return newTitle;
};
this.setTitle = function(newTitle){
title = newTiyle||'';
};
this.getAuthor = function(){
return author;
};
this.setAuthor = function(newAuthor){
author = newAuthor||'';
}; this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
}
//不需要直接访问私有属性的方法都可以在prototype中声明。
Book.prototype = {
display:function(){...}
}

更高级的对象创建。

var Book = (function(){
//私有静态属性
var numOfBooks = 0;
//私有静态方法
function checkIsbn(isbn){
...
}; return function(newIsbn,newTitle,newAuthor){
//私有属性
var isbn,title,anthor; //特权方法,可以访问私有变量
this.getIsbn = function(){
return isbn;
};
this.setIsbn = function(){
if(!checkIsbn(newIsbn)) throw new Error('Book:Invalid ISBN.');
isbn = newIsbn;
};
this.getTitle = function(){
return newTitle;
};
this.setTitle = function(newTitle){
title = newTiyle||'';
};
this.getAuthor = function(){
return author;
};
this.setAuthor = function(newAuthor){
author = newAuthor||'';
}; numOfBooks++;
if(numOfBooks > 50) throw new Err('Book:only 50 instances of Book can be created.');
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
} })();
//不需要直接访问私有属性的方法都可以在prototype中声明。
Book.prototype = {
display:function(){...}
}

一些简单的说明:

  • 首先,用()()创建了一个闭包,立即执行,并返回另一个函数。
  • 其次,返回函数被赋值给Book,成为一个构造函数。
  • 三次,实例化Book调用的内层函数,外层只是用于创建一个可以存放静态私有成员的闭包。
  • 四次,checkIsbn被设计为静态方法,因此Book的每个实例都不会生成该静态方法,该方法作用在构造类上,而不是实例上。
  • 五次,这些静态方法因为包含在闭包中,所以闭包中的其它方法可以访问,而且内存中只存在一份。
  • 六次,这些方法声明在返回构造器之外,不是特权方法,所以不能访问定义在构造器中的私有属性。
  • 七次,判断一个私有方法是否应该被设计为静态方法,一条经验法则则是看它是否要访问任何实例数据,如果不需要,那么将其设计为静态方法则更有效率。

读书笔记之 - javascript 设计模式 - 接口、封装和链式调用的更多相关文章

  1. 读书笔记之 - javascript 设计模式 - 观察者模式

    在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式是一种管理人与其任务(确切的讲,是对象及其行为和状态之间的关系)之间的关系的得力工具.用javascript的话来讲,这种模式的实 ...

  2. 如何写 JS 的链式调用 ---》JS 设计模式《----方法的链式调用

    1.以$ 函数为例.通常返回一个HTML元素或一个元素集合. 代码如下: function $(){ var elements = []; ;i<arguments.length;i++){ v ...

  3. javascript学习(10)——[知识储备]链式调用

    上次我们简单的说了下单例的用法,这个也是在我们java中比较常见的设计模式. 今天简单说下链式调用,可能有很多人并没有听过链式调用,但是其实只要我简单的说下的话,你肯定基本上都在用,大家熟知的jQue ...

  4. 读书笔记之 - javascript 设计模式 - 命令模式

    本章研究的是一种封装方法调用的方式.命令模式与普通函数有所不同.它可以用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行. 它也可以用来消除调用操作的对象和实现操作的 ...

  5. 读书笔记之 - javascript 设计模式 - 代理模式

    代理(proxy)是一个对象,它可以用来控制对另一对象的访问.它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象.另外那个对象通常称为本体.代理可以代替本体被实例化,并使其可被远程访 ...

  6. 读书笔记之 - javascript 设计模式 - 享元模式

    本章探讨另一种优化模式-享元模式,它最适合于解决因创建大量类似对象而累及性能的问题.这种模式在javascript中尤其有用,因为复杂的javascript代码很快就会用光浏览器的所有可用内存,通过把 ...

  7. 读书笔记之 - javascript 设计模式 - 工厂模式

    一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一 ...

  8. 读书笔记之 - javascript 设计模式 - 责任链模式

    责任链模式可以用来消除请求的发送者和接收者之间的耦合.这是通过实现一个由隐式地对请求进行处理的对象组成的链而做到的.链中的每个对象可以处理请求,也可以将其传给下一个对象. 责任链的结构: 责任链由多个 ...

  9. 读书笔记之 - javascript 设计模式 - 装饰者模式

    本章讨论的是一种为对象增添特性的技术,它并不使用创建新子类这种手段. 装饰者模式可以透明地把对象包装在具有同样接口的另一对象之中,这样一来,你可以给一些方法添加一些行为,然后将方法调用传递给原始对象. ...

随机推荐

  1. Java 性能优化技巧及实战

    关于Java代码的性能优化,是每个javaer都渴望掌握的本领,进而晋升为大牛的必经之路,但是对java的调优需要了解整个java的运行 机制及底层调用细节,需要多看多读多写多试,并非一朝一夕之功.本 ...

  2. oc学习之路----多级指针的使用和内存分析

    ---恢复内容开始--- 精髓:要熟悉指针的使用,首先要熟悉指针的各种状态存得是什么数据.(以一级指针 int *p1 二级指针:int **p2 三级指针:int ***p3为例) 一级指针:*p1 ...

  3. Android开发:如何安全的中止一个自定义线程Thread

    http://blog.csdn.net/yanzi1225627/article/details/8582078 经研究,我推荐这种写法: /*自定义线程*/ class MyThread impl ...

  4. [TypeScript] Stopping a TypeScript Build When Errors Are Found

    TypeScript will always compile even if there are ridiculous errors in your project. This lesson show ...

  5. 【转】cocos2d-x学习笔记03:绘制基本图元

    第一部分:基本图形绘制 cocos2dx封装了大量opengl函数,用于快速绘制基本图形,这些代码的例子在,tests\DrawPrimitivesTest目录下 注意,该方法是重载node的draw ...

  6. C++使用Json作为数据包装格式的通信

    出处:http://adebugger.cn/2009/11/cpp-json-data-communication/ http://hi.baidu.com/tibelf/item/6be2accd ...

  7. C#泛型的性能优势

    我写东西一向追求短小精悍,就不放代码去验证的,只说结论,并会与Java泛型做对比.有不对之处还望指出. 泛型作为一个在C#2.0中就引入的特性,也是C#的重要特性之一,我经常看到有人讨论泛型带来的便捷 ...

  8. springMVC工作原理图

  9. .net缓存应用与分析

    在 ASP.NET 提供的许多特性中,相比 ASP.NET 的所有其他特性,缓存对应用程序的性能具有最大的潜在影响,利用缓存和其他机制,ASP.NET 开发人员可以接受使用开销很大的控件(例如,Dat ...

  10. Java基础知识强化之网络编程笔记02:Socket通信原理图解

    1. Socket (1)Socket套接字  网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字 (2)Socket原理机制:  • 通信两端都有Socket.  • 网 ...