读书笔记:js设计模式

面向过程编程,面向对象编程和函数式编程
> 定义一个类
方法1:
function Anim(){

}

Anim.prototype.start = function(){ .. };
Anim.prototype.stop = function(){ .. };

或者
方法2:
function Anim(){ .. }
Anim.prototype = {
start: function(){.. },
stop: function(){ .. },
constructor: Anim
}

或者
方法3:
//从视觉上 整个类都在构造函数中定义
function Anim(){
...
if(!Anim.prototype.version){ //假设version是这个类 肯定具备的方法
Anim.prototype.version = ‘1.0‘;
Anim.prototype.start = function(){..};
Anim.prototype.stop = function(){...};
}
}

new Anim(); //第1次new的时候 完成对原型对象的定义

或者
方法4:
把向函数原型对象添加方法的过程定义为函数,如:
Function.prototype.method = function(name, fn){
this.prototype[name] = fn;
return this; //使可以链式调用
}

//与第1中方法没什么差别
function Anim(){...}
Anim.method(‘start‘, function(){..});
Anim.method(‘stop‘, function(){..});

> js的数据类型:原始类型 number string boolean null undefined, 引用类型 object ( array, function, regexp, math, number, string, boolean)
原始数据类型按值传递; 引用类型按引用传递(即引用地址)
数据类型转换:
toString();
parseInt();
parseFloat();
!!number
注意 js的隐式类型转换

> 函数是一等对象,可以存储在变量中,可以作为参数传递,可以作为函数返回值返回。
匿名函数可以激发一个作用域,无影响全局环境,实现数据封装
(function(){
var foo = 10;
var bar = 2;
alert(foo * bar);
})();

var result = (function(foo, bar){
return foo * bar
})(20, 10);

js具有函数作用域,js的作用域是词法作用域(函数运行在定义它的作用域中,而不是在调用它的作用域中)
匿名函数最有趣的用途是创建闭包。
用匿名函数创建闭包的例子:
var baz;
(function(){
var foo = 10;
var bar = 2;
baz = function(){
return foo * bar;
}
})();
baz(); //20

> js的对象都是易变的
/* class Person */
function Person(name, age){
this.name = name;
this.age = age;
}
Person.prototype = {
getName: function(){ reutn this.name },
getAge: function(){ return this.age },
constructor: Person
}

/* instantiate the class */
var alice = new Person(‘Alice‘, 19);

/* modify the class */
Person.prototype.geeting = function(){ return ‘Hi,‘ + this.getName + ‘!‘};

/* modify the instance */
alice.displayGeeting = function(){
alert(this.geeting());
}

js中,任何东西都能够在运行时修改

> 继承
原型式继承 和 类式继承 两种继承范型

在js中使用设计模式的好处:可维护性,性能,程序员间沟通;缺点:复杂性 性能(有的模式对性能有负面影响,有的能提高性能)

-----------------------------------
接口
-----------------------------------
接口:提供了一种说明一个对象应该具有什么方法的手段
假设一批对象实现了相同的接口,那么即使他们彼此不相关都可以被同样对待
如: 一批对象都实现了 comparable接口, 那么就可以调用 obj.compare(obj2);

接口可以告诉程序员一个类实现了哪些方法,接口说明了一个类具有哪些特性和操作

> js中的模仿接口的方法: 注释法,属性检查法,鸭式辨型

1.注释法:
/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}

interface FormItem{
function save();
}
*/

var CompositeForm = function(id, method, action){//implements Composite, FormItem
...
};

//implement the Composite interface
CompositeForm.prototype.add = function(child){ .. };
CompositeForm.prototype.remove = function(child){ .. };
CompositeForm.prototype.getChild = function(index){ ..};

//implement the FormItem interface
CompositeForm.prototype.save = function(){...};

依靠程序员自觉遵守接口约定,不影响执行性能;但是没有错误提示

2.属性检查法:

/*
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(‘instance do not implement the required interfaces‘);
}
...
}

//检查所需接口是否在实例对象自称实现的接口数组中能找到
~~ 检查传入的接口名,是否都在自称实现的接口数组中能找到
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; //没有中途退出 即所有接口都能找到
}

3.鸭式辨型:走路嘎嘎叫的就是鸭子
如果对象具有与接口定义的方法同名的所有方法,就认为它实现了这个接口

/* class Interface */
var Composite = new Interface(‘Composite‘,[‘add‘, ‘remove‘, ‘getChild‘]);
var FormItem = new Interface(‘FormItem‘,[‘save‘]);

