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. bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit ...

  2. HW4.16

    import java.util.Scanner; public class Solution { public static void main(String[] args) { Scanner i ...

  3. rman进行备份、恢复

    rman备份可以很复杂,但学习需要由浅入深,下面是最常见的几种备份.恢复方式. 备份数据的路径与大小: SQL> show parameter  DB_RECOVERY_FILE_DEST NA ...

  4. iOS开发笔记之TableView优化

    TableView相信只要是做iOS开发的就不会陌生,目前大多数iOS的app都是采用TabBar+NavigationBar+TableViewController这一主流框架, 既然用的这么频繁, ...

  5. C++:复制构造函数在什么时候被调用?

    这个问题不是疑问了,查了一下国外网站,总结一下.假设Person是一个类,复制构造函数的调用会在以下几种情况下发生: 1.对象在创建时使用其他的对象初始化 Person p(q); //此时复制构造函 ...

  6. 为什么String要设计成不可变的?

    英文原:http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/ 转自:http://blog.csdn.net/ren ...

  7. ios9中 UIStackView的使用

    ios9中 UIStackView的使用 by 伍雪颖 UIStackView能够垂直或水平排布多个subview, 自己主动为每一个subview创建和加入Auto Layout constrain ...

  8. ExecutorService(转)

    ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{ } 2.建立ExecutorService线程池 Execut ...

  9. float与double的范围和精度(摘录)

    什么是浮点数在计算机系统的发展过程中,曾经提出过多种方法表达实数.典型的比如相对于浮点数的定点数(Fixed Point Number).在这种表达方式中,小数点固定的位于实数所有数字中间的某个位置. ...

  10. GUI编程笔记(java)10:GUI实现一级菜单

    1.首先:菜单组件 MenuBar,Menu,MenuItem              先创建菜单条,再创建菜单,每一个菜单中建立菜单项.              也可以菜单添加到菜单中,作为子菜 ...