学习书籍:《JavaScript设计模式》
学习目标:
- 建造者模式、原型模式、单例模式
学习内容:
建造者模式:将一个复杂对象的构建层与其表示层相互分离,同样的构建过程可采用不同的表示
/* 建造者模式
工厂模式主要是为了创建对象实例或者类簇(抽象工厂),关心的是最终产出的是什么,所以通过工厂模式我们得到的都是对象实例或者类簇。
然而建造者模式在创建对象时要更为复杂的一些,虽然其目的也是为了创建对象,但是它更多关心的是创建这个对象的整个过程、这个对象的细节。
*/
// 创建一个对象人
var Huamn = function(param){
// 技能
this.skill = param && param.skill || '保密'; // 表示,如果存在param,并且param存在skill属性,就用这个属性赋值给this的skill属性,否则将用默认值’保密‘来设置
// 兴趣爱好
this.hobby = param && param.skill || '保密';
}
// 类人原型方法
Human.prototype = {
getSkill: function(){
return this.skill;
},
getHobby: function(){
return this.hobby;
}
}
// 实例化姓名类
var Named = function(name){
var that = this;
// 构造器
// 构造函数解析姓名的姓与名
(function(name, that){
that.wholeName = name;
if (name.indexOf(' ')>-1){
that.FirstName = name.slice(0, name.indexOf(' '));
that.SecondtName = name.slice(name.indexOf(' '));
}
})(name, that);
}
// 实例化职位
var Work = function() {
var that = this;
// 构造器
// 构造函数中通过传入的职位特征来设置相应职位以及描述
(function(work, that){
switch(work){
case 'code':
that.work = '工程师';
that.workDescript = '每天沉醉于编程';
break;
case 'UE':
that.work = '设计师';
that.workDescript = '设计更似一种艺术';
break;
case 'teach':
that.work = '教师';
that.workDescript = '分享也是一种快乐';
break;
default:
that.work = work;
that.workDescript = '对不起,我们还不清楚您所选择职位的相关描述';
}
})(work, that);
}
// 更换期望的职位
Work.prototype.changeWork = function(work){
this.work = work;
}
// 添加对职位的描述
Work.prototype.changeDescript = function(setence){
this.workDescript = setence;
}
/****
* 应聘者构造者
* 参数name: 姓名
* 参数work: 期望职位
**/
var Person = function(name, work){
// 创建应聘者缓存对象
var _person = new Human();
// 创建应聘者姓名解析对象
_person.name = new Named(name);
// 创建应聘者期望职位
_person.name = new Work(work);
//将创建的应聘者对象返回
return _person;
}
var person = new Person('Xiao Ming', 'code');
consolg.log(person.skill); // 保密
consolg.log(person.name.FirstName); // Xiao
consolg.log(person.work.work); // 工程师
consolg.log(person.work.workDescript); // 每天沉醉于编程
person.work.changeDescript('更改一下职位描述');
consolg.log(person.work.workDescript); // 更改一下职位描述
原型模式:将原型对象指向创建对象的类,使这些类共享原型对象的方法与属性。
/*
原型模式就是将可复用的、可共享的、耗时大的从基类中提取出来然后放在其原型中,然后子类通过组合继承或者寄生组合式继承而将方法和属性继承下来,
对于子类中需要重写的方法重写,这样子类创建的对象既具有子类的属性和方法也共享了基类的原型方法。
*/
// 图片轮播类
var LoopImages = function(imgArr, container){
this.imagesArray = imgArr; // 轮播图片数组
this.container = container; // 轮播图片容器
}
LoopImage.prototype = {
// 创建轮播图片
createImage: function(){
console.log('LoopImages createImage function');
},
// 切换下一张图片
changeImage: function(){
console.log('LoopImages changeImage function');
}
}
// 上下滑动切换类
var SlideLoopImg = function(imgArr, container){
// 构造函数继承图片轮播类
LoopImages.call(this, imgArr, container);
}
SlideLoopImg.prototype = new LoopImages();
// 重写继承的切换下一张图片方法
SlideLoopImg.prototype.changeImage = function(){
consolg.log('SlideLoopImg changeImage function')
}
// 渐隐切换类
var FadeLoopImg = function(imgArr, container, arrow){
LoopImages.call(this, imgArr, container);
// 切换箭头私有变量
this.arrow = arrow;
}
FadeLoopImg.prototype = new LoopImages();
FadeLoopImg.prototype.changeImage = function(){
consolg.log('FadeLoopImg changeImage function')
}
// 测试用例
var fadeImg = new FadeLoopImg([
'01.jpg',
'02.jpg',
'03.jpg',
'04.jpg'], 'slide', [
'left.jpg',
'right.jpg'
]);
console.log(fadeImg.container) // slide
fadeImg.changeImage(); // FadeLoopImg changeImage function
/* 原型的拓展
原型对象是一个共享的对象,那么无论是父类的实例对象或者是子类的继承,都是对它的一个指向引用,所以原型对象才会被共享。
既然被共享,那么对原型对象的拓展,无论是子类还是父类的实例对象都会继承下来。
*/
LoopImage.prototype.getImageLength = function(){
return this.imagesArrary.length;
}
FadeLoopImg.prototype.getContainer = function(){
return this.container;
}
console.log(fadeImg.getImageLength()); // 4
console.log(fadeImg.getContainer()); // slide
/** 原型继承
* 基于已经存在的模板对象克隆出新对象的模式
* arguments[0], arguments[1], arguments[2]: 参数1,2,3 表示模板对象
* 注意:这里对模板引用类型的属性实质上进行了浅复制(引用类型属性共享),当然可以根据需求进行深复制(引用类型属性复制)
**/
functin prototypeExtend(){
var F = function(){},
args = arguments,
i = 0,
len = args.length;
for (; i<len; i++){
// 遍历每个模板对象中的属性
for (var j in args[i]){
// 将这些属性赋值到缓存类原型中
F.prototype[j] = args[i][j];
}
}
// 返回缓存类的一个实例
return new F();
}
var penguin = prototypeExtend({
speed: 20,
swim: function(){
console.log('游泳速度' + this.speed);
}
},{
run: function(speed){
console.log('奔跑速度' + speed);
}
},{
jump: function(){
console.log('跳跃动作');
}
})
penguin.swim(); // 游泳速度20
penguin.run(10); // 奔跑速度10
penguin.jump(); // 跳跃动作
单例模式:又称单体模式,是只允许实例化一次的对象类。
/* 命名空间
由于人们可用的单词或者汉字拼音是有限的,所以不同的人定义的变量或者方法,但由于人们可用的单词或者拼音是有限的,所以不同的人定义的变量很有能重复,此时需要用命名空间来约束每个人定义变量来解决问题。
比如小李定义了一个xiaoli命名空间,那么后面可以用xiaoli.xx(xx表示定义的变量名)来使用
*/
var xiaoli = {
g: function(id){
return document.getElementById(id)
},
css: function(id, key, value){
// 通过当前对象this来使用g方法
this.g(id).style[key] = value;
}
}
/* 单例模式还可以用来管理代码库的各个模块。 */
var A = {
Util: {
util_method1: function(){},
util_method2: function(){},
},
Tool: {
tool_method1: function(){},
tool_method2: function(){},
},
Ajax: {
get: function(){},
post: function(){},
}
}
A.Tool.tool_method1()
A.Ajax.get()
/* 利用单例模式管理静态变量
javascript没有static这类关键词,但可以根据静态变量的特性来操作:将变量放在一个函数中,且不提供赋值变量的方法,只提供获取变量的方法,不就可以做到限制变量的修改和供外界访问了吗;
为了放在函数内的变量还能供外界访问,就可以让函数执行一次,此时创建的对象内保存的静态变量可以通过取值器访问,最后将这个对象作为一个单例放在全局空间里作为静态变量单例对象供他人使用
*/
var Conf = (function(){
// 私有变量
var conf = {
MAX_NUM: 100,
MIN_NUM: 1,
COUNT: 1000
}
// 返回取值器对象
return {
// 取值器方法
get: function(name){
return conf[name] ? conf[name] : null;
}
}
})();
var count = Conf,get('COUNT');
console.log(count); // 1000
/* 惰性单例
对单例对象延迟创建,又称’惰性创建‘
*/
// 惰性载入单例
var LazySingle = (function(){
// 单例实例引用
var instance = null;
// 单例
function Single(){
// 私有属性和方法
return {
publicMethod: function(){},
publicProperty: '1.0'
}
}
// 获取单例对象接口
return function(){
// 如果为创建单例将创建单例
if (!_instance){
_instance = Single();
}
// 返回单例
return _instance;
}
})();
console.log(LazySingle().publicProperty); // 1.0