//class CompositeForm
var CompositeForm = function(id, method, action){
...
}

function addForm(formInstance){
ensureImplements(formInstance, Composite, FormItem);
...
}

保证类真正实现了接口定义的方法,缺点:类没有声明自己实现了哪些接口

比较好的方法是结合注释法 + 鸭式辨型 如:
// Interfaces
var Composite = new Interface(‘Composite‘,[‘add‘, ‘remove‘, ‘getChild‘]);
var FormItem = new Interface(‘FormItem‘,[‘save‘]);

//class CompositeForm
var CompositeForm = function(id, method, action){ //implements Composite, FormItem
...
}

function addForm(formInstance){
ensureImplements(formInstance, Composite, FormItem);
...
}

/** class Interface **/
function Interface(name, methods){
if(arguments.length !== 2){
throw new Error(‘expect 2 arguments‘);
}
this.name = name;
this.methods = [];
if(methods.constructor !== Array){ throw new Error(‘expect an array for methods param‘)}
for(var i =0, len=methods.length; i<len; i++){
if(typeof methods[i] !==‘string‘){
throw new Error(‘expect string value‘);
}
this.methods.push(methods[i]);
}
}

/** ensureImplements method **/
function ensureImplements(object){
if(arguments.length < 2){ throw new Error(‘expect arguments at least 2‘); }
for(var i=1, len=arguments.length; i<len; i++){
var interface = arguments[i];//遍历接口
if(interface.constructor !== Interface){
throw new Error(‘expect this argument to be Interface instance‘);
}
for(var j=0,mLen=interface.methods.length; j<mLen; j++){//遍历接口的方法
var method = interface.methods[j];
if(!object[method] || typeof object[method] !==‘function‘){
//若object没有跟接口方法同名的方法 则抛出异常
throw new Error(‘not the method:‘+method);
}
}
}
}

接口降低了对象间的耦合度,提高了代码的灵活性,不要求对象间有继承关系,只要它们实现了同样的接口,就可以同等对待。
接口记载着api,可作为程序员交流的工具
应用场景:对外部提供的服务api如搜索 地图 邮件等,创建一个Interface对象,对接收到每个对象做接口检查,如:
var DynamicMap = new Interface(‘DynamicMap‘,[‘centerOnPoint‘,‘zoom‘,‘draw‘]);

function displayRoute(mapInstance){//自己用到的实例方法做成接口,保证传入的实例确实有这些方法
ensureImplements(mapInstance, DynamicMap);
mapInstance.centerOnPoint(12,34);
mapInstance.zoom(3);
mapInstance.draw();
...
}

是否需要使用接口做检查,取决于项目的大小,和做检查的必要性。
对传入对象做接口检查(是否实现某个接口)比做类检查(是否某个类),要有保证和灵活一点。

依赖于接口的设计模式
工厂模式、组合模式、装饰者模式、命令模式

>> 封装和信息隐藏
为对象创建私有成员是面向对象语言最基本和最有用的特性之一。封装是面向对象的基石。
js中的封装是通过闭包来实现的。
信息隐藏有助于减轻功能模块间的依赖度,只需要获得结果,不需要知道具体的内部实现。
封装:对对象内部数据和实现细节的隐藏,许多面向对象语言用关键字声明 该属性或方法为隐藏的(私有的), JAVA中private
,js中用闭包

封装:隐藏对象内部的数据和实现细节,只能用已定义的方法访问对象的数据。

>> 创建对象的基本模式

> 门户大开型对象 (对象的所有属性都是公共成员)
var Book = function(isbn, title, author){
if(isbn == undefined) throw new Error(‘isbn is required‘); //没检查isbn是否正确,可能影响数据库检索和display图书信息
//to => if(!this.checkIsbn(isbn)) throw new Error(‘isbn is invalid‘);
this.isbn = isbn;
this.title = title || ‘no title specified‘;
this.author = author || ‘no author specified‘;
}
Book.prototype.display = function(){
... //生成显示图书信息的元素
}
Book.prototype.checkIsbn = function(){
...
}

---------
改为:只能通过getter,setter访问门户大开型对象的属性
var Publication = new Interface(‘Publication‘,[‘getIsbn‘,‘setIsbn‘,‘getTitle‘,‘setTitle‘,‘getAuthor‘, ‘setAuthor‘,‘display‘]);
var Book = function(isbn, title, author){
this.setIsbn(isbn);
this.setTitle(title);
this.setAuthor(author);
}
Book.prototype = {
constructor: Book,
getIsbn: function(){return this.isbn},
setIsbn: function(isbn){ if(!this.checkIsbn(isbn) ){ throw new Error(‘invalid isbn‘)}else{ this.isbn = isbn } },
getTitle: function(){ return this.title},
setTitle:function(title){ return this.title = title || ‘no specified title‘},
getAuthor: function(){ return this.author},
setAuthor:function(Author){ return this.author = author || ‘no specified Author‘},
display: function(){...},
checkIsbn: function(){..}
}
但是
book = new Book(‘0-234-234-034-9‘,‘something in life‘, ‘kitty‘);
book.isbn = ‘0-000-000-000-2‘; //实例对象的属性还是可以任意修改的
优点:简单,易于理解
缺点:所有属性和方法公开 无法保护内部数据,getter,setter增加代码量

> 用命名规范区别私有成员 ( 属性名加_前缀表明其为私有,如:_name = ‘kitty book‘ )
var Book = function(isbn, title, author){
this.setIsbn(isbn);
this.setTitle(title);
this.setAuthor(author);
}
Book.prototype = {
constructor: Book,
getIsbn: function(){return this._isbn},
setIsbn: function(isbn){ if(!this.checkIsbn(isbn) ){ throw new Error(‘invalid isbn‘)}else{ this._isbn = isbn } },
getTitle: function(){ return this._title},
setTitle:function(title){ return this._title = title || ‘no specified title‘},
getAuthor: function(){ return this._author},
setAuthor:function(Author){ return this._author = author || ‘no specified Author‘},
display: function(){...},
checkIsbn: function(){..}
}

> 作用域 嵌套函数和闭包
js只有函数作用域,而且是词法作用域,函数是运行在定义它们的作用域,而不是调用它们的作用域的。
返回一个内嵌函数是创建闭包的常用方法。

var Book = function(newIsbn, newTitle, newAuthor){// implements Publication
var isbn, title, author; //私有属性
function checkIsbn(isbn){...} //私有方法
this.getIsbn = function(){ return isbn }; //特权方法
this.setIsbn = function(newIsbn){ if(checkIsbn(newIsbn)) isbn = newIsbn; };//特权方法
this.getTitle = ..
this.setTitle = ..
this.getAuthor = ..
this.setAuthor = ..

//初始化对象私有数据
this.setIsbn(newIsbn);
this.setTitle(newTitle);
this.setAuthor(newAuthor);
}

Book.prototype = {
construcor: Book,
display: function(){..} //公共方法,非特权方法
}

读书笔记:js设计模式

时间: 2024-10-22 16:27:39

读书笔记:js设计模式的相关文章

5分钟读书笔记之 - 设计模式 - 工厂模式

