概念
设计模式是为了解决某种问题,而设计的一套最佳解决方案。
常见的设计模式:
- 单例模式
- 组合模式
- 观察者模式
- 命令模式
- 代理模式
- 工厂模式
- 策略模式
- 。。。
单例模式
让一个类创建出来的所有对象,里面的所有属性和方法都一模一样。比如说封装一个类,将一些常用的操作函数为方法放进去,以后每次都使用同一个对象来调用这些方法
正常情况,一个类创建出来的每个对象都是不一样的。
class Carousel{
constructor(classname){
this.box = document.querySelector("."+classname);
}
}
var a = new Carousel("box");
var b = new Carousel("box1");
console.log(a === b); // false
单例模式就是让这两个对象是一样的,也就是说,一个类永远只有一个实例对象
var single = (function(){
class Carousel{
}
var res = undefined;
return function(){
if(!res){
res = new Carousel();
}
return res;
}
})();
var s1 = single();
var s2 = single();
console.log(s1 === s2); // true
例:封装一些常用操作在一个类中
var single = (function(){
class Carousel{
constructor(){
this.onOff = window.getComputedStyle;
}
// 设置样式的操作
setCss(ele,styleObj){
for(var attr in styleObj){
ele.style[attr] = styleObj[attr];
}
}
// 获取样式
getStyle(ele,attr){
if(onOff){
return window.getComputedStyle(ele)[attr];
}else{
return ele.currentStyle[attr];
}
}
}
var res = undefined;
return function(){
if(!res){
res = new Carousel();
}
return res;
}
})();
var s1 = single();
var s2 = single();
console.log(s1 === s2); // true
box = document.querySelector(".box");
single().setCss(box,{
height:"200px",
background:"red"
});
组合模式
组合模式就是制作启动器。多个类在实例化以后,执行起来使用一个同名的方法来启动,这时候可以做一个启动器,让多个类一起启动。
class Carousel{
init(){
console.log("轮播图开始运行");
}
}
class Tab{
init(){
console.log("选项卡开始运行");
}
}
class Enlarge{
init(){
console.log("放大镜开始运行");
}
}
// 这3个类要运行起来需要各自实例化,并调用每个类中的init方法,此时就可以使用组合模式做一个启动器
组合模式制作启动器
class Starter{
constructor(){
this.arr = []; // 定义一个数组
}
add(className){
this.arr.push(className); // 将这个多个类放进数组
}
run(){
for(var i=0;i<this.arr.length;i++){
arr[i].init(); // 让数组中的每个类都调用init方法
}
}
}
var starts = new Starter();
starts.add(new Carousel);
starts.add(new Tab);
starts.add(new Enlarge);
starts.run();
观察者模式
观察者模式,又称发布-订阅模式。意思是让一个人不停的监控某件东西,当这个东西要发生某种行为的时候,这个人就通知一个函数执行这个行为的操作。
例:当事件代码写好之后,其实就是这个事件不停的监控用户在页面中的行为,一旦用户触发这个事件的时候 ,就调用函数处理这个事件。
div.addEventListener("click",function(){});
// 这个事件写好以后,就一直在页面中监控用户行为,用户点击这个元素的时候,就调用函数
观察者模式就是类似的操作,写观察者模式的目的,是为了给一个非元素的数据绑定一个自定义事件。
例:给一个obj绑定一个abc事件
分析:
给一个元素绑定事件,有绑定方法,有触发条件,有取消绑定。
要给一个对象绑定一个自定义事件。那么这个事件如何绑定,如何触发,如何解绑这个事件。
所以:
- 需要一个方法处理事件的绑定。
- 需要一个方法处理如何触发这个事件。
- 需要一个方法处理如何解绑这个事件。
元素的事件,一个时间类型可以绑定多个函数。
所以:
需要一个空间,储存事件类型对应的处理函数们。
雏形 :
class watch{
bind(){
}
touch(){
}
unbind(){
}
}
var w = new watch();
此时,如要给这个函数绑定事件和处理函数的话,需要事件类型和处理函数作为参数,所以调用时要传入实参
var w = new watch();
w.bind("cl",a); // 给w对象绑定cl事件类型,执行a函数
w.bind("cl",b); // 给w对象绑定cl事件类型,执行b函数
function a(){
console.log("事件处理函数a");
}
function b(){
console.log("事件处理函数b");
}
绑定
在bind方法中接收函数,并将事件类型和处理函数对应存储起来:
constructor(){
this.obj = {} // 存储格式:{事件类型:[函数1,函数2]}
}
bind(type,handle){
this.obj[type] = [handle]; // 对象存储方式{"cl":[a]}
}
如果给这个事件再绑定一个函数b的话,会将原来的a覆盖掉的,所以,应该先判断。如果对应 的这个数组中没有数据就直接放进去,如果有了就应该追加
bind(type,handle){
if(!this.obj[type]){
this.obj[type] = [handle]; // 对象存储方式{"cl":[a]}
}else{
this.obj[type].push(handle);
}
}
触发
触发这个事件需要传入触发哪个事件类型
touch(type){
// 首先要判断,这个事件类型是否绑定,没有绑定不能触发
if(!this.obj[type]){
return false;
}else{
// 将这个事件的所有绑定的处理函数一起调用
for(var i=0;i<this.obj[type].length;i++){
this.obj[type][i]();
}
}
}
这两个处理函数都没有参数,如果要传入参数的时候应该怎么处理?
触发事件的时候就要传入实参
w.touch("cl","张三",20);
触发事件的方法就应该处理这些参数
touch(type,...arr){ // 因为参数不定长,所以使用合并运算符
// 首先要判断,这个事件类型是否绑定,没有绑定不能触发
if(!this.obj[type]){
return false;
}else{
// 处理参数:模拟系统事件的参数事件对象,将所有参数都集中在一个对象中
var e = {
type:type,
args:arr
}
// 将这个事件的所有绑定的处理函数一起调用
for(var i=0;i<this.obj[type].length;i++){
this.obj[type][i](e);
}
}
}
添加一个带参数的处理函数,并触发事件执行:
w.bind("cl",c); // 给w对象绑定cl事件类型,执行c函数
w.touch("cl","张三",20);
function c(e){
console.log("我是处理函数c,打印:姓名"+e.name+",年龄"+e.age);
}
解绑
解绑也需要知道解绑的事件类型和处理函数
unbind(type,handle){
// 先判断是否绑定了这个事件
if(!this.obj[type]){
return false;
}else{
// 从数组中将这个处理函数删除
for(var i=0;i<this.obj[type].length;i++){
if(this.obj[type][i] === type){
this.obj[type].splice(i,1);
i--; // 放置数组塌陷
}
}
}
}
如果绑定事件的时候使用了匿名函数,就无法进行解绑了,所以再添加一个解绑事件所有函数的处理方法:
clear(type){
if(!this.obj[type]){
return false;
}else{
// 直接从对象中将这个属性删除
delete this.obj[type];
}
}
数据劫持
<body>
<div></div>
</body>
<script type="text/javascript">
function edit(val){
document.querySelector("div").innerText = val;
}
var obj = {
name:"张三",
age:12
}
edit(obj.age);
Object.defineProperty(obj,"age",{
configurable:true, // 允许配置
get(){ // 设置值
return 18;
},
set(val){ // 值发生改变的时候要做的事情
console.log("正在将obj的age属性修改为"+val);
edit(val);
}
});
obj.age = 20;
console.log(obj);
</script>