这篇博客只是自己对设计模式的理解的备忘~

看完了《JavaScript设计模式》这本书,一直没有写博客记录一下,最近抽出时间来重读了一下,就顺便记录一下~

如果你只是想粗略了解一下JavaScript的设计模式,继续读下去,就好了,如果你想详细了解的话,推荐汤姆大叔的系列博客 深入理解JavaScript系列

下面有些内容也是摘自汤姆大叔的博客~~

1.Constructor(构造器)模式

声明一个首字母大写的function,通过在构造器前面加new关键字,实例化一个对象。

可以使用原型来定义函数,原型定义的函数,不同的对象可以共用。

例子

// Constructor pattern
function Car(modal, year, miles) {
this.modal = modal;
this.year = year;
this.miles = miles; // 这个方法会覆盖prototype中的toString方法
this.toString = function() {
return this.modal + " in object toString function";
}
} Car.prototype.toString = function() {
return this.modal + " in prototype toString function";
}; var a = new Car("a", 2009, 20000);
var b = new Car("b", 2011, 1000); console.log(a.toString());
console.log(b.toString());

2.Module(模块)模式

可以直接声明一个module对象。也可以执行一个函数,返回一个module对象,执行函数时可以引入一些变量(如JQuery、Underscore)。第二种方式可以引入私有变量和私有函数。

例子

// Module pattern
// 引入了jQuery和Underscore
var myModule = (function(jQ) {
// 私有变量
var privateVar = "private";
// 私有函数
function privateMethod() {
jQ("body").html("test");
}; return {
// 公有变量
publicVar: "public",
// 公有函数
publicMethod: function() {
privateVar += "Var";
privateMethod();
console.log(privateVar);
}
};
})(jQuery);
// 调用公有函数
myModule.publicMethod()

优点:对拥有面向对象背景的开发人员来说更整洁,而且它支持私有数据。

缺点:如果要修改可见性(即公有还是私有)时,必须修改每一个曾经使用过该成员的地方,之后也无法在方法里添加私有成员。

3.Revealing Module(揭示模块)模式

将公有指针指向私有的函数和属性上,个人感觉有点像Java的对象。

例子

// Revealing Module pattern
var myRevealingModule = function() {
var privateVar = "Harry",
publicVar = "Potter"; function privateFunction() {
console.log("Name : " + private);
} function publicSetName(strName) {
privateVar = strName;
} function publicGetName() {
privateFunction();
} // 将暴露的公有指针指向到私有函数和属性上
return {
setName: publicSetName,
getName: publicGetName,
familyName: publicVar
}
}

优点:可以使脚本语法一致,很容易看出哪些函数和变量可以被公开访问,可读性高。

缺点:如果一个私有函数引用了一个共有函数,在将公有函数替换掉后,只是替换了公有指针的指向,私有函数还是用调用之前的函数。

4.Singleton(单例)模式

单例模式限制了类只能实例化一次。在实例不存在的时候,它会通过一个方法创建类的新实例,如果实例已经存在,它会直接返回该对象的引用。Singleton不同于静态类(或对象),可以延迟初始化。

在JavaScript中,Singleton充当共享资源命名空间,从全局命名空间中隔离出代码实现,从而为函数提供单一访问点。

例子

// Singleton pattern
var mySingleton = (function() {
// 实例保持了Singleton的一个引用
var instance; function init() {
// Singleton
// 私有函数和变量
function privateMethod() {
console.log("I am private");
}
var privateVar = "I am also private";
var privateRandomNumber = Math.random();
return {
// 公有函数和变量
publicMethod: function() {
console.log("I am public");
},
publicProperty: "I am also public",
getRandomNumber: function() {
return privateRandomNumber;
}
};
};
return {
// 获取Singleton的实例,如果存在就返回,不存在就创建新实例
getInstance: function() {
if(!instance) {
instance = init();
}
return instance;
}
}
})(); var singleA = mySingleton.getInstance();
var singleB = mySingleton.getInstance();
console.log(singleA.getRandomNumber() === singleB.getRandomNumber());// true

模式的适用性

  • 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。
  • 该唯一的实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

5.观察者(Observer)模式

观察者模式定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。

例子

// Observer pattern
function Observer() {
this.fns = [];
}
Observer.prototype = {
subscribe: function(fn) {
this.fns.push(fn);
},
unsubscribe: function(fn) {
this.fns = this.fns.filter(
function(el) {
if (el !== fn) {
return el;
}
}
);
},
update: function(o, thisObj) {
var scope = thisObj || window;
this.fns.forEach(
function(el) {
el.call(scope, o);
}
);
}
}; //测试
var o = new Observer;
var f1 = function(data) {
console.log('Robbin: ' + data + ', 赶紧干活了!');
}; var f2 = function(data) {
console.log('Randall: ' + data + ', 找他加点工资去!');
}; o.subscribe(f1);
o.subscribe(f2); o.update("Tom回来了!") //退订f1
o.unsubscribe(f1);
//再来验证
o.update("Tom回来了!"); /* // 如果提示找不到filter或者forEach函数,可能是因为你的浏览器还不够新,暂时不支持新标准的函数,你可以使用如下方式自己定义
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (fn, thisObj) {
var scope = thisObj || window;
for (var i = 0, j = this.length; i < j; ++i) {
fn.call(scope, this[i], i, this);
}
};
}
if (!Array.prototype.filter) {
Array.prototype.filter = function (fn, thisObj) {
var scope = thisObj || window;
var a = [];
for (var i = 0, j = this.length; i < j; ++i) {
if (!fn.call(scope, this[i], i, this)) {
continue;
}
a.push(this[i]);
}
return a;
};
}
*/

观察者的使用场合就是:当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

总的来说,观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。

6.Mediator(中介者)模式

中介者是一种行为设计模式,它允许我们公开一个统一的接口,系统的不同部分可以通过该接口进行通信。如果一个系统的各个组件之间有太多直接关系,可以创建一个控制点,各个组件通过这个控制点进行通讯。简单点说,就是有个控制中心控制着各个组件之间的通讯,那个控制中心就是中介者。

高级代码可查看Mediator.js https://github.com/ajacksified/Mediator.js

例子

<!doctype html>
<html lang="en">
<head>
<title>JavaScript Patterns</title>
<meta charset="utf-8">
</head>
<body>
<div id="results"></div>
<script>
function Player(name) {
this.points = 0;
this.name = name;
}
Player.prototype.play = function () {
this.points += 1;
mediator.played();
};
var scoreboard = { // 显示内容的容器
element: document.getElementById('results'), // 更新分数显示
update: function (score) {
var i, msg = '';
for (i in score) {
if (score.hasOwnProperty(i)) {
msg += '<p><strong>' + i + '<\/strong>: ';
msg += score[i];
msg += '<\/p>';
}
}
this.element.innerHTML = msg;
}
}; var mediator = { // 所有的player
players: {}, // 初始化
setup: function () {
var players = this.players;
players.home = new Player('Home');
players.guest = new Player('Guest');
}, // play以后,更新分数
played: function () {
var players = this.players,
score = {
Home: players.home.points,
Guest: players.guest.points
}; scoreboard.update(score);
}, // 处理用户按键交互
keypress: function (e) {
e = e || window.event; // IE
if (e.which === 49) { // 数字键 "1"
mediator.players.home.play();
return;
}
if (e.which === 48) { // 数字键 "0"
mediator.players.guest.play();
return;
}
}
}; // go!
mediator.setup();
window.onkeypress = mediator.keypress; // 30秒以后结束
setTimeout(function () {
window.onkeypress = null;
console.log('Game over!');
}, 30000);
</script>
</body>
</html>

中介者模式一般应用于一组对象已定义良好但是以复杂的方式进行通信的场合,一般情况下,中介者模式很容易在系统中使用,但也容易在系统里误用,当系统出现了多对多交互复杂的对象群时,先不要急于使用中介者模式,而是要思考一下是不是系统设计有问题。

另外,由于中介者模式把交互复杂性变成了中介者本身的复杂性,所以说中介者对象会比其它任何对象都复杂。

7.Prototype(原型)模式

Prototype模式为一种基于现有对象模板,通过克隆方式创建对象的模式。

可以通过Object.create创建一个拥有指定原型和对象的属性,也可以通过函数模仿构造函数创建。

例子

// Prototype pattern
// 使用Object.create方法
var vehicle = {
getModel: function () {
console.log('车辆的模具是:' + this.model);
}
};
// 可以在Object.create的第二个参数里使用对象字面量传入要初始化的额外属性.
// 其语法与Object.defineProperties或Object.defineProperty方法类型。
// 它允许您设定属性的特性,例如enumerable, writable 或 configurable。
var car = Object.create(vehicle, {
'id': {
value: MY_GLOBAL.nextId(),
enumerable: true // 默认writable:false, configurable:false
},
'model': {
value: '福特',
enumerable: true
}
}); // 模仿一个构造函数
var beget = (function(){
function F() {}
return function (proto) {
F.prototype = proto;
}
})();

8.Command(命令)模式

用于将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及执行可撤销的操作。也就是说改模式旨在将函数的调用、请求和操作封装成一个单一的对象,然后对这个对象进行一系列的处理。此外,可以通过调用实现具体函数的对象来解耦命令对象与接收对象。

例子

// Command pattern
var CarManager = {
// 请求信息
requestInfo: function(model, id) {
return 'The information for ' + model + ' with ID ' + id + ' is foobar';
},
// 购买汽车
buyVehicle: function(model, id) {
return 'You have successfully purchased Item ' + id + ', a ' + model;
},
// 组织view
arrangeViewing: function(model, id) {
return 'You have successfully booked a viewing of ' + model + ' ( ' + id + ' ) ';
}
};
// 添加执行的函数
CarManager.execute = function(name) {
return CarManager[name] && CarManager[name].apply(CarManager, [].slice.call(arguments, 1))
} // 执行
CarManager.execute("arrangeViewing", "Harry Potter", "10000");

命令模式为我们提供了一种分离职责的手段,这些职责包括从执行命令的任意地方发布命令以及将该职责转而委托给不同对象。

实施明智的、简单的命令对象把action动作和调用该动作的对象绑定在一起。它们始终包括一个执行操作(如 run()或 execute())。所有具有相同接口的Command对象可以根据需要轻松交换,这被认为是该模式的一个更大的好处。

9.Facade(外观)模式

Facade模式为更大的代码提供了一个方便的高层次的接口,能够隐藏其底层的真实复杂性。可以把它想成是简化的API来展示给其他开发人员,通常是可以提高可用性的。

但该模式会影响性能,比如说在jQuery中只需要使用$()就可以取到元素,用户不需要使用$.getById()或 $.getByClass()等,但在抽象的时候(即实现的时候),就需要做处理,会降低性能。

例子

// Facade pattern
var addMyEvent = function(el, ev, fn) {
if(el.addEventListener) {
// W3C事件模型
el.addEventListener(ev, fn, false);
} else if(el.attachEvent) {
// IE事件模型
el.attachEvent("on" + ev, fn);
} else {
// Traditional事件模型
el["on" + ev] = fn;
}
}

使用Facade模式时,要了解涉及的任何性能成本,确认是否值得抽象。

10.Factory(工厂)模式

Factory模式通过提供一个通用接口来创建对象。如果对象创建过程相对比较复杂,这种方法特别有用,例如,如果它强烈依赖于动态因素或应用程序配置的话。

下面是一个抽象工厂的例子

// Factory pattern
// 定义Car的构造函数
function Car(options) {
// 默认值
this.doors = options.doors || 4;
this.state = options.state || "brand new";
this.color = options.color || "sliver";
} // 定义Truck的构造函数
function Truck(options) {
this.state = options.state || "used";
this.wheelSize = options.wheelSize || "large";
this.color = options.color || "blue";
} var AbstractVehicleFactory = (function(){
// 存储车辆类型
var types = [];
return {
getVehicle: function( type, customizations ) {
var Vehicle = types[type];
return (Vehicle) ? new Vehicle(customizations) : null;
},
registerVehicle: function( type, Vehicle ) {
var proto = Vehicle.prototype;
// 可以加条件判断注册满足何种条件的车辆
types[type] = Vehicle;
return AbstractVehicleFactory;
}
}
})(); // 用法
AbstractVehicleFactory.registerVehicle("car", Car);
AbstractVehicleFactory.registerVehicle("truck", Truck); // 基于抽象车辆类型实例化一个新的car对象
var car = AbstractVehicleFactory.getVehicle("car", {
color: "lime green",
state: "like new"
}); // 同理实例化一个新的truck对象
var truck = AbstractVehicleFactory.getVehicle("truck", {
wheelSize: "medium",
color: "neon yellow"
});

适用场景

  • 当对象或组件设置涉及高复杂性时
  • 当需要根据所在的不同环境轻松生成对象的不同实例时
  • 当处理很多共享相同属性的小型对象或组件时
  • 在编写只需要满足一个API契约(亦称鸭子类型)的其他对象的实例对象时。对于解耦是很有用的

11.Mixin模式

Mixin是可以轻松被一个子类或一组子类继承功能的类,目的是函数复用。

// Mixin pattern
// 定义简单的Car构造函数
var Car = function(settings) {
this.model = settings.model || "no modal provided";
this.color = settings.color || "no color provided";
} // Mixin
var Mixin = function() {}; Mixin.prototype = {
driveForword: function() {
console.log("drive forword");
},
driveBackword: function() {
console.log("drive backword");
},
driveSideways: function() {
console.log("drive sideways");
}
}; // 通过一个方法将现有对象扩展到另外一个对象上
function augment(receivingClass, givingClass) {
// 只提供特定的方法
if(arguments[2]) {
for (var i = 2, len = arguments.length; i < len; i++) {
receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
};
}
// 提供所有的方法
else {
for (var methodName in givingClass.prototype) {
// 确保接收类不包含所处理方法的同名方法
if(!Object.hasOwnProperty(receivingClass.prototype, methodName)) {
receivingClass.prototype[methodName] = givingClass.prototype[methodName];
}
// 另一个方式
// if(!receivingClass.prototype[methodName]) {
// receivingClass.prototype[methodName] = givingClass.prototype[methodName];
// }
};
}
} // 给Car构造函数增加"driveForword"和"driveBackword"两个方法
augment(Car, Mixin, "driveForword", "driveBackword"); // 创建一个新Car
var myCar = new Car({
model: "Ford Escort",
color: "blue"
}); // 测试确保新增方法可用
myCar.driveForword();
myCar.driveBackword();
// 输出
// drive forword
// drive backword // 也可以通过不声明特定方法名的形式,将Mixin的所有方法都添加到Car里
augment(Car, Mixin); var mySportCar = new Car({
model: "Porsche",
color: "red"
}); mySportCar.driveSideways();
// 输出
// drive sideways

Mixin有助于减少系统中的重复功能及增加函数复用。当一个应用程序可能需要在各种对象实例中共享行为时,我们可以通过在Mixin中维护这种共享功能并专注于仅实现系统中真正不同的功能,来轻松避免任何重复。

Mixin的缺点稍有争议,有些开发人员认为将功能注入对象原型中是一种很糟糕的想法,因为它会导致原型污染和函数起源方面的不确定性。

12.Decorator(装饰者)模式

通常,Decorator提供了将行为动态添加至系统的现有类的能力。其想法是,装饰本身对于类原有的基本功能来说并不是必要的;否则,它就合并到超类本身了。

下面例子只是一个简单的例子

// Decorator pattern
function Vehicle(vehicleType) {
this.vehicleType = vehicleType || "car";
this.model = "default";
this.license = "00000-000";
} // 测试基本的Vehicle实例
var testInstance = new Vehicle("car");
console.log(testInstance); // 创建一个Vehicle实例进行装饰
var truck = new Vehicle("truck"); // 给truck装饰新功能
truck.setModel = function(modelName) {
this.model = modelName;
} truck.setColor = function(color) {
this.color = color;
} // 测试赋值是否正常工作
truck.setModel("CAT");
truck.setColor("blue"); console.log(truck);

对象可以被新的行为包装或装饰,然后可以继续被使用,而不必担心被修改的基本对象。

jQuery.extend()允许我们在运行时或者在随后一个点上动态地将两个或两个以上的对象(和它们的属性)一起扩展(或合并)为一个单一对象。在这种情况下,一个目标对象可以用新功能来装饰,而不会在源/超类对象中破坏或重写现有的方法。

JavaScript设计模式的博客就先到这里,之后如果学习了新的模式,会及时补充到这篇文章中~~

JavaScript的学习--JavaScript设计模式的总结的更多相关文章

  1. 为什么学习JavaScript设计模式,因为它是核心

    那么什么是设计模式呢?当我们在玩游戏的时候,我们会去追求如何最快地通过,去追求获得已什么高效率的操作获得最好的奖品:下班回家,我们打开手机app查询最便捷的路线去坐车:叫外卖时候,也会找附近最近又实惠 ...

  2. 关于学习javascript的一些建议

    有被朋友或同事问到过,要如何学习前端技术,他们大多是已经掌握其他语言的程序员,或是计算机相关专业的在校生. 每次被问到,总要组织回忆一番,本着DRY原则,我还是根据我学习javascript(下文都简 ...

  3. 转:如何学习javascript

    ps:每过一段时间就会发觉自己懂的越来越少,打开编辑器的时候不知道从何入手.兴许是过于急于求成,总没有系统地去对这门语言进行学习,囫囵吞枣,只想着能够解决工作需求.是时候让自己重新出发,从零开始,一页 ...

  4. 【转载】如何学习javascript

    如何学习Javascript 作者: chaomao  首先要说明的是,咱现在不是高手,最多还是一个半桶水,算是入了JS的门. 谈不上经验,都是一些教训. 这个时候有人要说,“靠,你丫半桶水,凭啥教我 ...

  5. 如何学习javascript?(转)

    推荐几本好书: Step 1: <JavaScript DOM编程艺术> 看这本书之前,请先确认您对Javascript有个基本的了解,应该知道if else之类的语法,如果不懂,先去看看 ...

  6. 如何循序渐进有效学习 JavaScript?

    著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:宋学彦链接:http://www.zhihu.com/question/19713563/answer/23068003来源: ...

  7. 如何学习Javascript ?

    先说说学js的条件 论条件,咱是文科生,大学专业工商管理,和计算机毛关系都没:有人说英语,读了四年大学,很遗憾,咱还四级没混过:就咱这条件都学得乐呵呵的,您还等啥.当然学习JS也是有门槛的,就是你的h ...

  8. 大量Javascript/JQuery学习教程电子书合集

    [推荐分享]大量Javascript/JQuery学习教程电子书合集,送给有需要的人   不收藏是你的错^_^. 经证实,均可免费下载. 资源名称 资源大小   15天学会jQuery(完整版).pd ...

  9. [推荐分享]大量Javascript/JQuery学习教程电子书合集,送给有需要的人

    不收藏是你的错^_^. 经证实,均可免费下载. 资源名称 资源大小   15天学会jQuery(完整版).pdf 274.79 KB   21天学通JavaScript(第2版)-顾宁燕扫描版.pdf ...

随机推荐

  1. Win7下Eclipse中文字体太小

    http://www.cnblogs.com/newdon318/archive/2012/03/23/2413340.html 最近新装了Win7,打开eclipse3.7中文字体很小,简直难以辨认 ...

  2. 美帝的emal to message gateway

    Provider Email to SMS Address Format AllTel number@text.wireless.alltel.com AT&T number@txt.att. ...

  3. jquery ajax post 中文乱码解决

    jquery ajax post 方式默认传递的是UFT-8字符,即使页面设置了gbk编码也无效.字符传到后台时如果后台设置的gbk或者其他编码,就会出现乱码.解决很容易,将后台页面设置成UTF-8编 ...

  4. 扫描二维码判断移动设备(Android/ios),以及判断是否微信端扫描

    <section class="download"> <a href="apk地址" class="android" st ...

  5. ajax跟取后台 josn 之 josn理解

    json是一种轻量级的数据交换格式,是 JavaScript 原生格式,是理想的数据交换格式. 1.json对象json对象以“{”开始 , 以“}”结束,每个“名称”后跟一个“:”(冒号),‘名:值 ...

  6. Smack 3.3.1 发布,Java 的 XMPP 开发包

    Smack 3.3.1 发布了,这是一个小更新版本,主要更新包括: [SMACK-441] - Memory leak in KeepAliveManager [SMACK-447] - Compre ...

  7. svn patch用法

    最近遇到了一个patch的使用场景: 有一个同事对源码做了一些修改,但是又不想将源码提交到SVN服务器,而我又想得到他所做的修改. patch的使用方法: 创建patch 在要导出“修改”的目录中,单 ...

  8. 《C#图解教程》读书笔记之四:类和继承

    本篇已收录至<C#图解教程>读书笔记目录贴,点击访问该目录可获取更多内容. 一.万物之宗:Object (1)除了特殊的Object类,其他所有类都是派生类,即使他们没有显示基类定义. ( ...

  9. java生成压缩文件

    在工作过程中,需要将一个文件夹生成压缩文件,然后提供给用户下载.所以自己写了一个压缩文件的工具类.该工具类支持单个文件和文件夹压缩.放代码: import java.io.BufferedOutput ...

  10. .NET中Dictionary<TKey, TValue>浅析

    .NET中Dictionary<TKey, Tvalue>是非常常用的key-value的数据结构,也就是其实就是传说中的哈希表..NET中还有一个叫做Hashtable的类型,两个类型都 ...