一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一种模式,它使用一种方法来决定究竟实例化那个具体的类. 简单工厂模式 假设你想开几个自行车商店,每个商店都有几种型号的自行车出售,可以用这样一个类来表示: var BicycleShop = function(){} BicycleShop.prototype = { sellBicycle:func

5分钟读书笔记之 - 设计模式 - 桥接模式

补充一点知识: 私有变量 在对象内部使用'var'关键字来声明,而且它只能被私有函数和特权方法访问.私有函数 在对象的构造函数里声明(或者是通过var functionName=function(){...}来定义),它能被特权函数调用(包括对象的构造函数)和私有函数调用.特权方法 通过this.methodName=function(){...}来声明而且可能被对象外部的代码调用.可以使用:this.特权函数() 方式来调用特权函数,使用 :私有函数()方式来调用私有函数.公共属性 通过thi

【读书笔记】设计模式第6章:行为型模式2

本文主要分析了中介者模式.观察者模式.备忘录模式.访问者模式.状态模式.解释器模式,介绍它们的定义.优缺点.使用场景,以及实例代码.为了深刻地理解设计模式,最重要的还是动手编写代码. 我参照书中的例程重新构想了一些更加生动.易于理解的例子,希望大家喜欢. 代码可以通过以下链接进行浏览: http://git.oschina.net/caipeichao/java-design-pattern 这些代码都经过编译运行,保证没有错误. 中介者模式 定义 也叫调停者模式 用一个中介对象来封装一系列同事

【读书笔记】设计模式第五章:行为型模式

本文主要分析了模板方法模式.命令模式.责任链模式.策略模式.迭代器模式,介绍它们的定义.优缺点.使用场景,以及实例代码.为了深刻地理解设计模式,最重要的还是动手编写代码. 我参照书中的例程重新构想了一些更加生动.易于理解的例子,希望大家喜欢. 代码可以通过以下链接进行浏览: http://git.oschina.net/caipeichao/java-design-pattern 这些代码都经过编译运行,保证没有错误. 模板方法 定义 定义一个操作中的算法框架,而将一些步骤延迟到子类中 角色:抽

读书笔记 - js高级程序设计 - 第十五章 使用Canvas绘图

读书笔记 - js高级程序设计 - 第十三章 事件 canvas 具备绘图能力的2D上下文 及文本API 很多浏览器对WebGL的3D上下文支持还不够好 有时候即使浏览器支持,操作系统如果缺缺乏必要的绘图驱动程序,则浏览器即使支持了也没用   <canvas> var drawing = document.getElementById("drawing"); if( drawing.getContext ){ drawing.getContext("2d"

5分钟读书笔记之 - 设计模式 - 门面模式

门面模式有俩个作用: 简化类的接口 消除类与使用它的客户代码之间的耦合 在javascript中,门面模式常常是开发人员最亲密的朋友.它是几乎所有javascript库的核心原则,门面模式可以使库提供的工具更容易理解.使用这种模式,程序员可以间接地与一个子系统打交道,与直接访问子系统相比,这样做更不容易出错. addEvent函数是一个基本的门面,你不用在每次为一个元素添加事件监听器的时候都得针对浏览器间的差异进行检查,有了这个便利,你可以把这个添加事件的底层细节抛在脑后,而把心思集中在如何构建

5分钟读书笔记之 - 设计模式 - 装饰者模式

本章讨论的是一种为对象增添特性的技术,它并不使用创建新子类这种手段. 装饰者模式可以透明地把对象包装在具有同样接口的另一对象之中,这样一来,你可以给一些方法添加一些行为,然后将方法调用传递给原始对象.相对于创建子类来说,使用装饰者模式对象是一种更灵活的选择. 装饰者可用于为对象增加功能.它可以用来替代大量子类. 考虑前面的自行车类,你现在可能提供一些配件供用户选择,装饰者模式要求我们只需要创建选件类,这些类与四种自行车类都要实现Bicycle接口,但是他们只被用作这些自行车类的包装类.在这个例子

5分钟读书笔记之 - 设计模式 - 适配器模式

适配器模式可以用来在现在接口和不兼容的类之间进行适配. 使用这种模式的对象又叫包装器,因为他们是在用一个新接口包装另一个对象. 在设计类的时候往往遇到有些接口不能与现有api一同使用的情况,借助于适配器,你可以不用直接修改这些类也能使用他们. 适配器的特点: 适配器可以被添加到现有代码中以协调俩个不同的接口.从表面上来看,适配器模式很像门面模式,他们都要对别的对象进行包装并改变其呈现的接口,二者之间的差别在于他们如何改变接口.门面元素展现的是一个简化接口,它并不提供额外的选择,而且有时是为了方便

5分钟读书笔记之 - 设计模式 - 单体模式

单体是一个用来划分命名空间,并将一批相关方法和属性组织在一起的对象,如果它可以被实例化,那么它只能被实例化一次. 单体模式,就是将代码组织为一个逻辑单元,这个逻辑单元中的代码可以通过单一的变量进行访问. 单体基本结构是这样: var Singleton = { attribute1:true, attribute2:10, method1:function(){}, method2:function(){} } 借助闭包实现单体: Namespace.Singleton = {} 定义之后立即执