blog
blog copied to clipboard
JS Design Patterns
JS设计模式
常见 设计模式有
- 构造函数模式 constructor
- 工厂模式 factory
- 混合模式 mixin
- 模块模式 module
- 单例模式 singleton
- 订阅发布模式 subscibe & pulish
- 观察者模式 observer
构造函数模式 constructor
使用场景:创建一个复杂的对象,可以添加方法
new 函数调用,返回新的对象。每次都是新的引用 方法放在原型链上
//构造函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.sayName = function () {
return this.name;
};
var student = new Person("若愚", 30);
var student1 = new Person("slash", 30);
工厂模式 factory
使用场景:创建一个简单的对象
存在的意义是 每次创建新的引用,生产出来的数据结构一致
//工厂模式
function createPerson(name) {
var person = {
name: name,
sayName: function () {
console.log(this.name);
}
};
return person;
}; // 开辟新内存
// createPerson('a')
// createPerson('b')
混合模式 mixin
使用场景:可继承的公用方法
通过继承来实现
将一些可重用的代码放在父类上,子类通过继承得到属性和方法,并能根据需要自身添加新的属性、方法
// 混合模式 mixin == > 实现继承
// 实例1js
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) {
// 实例属性实现继承
Person.call(this, name, age);
this.score = score;
};
// 将Person.prototype挂在Student.prototype的原型链第一层
// 原型链实现继承
Student.prototype = Object.create(Person.prototype);
Student.prototype.getName = function () {
return this.name;
}
模块模式 module
使用场景:需要封装的组件
封闭作用域,通过闭包读取内部变量
暴露给外界的仅仅是return选择的内容
//模块模式
//通过闭包,实现作用域的隔离来实现一个模块
var Person = (function () {
var name = 'ruoyu';
function sayName() {
console.log(name);
}; //词法作用域:一个函数可以访问到的上下文环境由它所定义的位置决定
// 词法作用域
return {
name: name,
sayName: sayName
}
})();
单例模式 singleton
使用场景:只需出现一次,独一无二的组件
匿名函数(lambda函数)划分命名空间,避免全局污染
可以被实例化,且实例化一次。
指向同一个引用,只开辟一次内存空间
//单例模式 singleton单例
// 匿名函数
var People = (function () {
var instance;
function init(name) {
return {
name: name
};
};
//词法作用域
return {
createPeople: function (name) {
if (!instance) { // 如果没有instance就初始化
instance = init(name); // 所以第一次调用就初始化,之后不再执行这段代码
}
return instance; //单例就是只开辟了一个内存空间
} //第一次传入的值被储存下来
}; //之后生成的实例全都指向同样的地方
}());
People.createPeople('jirengu') === People.createPeople('hello') //true //{name:'jirengu'} {name:'jirengu'}
订阅发布模式 subscibe & pulish
// 订阅发布模式
//逻辑:
// subscribe publish $('.btn').on('click', function (event) {
// console.log('clicked')
// }); === > {
// 'click': [fn]
// }
// $('.btn').on('mouseover', function (event) {
// $('.btn').trigger('click',event); ====> [fn].forEach(fn(event));
// })
var EventCenter = (function () { // 新建订阅中心
//我们如何去实现
var F = function () {
this.eventPool = {}; // 新建缓存列表,存储订阅者的回调函数
};
F.prototype.on = function (name, callback) { // 添加消息:绑定事件
this.eventPool[name] = this.eventPool[name] || []; //检查事件列表events{}里是否有这个key
this.eventPool[name].push(callback); 在选定事件里增加一个函数
};
F.prototype.trigger = function (name) {
if (!this.eventPool[name]) {
this.eventPool[name] = [];
}
this.eventPool[name].forEach(function (fn) { //如果存在,遍历列表events,依次执行处理程序
fn()
})
};
return F;
}());
var e = new EventCenter();
e.on('hello', function () {
console.log('hello')
});
// === > 存 { 'hello': [fn] }
e.trigger('hello');
// hello ===> 取后执行
观察者模式 observer
//观察者模式 observer
$('input').change(function () {
$('input').observers.forEach(function (observer) {
observer();
})
});
$('input').observers = [];
$('input').subscribe = function (fn) {
this.observers.push(fn);
};
// API使用方式
// $('.input').subscribe(fn) ==> 记录日志
// $('input').subscribe(fn1) ==> 发起请求
// $('input').subscribe(fn2) ==> 改变UI
订阅发布 模式 与 观察者模式 的区别
很多的资料都认为二者是一样的 其实仔细考虑,还是有很重要的不同点
-
观察者模式 创建一个需要观察的事件时,要先到subscribe里面进行注册,这个函数将此事件推送到需要通知的数组里,然后,当订阅的内部状态发生变化时,把这个变化通知所有的观察者。 随意注册事件,调用事件,不会对事情做区分
-
发布-订阅模式 角色为发布者(publisher)和订阅者(subscriber),pub和sub之间没有直接的耦合关系,pub发布一个消息事件(event),sub订阅感兴趣的消息事件,sub也可以取消订阅。 是对特定一系列的订阅事件进行统一管理
初步得出结论:
观察者模式中的观察者是和主题对象紧耦合的。 发布-订阅模式中的发布者和订阅者是松耦合的,发布者和订阅者是通过事件发生联系的。 在开发大型项目的时候,订阅/发布模式会让业务更清晰。
使用发布订阅模式写一个事件管理器,可以实现如下方式调用
Event.on('change', function(val){
console.log('change... now val is ' + val);
});
Event.fire('change', '饥人谷');
Event.off('changer');
代码
var eventCenter = (function () {
var e = function () {
this.eventPool = [];
}
e.prototype = {
on: function (name, callback) {
this.eventPool[name] = this.eventPool[name] || [];
this.eventPool[name].push(callback);
},
fire: function (name, value) {
if (!this.eventPool[name]) return;
this.eventPool[name].forEach(function (e) {
e(value);
});
},
off: function (name) {
delete this.eventPool[name];
}
};
return e;
})()
//测试
var Event = new eventCenter()
Event.on('change', function (val) {
console.log('change... now val is ' + val);
});
Event.fire('change', 'aaaaaa');
Event.off('change');
Event.on('change1', function (val) {
console.log('第二次~' + val);
});
Event.fire('change1', 'bbbbbb');