JavaScript设计模式

单例模式

原理:控制好创建实例时,确保不会重复创建

字面量

最简单的单例

1
2
3
4
5
6
var singleton = {
v1: 'vv',
method1: function () {}

// ...
};

函数表达式方式,通过封装返回共有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var singleton = function () {

// 私有变量和方法
var privateV = 'pp'; // 私有变量

function privateF() {
console.log( 'private function' );
}

// 返回公用变量或方法
return {
publicV: 'pppp',

// 共有方法可以访问对象的私有方法,外部无法访问privateF()
publicF: function () {
privateF();
}
};
};


// 使用
var single = singleton();

single.publicV; // 'pppp'

single.publicF(); // 'private funtion'
  • 使用时初始化

原理是在对象中增加初始化对象 initedObj 以及初始化函数 init ,在使用是判断该初始化对象如果已经初始化就直接返回,否则就调用 init 进行初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
var singleton = (function () {
var initedObj = null;

function init() {
return {
// 返回单例对象,单例对象的变量和方法都在这里声明和定义
publicF: function () {},

publicV: 'i am public var'
};
}

// 返回共有函数,来创建单例
return {
getInstance: function () {
// 只有在单例对象未创建的时候才去创建,否则直接返回已经存在的,从而保证了唯一性以及使用时创建
if ( !initedObj ) {
initedObj = init();
}

return initedObj;
}
};
}());

// 上述代码执行完成后,singleton就成了一个拥有getInstance方法的对象

// 当需要创建单例对象的时候可以通过下面方式去创建

var single = singleton.getInstance();

// 调用单例对象的方法和属性
single.pulicF();

single.pulicV;
  • 从实例去控制单例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function Singleton() {

// 实例是否存在?
if ( typeof Singleton.instance === 'object' ) {
return Singleton.instance;
}

// 其他内容
this.time = '2016';
this.size = 'large';

// 缓存实例
Singleton.instance = this;

/*
这里添加这个打印就能很明显的看出这个单实例实现原理

1. 当第一个new的时候single1,会输出
Singleton {time: "2016", size: "large"}
2. 后面重复new,不管多少此都会只有这一个输出,因为在if那里直接return了
3. 如果将if里的return注释掉,就会出现new多少个就会输出多少个

所以该方式单例实现的主要关键就在开始的if语句判断
*/
console.log( this );


// 隐式返回this
}

var single1 = new Singleton();
// single1创建之后,Singleton.instance就被指向了single1

var single2 = new Singleton();
// 此时重新new,在Singleton经过if语句的实例是否存在判断,
// 就直接返回single1里面的实例对象了,事实上这个new在Singleton构造函数里面只执行到了if(){}就结束了,无论后面有多少个new操作,都和single2的创建一样,直接返回了single1,也就是第一个new出来的对象

console.log( single1 === single2 ); // true
  • 从构造函数去控制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    function Singleton() {
    var instance = this;

    this.start = '111';

    Singleton = function () {
    return instance;
    };
    }

    /*
    其实这种方式和第一种没什么区别,都是从创建对象时去控制避免重新创建

    这里过程如下:

    1. 创建s1时候,重写了构造函数Singleton

    2. 等创建s2的时候,其实new Singleton()这个时候的用的构造函数就是s1创建时被重写的那个,即:
    function () {
    return instance;
    }

    而这里的instance事实上就是第一次创建s1时,this的指向,也就是s1实例

    */

    var s1 = new Singleton();

    s1.start = '222';

    var s2 = new Singleton();

    console.log(s2.start); // '222'

构造函数模式

1
2
3
4
5
6
7
8
9
10
11
// 推荐首字母大写,与普通函数区分开
function MyFunction() {
// 强制使用new来创建
if ( this instanceof MyFunction ) {
return new MyFunction();
}

// ...其他
}

var myFunc = new MyFunction();

TODO 建造者模式

一个大整体,包含很多组件时,当其下的部分组件发生变化时,应该如何应变,此时可以考虑使用建造者模式

函数回调??类似 ajax 请求的 successfail ???

TODO 工厂模式

TODO 装饰者模式

通过重载方法,来实现自己特定的功能

TODO 外观模式

预判,或者说预先就假设值,而不是每次使用的时候去判断

例如:事件绑定,在声明兼容接口addMyEvent的时候就去做预判

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 这种方式就避免了每次使用时都会组调用一次对应的事件添加行为
// 而是在声明的时候就判断出使用什么样的接口,然后把这个接口的实现
// 赋值给自定义的事件绑定接口addMyEvent,等到真正使用它的时候,
// 那么就会直接调用与当前环境相对应的接口了
var addMyEvent = (function ( ele, e, fn) {
if ( el.addEventListener ) {
return function ( ele, e, fn ) {
ele.addEventListener( e, fn, false );
};
} else if ( ele.attachEvent ) {
return function ( ele, e, fn ) {
ele.attachEvent( 'on' + e, fn );
};
} else {
return function ( ele, e, fn ) {
ele[ 'on' + e ] = fn;
};
}
} ());

代理模式(Proxy)

原理:即把自己不想做或不方便做的事情交给其他方法去完成,这个其他方法就称作代理方法,比如:A去买电影票Ticket,但是没有空,想让他的朋友B去帮他买,此时B就是A的代理;代理在原则上需要B和A拥有一样的行为,然后在B的行为里去调用A的行为。

  • 实现:A买Ticket,没空去,让B帮他去,那么这里B就成了A的代理

    分析:此问题有三个对象(买票者:A,帮忙买票人:B,电影票:Ticket),一个方法:buyTicket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 电影票,包含票价
function Ticket( price ) {
this.price = price;
}

// 买票者
function A( ticket ) {
this.ticket = ticket;
this.buyTicket = function( ) {
console.log('buy ticket price is ' + this.ticket.price);
}
}

// 代买者
function B( ticket ) {
this.ticket = ticket;
this.buyTicket = function ( ) {
( new A( this.ticket ) ).buyTicket( );
};
}

var b = new B( new Ticket('$100') );
b.buyTicket();

TODO 观察者模式(Observer),又名:发布订阅模式(Publish/Subscribe)

观察者模式基本原理:被观察者的对象中用一个数组保存观察者的回调函数
当被观察者对象发生变化时,调用其自身的变化函数,然后在该函数里面,处理观察者回调
函数数组,并且执行他们,从而达到被观察者发生变化时,观察者可以做出相应的变化

优点:

  1. 支持广播通信,自动通知所有已订阅的对象;
  2. 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性;
  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展及重用。

命令模式

原理:将对象里的函数和函数参数单独拎出来组织成对象,作为对象的另一个接口的参数传入,然后根据参数对象对应的成员去实现函数调用

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var manager = {
request: function ( a, b ) {},

query: function ( a, b ) {},

answer: function ( a, b ) {}
};

// 普通调用
manager.request( 'hello', 'world' );

// 命令模式修改,定义执行命令的接口
manager.exec = function ( command ) {
return manager[ command.func ]( command.a, command.b );
};

// 使用命令
manager.exec( { func: 'request', a: 'a', b: 'b' } );

// 上面函数执行完成,其实就相当于执行了
manager.request( a, b );

TODO 迭代器模式

本文标题:JavaScript设计模式

文章作者:ZhiCheng Lee

发布时间:2019年04月22日 - 16:01:45

最后更新:2019年04月22日 - 16:01:45

原始链接:http://blog.gcl666.com/2019/04/22/javascript_design_pattern/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%