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. [comparator] 策略模式

    我们知道策略模式,首先是各种策略可以替换,其实我们常见的Arrays.sort();中可以指定比较器,实现comparator接口的比较器,作为对象传入,这不就是策略模式吗,好理解吧

  2. 如何设置textarea光标默认为第一行第一个字符

    判断文本区是否有内容,如果没有那么光标肯定是在第一行第一个为止的,记住,空格回车也算是有内容在的,也会影响光标的位置

  3. Web 应用性能和压力测试工具 Gor - 运维生存时间

    Web 应用性能和压力测试工具 Gor - 运维生存时间 undefined 无需花生壳,dnspod实现ddns - 推酷 undefined

  4. yii项目开发项目常用技巧和方法汇总

    1.使用CActiveForm类组件如何输出不带html属性的结果 eg:<?php echo $form->textField($model,'email',array('size'=& ...

  5. hdoj 2147 kiki's game【博弈】

    kiki's game Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 40000/10000 K (Java/Others)Total ...

  6. Android 镜像地址[持续更新中]

    这里收集android国内镜像资源地址 大连东软信息学院镜像服务器地址:– http://mirrors.neusoft.edu.cn 端口:80北京化工大学镜像服务器地址:– IPv4: http: ...

  7. 一次mysql瘫痪解救

    最近手机app项目访问流量逐步的增加,对服务端webapi考验极大,是在一次新的业务消息推送后,极光推送给手机接受到的客户端达到19万个,此时app立马开始访问速度变慢了,用户体验相当差 客服接到的问 ...

  8. SQL GROUP BY GROUPING SETS,ROLLUP,CUBE(需求举例)

    实现按照不同级别分组统计 关于GROUP BY 中的GROUPING SETS,ROLLUP,CUBE 从需求的角度理解会更加容易些. 需求举例: 假如一所学校只有两个系, 每个系有两个专业, 每个专 ...

  9. [Redux] Using mapDispatchToProps() Shorthand Notation

    We will learn how to avoid the boilerplate code in mapDispatchToProps() for the common case where ac ...

  10. The content of element type "beans" must match "(description?,(import|alias|bean)*)

    The content of element type "beans" must match "(description?,(import|alias|bean)*) - ...