什么是设计模式
设计模式是在某种场合下对某个问题的一种解决方案。设计模式是通过概念总结出来的模版,总结出来的固定的东西。每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。
在前端开发中,设计模式是一种被广泛应用的思想。设计模式可以帮助开发者解决常见的问题,并提供可重用的解决方案。本文将会介绍前端常见的设计模式,并通过代码详解它们的实现。
为什么使用设计模式
设计模式是各种业务场景的最佳实践,有助于我们在写日常业务中时,提高自身的思路。例如单例模式,就可以用于登陆框,模态框等场景。每种设计模式,必然有其适合应用的场景,灵活运用设计模式,可以提高代码的可维护性,还可提升自身思维能力。
设计模式六大原则
所有的设计模式都需要遵循下面的六大原则
1、开闭原则
开闭原则的意思是:对扩展开放,对修改关闭,在程序需要进行拓展的时候,不能去修改原有的代码
2、里氏代换原则
里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现,里氏代换原则是对开闭原则的补充,实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范
3、依赖倒转原则
这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体
4、接口隔离原则
这个原则的意思是:使用多个隔离的接口,比使用单个接口要好,它还有另外一个意思是:降低类之间的耦合度,此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合
5、最少知道原则
最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立
6、合成复用原则
合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承
常用的设计模式
代理模式
代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问。
代理模式的生动理解可以类比为一个人找代理去购物。这个人想购买一些物品,但是由于某些原因无法亲自前往商店购买,于是他找到了一个代理人去帮他购买。
代理模式中的代理对象就像是这个代理人,它可以代替真实对象进行一些操作。
例:HTML元 素事件代理
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
let ul = document.querySelector('#ul');
ul.addEventListener('click', event => {
console.log(event.target);
});
</script>
-
优点: 代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用。 代理对象可以扩展目标对象的功能;通过修改代理对象就可以了,符合开闭原则;
-
缺点:处理请求速度可能有差别,非直接访问存在开销。
迭代器模式
迭代器模式:简单来说是在不暴露数据类型的情况下访问集合中的数据
应用场景:数据结构中有多种数据类型,列表,树等,提供通用操作接口
例:
(function (a, b, c) {
const arg = arguments;
const iterator = arg[Symbol.iterator]();
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}
})(1, 2, 3)
-
优点:可实现统一遍历接口,符合单一功能和开放封闭原则
-
缺点:迭代器模式将存储数据遍历数据两个职责拆分 ; 如果新添加一个集合类需要增加该集合类对应的迭代器类,类的个数成对增加。在一定程度上增加了系统复杂性 ;
原型模式
对于前端来说,当新创建的对象和已有对象存在较大共性时,可以通过对象的复制来达到创建新的对象,这就是原型模式。
例:
// Object.create()实现原型模式
const user = {
name: 'zhangsan',
age: 18
};
let userOne = Object.create(user);
console.log(userOne.__proto__); // {name: "zhangsan", age: 18}
// 原型链继承实现原型模式
class User {
constructor (name) {
this.name = name;
}
getName () {
return this.name;
}
}
class Admin extends User {
constructor (name) {
super(name);
}
setName (_name) {
return this.name = _name;
}
}
const admin = new Admin('zhangsan');
console.log(admin.getName());
console.log(admin.setName('lisi'));
- 优点:性能高,流程简单
- 缺点:实现复杂
单例模式
单例模式是一种创建型设计模式
顾名思义,单例模式中Class的实例个数最多为1。当需要一个对象去贯穿整个系统执行某些任务时,单例模式就派上了用场。
例子:
var single = (function(){
var unique;
function getInstance(){
// 如果该实例存在,则直接返回,否则就对其实例化
if( unique === undefined ){
unique = new Construct();
}
return unique;
}
function Construct(){
// ... 生成单例的构造函数的代码
}
return {
getInstance : getInstance
}
- 优点: 划分命名空间,减少全局变量 ;增强模块性,把自己的代码组织在一个全局变量名下,放在单一位置,便于维护; 且只会实例化一次。简化了代码的调试和维护
- 缺点:由于单例模式提供的是一种单点访问,所以它有可能导致模块间的强耦合从而不利于单元测试;无法单独测试一个调用了来自单例的方法的类,而只能把它与那个单例作为一个单元一起测试。
本文借鉴: