为什么有设计模式的概念?
1.设计模式是代码设计经验的总结,为了可重用代码,保证代码的可靠性等.
2.设计模式主要分为三大类型:创建型模式,结构型模式和行为型模式.
1.单例模式
- 定义:
- 单件模式确保一个类只有一个实例,并提供一个全局访问点.
- 使用场景:
- 用于创建独一无二的,只能有一个实例的对象,单件模式给了我们一个全局的访问点,和全局变量一样方便又没有全局变量的缺点.
- 把全局变量当成
单例
来使用容易造成命名污染.
防止命名空间污染的方法:
- 使用命名空间
- 使用闭包封装私有变量
<script type="text/javascript">
var People = (function () {//var的函数方法所以用大写
var instance;
function init() {
//定义私有方法和属性
//做某事
return {
//定义公共方法和属性
};
}
return {
createPeople: function () {
if (!instance) {//只能有一份内存的对象,有就不创建,没就创建
instance = init();
}
return instance;
}
};
}());
var obj1 = People.createPeople();
var obj2 = People.createPeople();
</script>
2.构造函数模式
- 组件,封装,复杂.
- JavaScript里函数有个原型属性叫prototype,当调用构造函数创建对象的时候,所有该构造函数原型的属性在新创建对象上都可用
- 构造函数用于创建特定类型的对象,不仅声明了使用的对象,构造函数还可以接受参数。
- 你可以自定义自己的构造函数,然后在里面声明自定义类型对象的属性或方法.
<script type="text/javascript">
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
return this.name;
};
var student = new Person('tony', 20);
console.log(student);
</script>
3.混合模式
- 为什么使用混合模式实现继承?
实现对象的继承,我们可以通过对象冒充,也可以通过原型链的方式.
但是,对象冒充就要求必须使用构造函数方式,而原型链则无法使用构造函数,那么,我们就综合一下,采区混合模式来实现继承.
- 创建类的最好方式,是用构造函数定义属性,使用原型方式定义方法.这样的机制同样适用于继承机制,用对象冒充来继承构造函数的属性,用原型链继承prototype对象的方法
<script type="text/javascript">
var Person = function (name, age) {
this.name = name;
this.age = age;
};
Person.prototype.sayName = function () {
console.log(this.name);
}
var Student = function (name, age, score) {
//这里的 call作用:改变作用域,可以引用构造函数
Person.call(this, name, age);//this是student
this.score = score;
//student继承了person的属性
};
//Object.create()可以调用这个方法来创建一个新对象。
//新对象的原型就是调用 create方法时传入的第一个参数
Student.prototype = Object.create(Person.prototype);
//student继承了person的方法
// Student.prototype = create(Person.prototype);
// function create (parentObj){
// function F(){}
// F.prototype = parentObj;
// return new F();
// };//这一段等同于上面Object.create.(Person.prototype).
Student.prototype.sayScore = function () {
console.log(this.score);
}
var student = new Student("likefool", 18, 90);
console.log(student);//obj{属性+方法}
student.sayName();//'likefool'
//student继承了person的属性和方法
//混合模式= 构造函数模式 + call继承属性
</script>
4.工厂模式
- 使用场景
- 创建新对象,且该对象需要被被封装.
工厂模式通过让子类来决定该创建的对象是什么,来达到将对象创建的过程封装的目的.
创建对象的方法使用的是继承,用于创建一个产品的实例.
<script type="text/javascript">
function createPerson(opts) {
var person = {
name: opys.name || 'peter'
};
person.sayName = function () {
console.log(this.name);
}
return person;
}
var p1 = createPerson({ name: 'tom' });
var p2 = createPerson({ name: 'kite' })
</script>
5.模块模式
- 立即执行函数,直接return结果供外部使用,不污染全局变量
<script type="text/javascript">
var Person = (function(){
var name = 'ruoyu';
function sayName(){
console.log(name);
}
return {
name: name,
sayName: sayName
}
})()
Person.sayName();
</script>
6.发布订阅模式(即观察者模式)
- 观察者模式又叫发布订阅模式,它定义了一种
一对多
的关系,让多个观察者对象同时监听某一个主题对象
,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
- 使用观察者模式的好处:
支持简单的广播通信,自动通知所有已经订阅过的对象。
页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
- 观察者主要让订阅者与发布者
解耦
,发布者不需要知道哪些模块订阅了这个主题,它只管发布这个主题就可以了,同样订阅者也无需知道那个模块会发布这个主题,它只管订阅这个主题就可以了
<script type="text/javascript">
var EventCenter = (function () {
var events = {};
/*
{
my_event: [{handler: function(data){xxx}}, {handler: function(data){yyy}}]
}
*/
//上面用数组保存方法的原因:这样实现了一个fire调用多个方法.一对多.
function on(evt, handler) {
events[evt] = events[evt] || [];
events[evt].push({
handler: handler
});
}
function fire(evt, args) {
if (!events[evt]) {
return;
}
for (var i = 0; i < events[evt].length; i++) {
events[evt][i].handler(args);
}
}
return {
on: on,
fire: fire
}
})();
EventCenter.on('my_event', function (data) {
console.log('my_event received...');
});
EventCenter.on('my_event', function (data) {
console.log('my_event2 received...');
});
EventCenter.fire('my_event',);
//逻辑:
//最外面放一个空对象.
//A方法作用:讲若干函数放入数组
//B方法:调用A里面所有的方法.
//AB怎么联系起来:通过外面的events对象,AB都是操作events对象.
//所以1对多.
</script>
参考:
7.发布订阅模式的范例
<script type="text/javascript">
var EventCenter = (function () {
//外部创建一个可以包含数组的对象
var events = {};
/*
{
my_event: [{handler: function(data){xxx}}, {handler: function(data){yyy}}]
}
*/
//这里只保存方法到数组,不做操作.
function on(evt, handler) {
events[evt] = events[evt] || [];
events[evt].push({
handler: handler
});
}
//对上面保存在数组里的函数做相关操作
//重点是:都是对同一event对象的数组操作
function fire(evt, args) {
if (!events[evt]) {
return;
}
for (var i = 0; i < events[evt].length; i++) {
events[evt][i].handler(args);
}
}
function off(evt) {
delete events[evt]
}
return {
on: on,
fire: fire,
off: off
}
})();
EventCenter.on('my_event', function (data) {
console.log('my_event received...');
});
EventCenter.on('my_event', function (data) {
console.log('my_event2 received...');
});
EventCenter.fire('my_event');
EventCenter.on('change', function (val) {
console.log('change... now val is ' + val);
});
EventCenter.fire('change', 'Tom');
EventCenter.off('change');//events[change]就被删除了
//所以不会再调用change相关方法(通知),delete原理就是删除数组里面的元素(即方法)
</script>