前端设计模式:
参考:
https://www.cnblogs.com/tugenhua0707/p/5198407.html
https://zhuanlan.zhihu.com/p/256405681
设计模式6大原则:https://www.cnblogs.com/toutou/p/4870926.html
前端面经:https://juejin.cn/post/6939774328858738696
设计模式
https://juejin.cn/post/6844904125721772039#heading-33
什么是前端设计模式:设计模式是一套被反复使用的、多数人知晓、经过分类编目的优秀代码设计经验的总结。特定环境下特定问题的处理方法。
作用:复用设计和代码,提高扩展性[面向接口编程,预留扩展插槽,新功能或特性很容易加入]
核心思想:封装变化。
最常用的模式有哪些啊。稍微了解的模式有哪些啊。
设计模式6大原则:为了升级扩展和维护的方便。降低依赖,降低耦合。
开闭原则:观察代码整个逻辑的变与不变两部分。将变化与不变的部分分离。使变化的部分灵活可扩展,使不变的部分保持稳定。
里氏代换原则:子类可以实现父类的抽象方法来扩展父类,不能覆盖父类的非抽象方法,即不能改变父类原有的功能。子类中可以增加自己特有的功能。
单一职责:每个类都有自己要负责的职责。
迪米特法则:一个对象尽量减少对其他对象的依赖,原则是低耦合,高内聚,提高代码复用率。
接口隔离原则:使用多个隔离的接口比使用单个接口要好。降低依赖,降低耦合。
依赖倒转原则:面向接口编程。依赖于抽象而不依赖于具体。开闭原则的基础。
GOF提出23种设计模式。
创建型模式:5
1.⭐原型模式:应用->设置函数的原型属性,实现继承。
原型有一个样板实例,原型模式简单理解就是拷贝实例对象。类初始化需要消耗很多资源,通过原型模式进行拷贝对象急减少了消耗。
function Animal(name) {
console.log('0000'+name);
this.name = name || 'animal';
this.eat = function(food) {
console.log(`${this.name} 在吃${food}`)
}
}
Animal.prototype.sleep = function() {
console.log(this.name + '正在睡觉');
}
function Cat() {};//创建抽象类
Cat.prototype = new Animal();//继承Animal类
Cat.prototype.name = 'cat';
var cat = new Cat();
console.log(cat);
cat.eat('鱼');
cat.sleep();
console.log(cat instanceof Cat);
console.log(cat instanceof Animal);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2.⭐单例模式:应用->只允许被实例化一次。可以利用闭包。确保一个类只有一个实例
单例模式的应用:windows的任务管理器。不能同事打开两个任务管理器。支付功能,一个订单只能支付一次。
优缺点:
节约系统资源。当需要频繁地创建和销毁对象时,可以提高系统的性能。
扩展性不好,因为没有抽象层。
单例类职责国中,在一定程度上违反了“单一职责”的原则
单例模式不适用于变化的对象,因为无法保存状态。
滥用单例模式会带来一些负面影响:比如如果实例化的对象长时间没有被引用,系统当成垃圾回收,将导致对象状态的丢失。
let singleCase = function(name){
this.name = name;
};
// 获取实例对象
let getInstance = (function() {
var instance = null;
return function(name) {//返回的这个函数引用了instance。所以导致IIFE立即执行函数执行完被销毁之后,变量instance会被缓存起来,形成了闭包。所以当传入two参数再次执行时,不会重新声明一个变量instance而是去访问缓存的instance。
if(!instance) {//相当于一个一次性阀门,只能实例化一次
instance = new singleCase(name);
console.log(instance);
}
return instance;
}
})();//
// 测试单体模式的实例,所以one===two
let one = getInstance("one");
let two = getInstance("two");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.⭐工厂模式:应用->创建实例。
简单工厂 :用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品)
抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)
直接使用工厂方法就能创建对象,而不用使用new关键字。在创建对象的过程中,看不到构造函数实例化的过程。
实例化对象的最佳方式。通过使用一个共同的接口来指向新创建的对象。
var Factory=function(type,content){
if(this instanceof Factory){//判断是不是实例对象,是就返回相关属性。不是就new创建。
return new this[type](content);
}else{
return new Factory(type,content);
}
}
Factory.prototype={
Python:function(content){
console.log('Python值为',content);
}
}
Factory('Python','我是Python');
function playerFactory (username){
var user= new object ();
user .username = username;
return user ;
}
var xm = playerFactory( 'xiao ming ')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
3.1.⭐简单工厂模式/静态工厂模式:应用->抽取类相同的属性和方法。创建单一对象
创建一个函数,通过接收参数来生成不同的对象。
提供一个[创建一系列相关或相互依赖对象的]接口。
// let useFactory = function(role) {
// function User(opt) {
// this.name = opt.name;
// this.age = opt.age;
// }
// switch(role) {
// case 'administrator':
// return new User(administrator);
// break;
// case 'admin':
// return new User(admin);
// break;
// default:
// console.log('不是管理员')
// }
// }
// let administrator = UserFactory('administrator');
// let admin = UserFactory('admin');
function UserFactory(role) {
function User(opt) {
this.name = opt.name;
this.age = opt.age;
console.log(this)
}
switch (role.name) {
case 'superAdmin':
return new User(role);
break;
case 'admin':
return new User(role);
break;
case 'user':
return new User(role);
break;
default:
console.log('000')
}
}
//调用
let superAdmin = UserFactory({name:'superAdmin',age:24});
let admin = UserFactory({name:'admin',age:28});
let normalUser = UserFactory({name:'user',age:18});
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
39
40
41
42
43
3.3.抽象工厂模式:将成员对象的实例化推迟到子类中。父类是一个抽象类,具体的业务逻辑在子类中实现。创建多类对象
//子类继承父类,子类重写父类中的抽象方法。
1
2
5.建造者模式:
1
结构型模式:7
1.⭐适配器模式:将一个接口转换成客户端需要的接口而不需要去修改客户端代码。使得不兼容的代码可以一起工作。
应用于适配函数参数。
举个例子:存在3条数据,分别来自3个数据来源,是3种不同的数据结构的。而我们接收的数据格式是规定好的某一种。采用适配器模式,就可以实现兼容,将不同的数据结构都适配成我们所能接收的数据结构。
//鸭子
var Duck = function(){};
Duck.prototype.fly = function(){
throw new Error("该方法必须被重写!");
};
Duck.prototype.quack = function(){
throw new Error("该方法必须被重写!");
}
//火鸡
var Turkey = function(){};
Turkey.prototype.fly = function(){
throw new Error(" 该方法必须被重写 !");
};
Turkey.prototype.gobble = function(){
throw new Error(" 该方法必须被重写 !");
};
//然后再定义具体的鸭子和火鸡的构造函数,分别为:
//鸭子
var MallardDuck = function () {
Duck.apply(this);
};
MallardDuck.prototype = new Duck(); //原型是Duck
MallardDuck.prototype.fly = function () {
console.log("可以飞很长的距离!");
};
MallardDuck.prototype.quack = function () {
console.log("嘎嘎!嘎嘎!");
};
//火鸡
var WildTurkey = function () {
Turkey.apply(this);
};
WildTurkey.prototype = new Turkey(); //原型是Turkey
WildTurkey.prototype.fly = function () {
console.log("飞翔的距离貌似有点短!");
};
WildTurkey.prototype.gobble = function () {
console.log("咯咯!咯咯!");
};
//为了让火鸡也支持quack方法,我们创建了一个新的火鸡适配器TurkeyAdapter:
var TurkeyAdapter = function(oTurkey){
Duck.apply(this);
this.oTurkey = oTurkey;
};
TurkeyAdapter.prototype = new Duck();
TurkeyAdapter.prototype.quack = function(){
this.oTurkey.gobble();
};
TurkeyAdapter.prototype.fly = function(){
var nFly = 0;
var nLenFly = 5;
for(; nFly < nLenFly;){
this.oTurkey.fly();
nFly = nFly + 1;
}
};
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
2.⭐装饰器模式:不改变原对象的基础上,给对象添加属性或方法
let decorator=function(input,fn){
//获取事件源
let input=document.getElementById(input);
console.log(input.onclick)
//若事件源已经绑定事件
if(typeof input.οnclick=='function'){
//缓存事件源原有的回调函数
let oldClickFn=input.onclick;
//为事件源定义新事件
input.οnclick=function(){
//事件源原有回调函数
oldClickFn();
//执行事件源新增回调函数
fn();
}
}else{
//未绑定绑定
input.οnclick=fn;
}
}
//测试用例
decorator('textInp',function(){
console.log('文本框执行啦');
})
decorator('btn',function(){
console.log('按钮执行啦');
})
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
3.外观模式:为子系统中的一组接口提供一个一致的界面,简化复杂接口
//在形式上,外观模式在javascript中就像这样:
function a(x){
// do something
}
function b(y){
// do something
}
function ab( x, y ){
a(x);
b(y);
}
1
2
3
4
5
6
7
8
9
10
11
4.桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。事件监控 ?
1
5.组合模式
6.享元模式
7.⭐代理模式:不直接操作原有对象,而是委托代理者去进行。代理的作用:对我们的请i去预先进行处理,或者转接给实际对象去处理。
代理模式是通过代理来控制对对象的访问。实际功能还是对象本身去执行。代理只是控制是否去执行这个功能。
代理模式使用场景:
模块职责单一且可复用
两个模块间的交互需要一定限制关系
行为型模式:11 关注对象之间的相互交互,解决系统在运行时对象之间的相互通信和协作,进一步明确对象的职责。
1.⭐模版方法模式:使用原型链。在父类中包括实现一些公共方法及封装子类中所有方法的执行顺序,在子类中可以重写方法。定义一个模块供以后传不同的参数调用。基于继承。抽象父类,在父类中封装子类的算法框架。具体实现的子类
let myFather = function(){};//创建抽象类父类。在父类中封装子类的算法框架
myFather.ptototype.one = function() {
console.log('把水煮沸')
}
myFather.ptototype.two = function() {
throw new Error("子类必须重写方法");
}
myFather.ptototype.three = function() {
throw new Error("子类必须重写方法");
}
myFather.prototype.isdo = function() {//添加钩子方法给不一定执行的函数添加条件控制
return true;
}
myFather.prototype.init = function() {
this.one();
this.two();
if(this.isdo()) {//3是否执行要看isdo的返回值
this.three();
}
}
let mySon = function(){};//创建子类
mySon.prototype = new myFather();//继承抽象类
mySon.prototype.two = function() {
console.log('重写two方法')
}
mySon.prototype.isdo = function() {
return window.confirm("需要执行three方法吗");
}
mySon.prototype.three = function() {
console.log('重写three方法')
}
let mySon_duixiang = new mySon();
mySon_duixiang.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
2.⭐观察者模式/发布-订阅模式:解决类与对象,对象与对象之间的耦合/依赖。发布者把消息推送给订阅者。发布,监听,处理
模块之间存在[一对多]的依赖关系。让多个观察者对象同时监听某个主题对象,当对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。
优缺点:
各模块相互独立。
依赖模块不稳定,依赖关系不稳定。
各模块由不同的人员,团队开发。
广泛应用于异步编程。DOM事件。
1
3.状态模式:定义一个对象状态改变会改变的行为变化,解决复杂的if判断
1
4.⭐策略模式:定义了一系列家族算法,并把每一种算法单独封装起来,让算法之间可以相互替换。简化if…else…所带来的复杂性维护难度。
策略+组合:
算法可以自有切换,扩展性很好,避免了使用多重条件判断。
所有策略都需要对外暴露
let strategies = {
'a': function(val) {
return val * 4;
},
'b': function(val) {
return val * 3;
},
'c': function(val) {
return val * 2;
}
}
let calcu = function(level,val) {
return strategies[level](val);
}
calcu('a',777);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
5.访问模式:通过继承封装一些该数据类型不具备的属性,让对象具备数组的操作方法
6.中介者模式:设置一个中间层,处理对象之间的交互
7. ⭐责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
作用:解耦了各节点关系。各节点灵活拆分重组。
使用场景:
负责的是一个完整流程或流程种的某个环节。
各环节可以复用
各环节有一定的执行顺序
各环节可重组
1
委托模式 ,节流模式
事件代理/事件委托:把原本需要绑定到子元素的事件委托给父元素。让父元素承担事件监听的工作。原理是DOM元素的事件冒泡。
好处:减少事件数量,避免内存泄漏,有利于提高性能。
重绘和节流:
singleton:单例模式,用来减少重复创建对象。
factory:工厂模式,用来解耦。
iterator:迭代器模式,用来遍历对象。
observer:观察者模式,用来收发消息。
templete:模板模式,用来避免执行相同的操作。
strategy:策略模式,用来定义算法等。
————————————————
版权声明:本文为CSDN博主「努力学习前端的77」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36545813/article/details/119143935