系列文章
- 使用JavaScript学习设计模式(1)| 小册免费学
- 使用JavaScript学习设计模式(2)| 小册免费学
- 使用JavaScript学习设计模式(3)| 小册免费学
- 使用JavaScript学习设计模式(4)| 小册免费学
前言
去年的时候先是看了修言大佬的性能优化掘金小册子,收获良多。
之后紧接着买了这本JavaScript 设计模式核⼼原理与应⽤实践,刚好最近有小册免费学的活动,就赶紧把这篇笔记整理出来了,并且补充了小册子中的没有写到的其余设计模式,学习过程中结合 JavaScript 编写的例子,以便于理解和加深印象。
与其说是一篇文章,其实更像是一篇总结性质的学习笔记。
为什么要学习设计模式?
学习之前,先了解什么是设计模式?
简答理解 它是一套被反复使用、多人知晓的、经过分类的、代码设计经验总结。
烹饪有菜谱,游戏有攻略,每个领域都存在一些能够让我们又好又快地达成目标的“套路”。在程序世界,编程的“套路”就是设计模式。
学习它也就是学习这个编程世界的套路,对以后升级打怪打装备有很大的帮助。在瞬息万变的前端领域,设计模式也是一种“一次学习,终生受用”知识。
设计模式的原则
一大法则:
- 迪米特法则:又叫最少知识法则,一个软件实体应该尽可能少的语其他实体发生相互作用,每一个软件单位对其他的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。
五大原则:
- 单一职责原则:一个类,应该仅有一个引起它变化的原因,简而言之,就是功能要单一。
- 开放封闭原则:对扩展开放,对修改关闭。
- 里氏替换原则:基类出现的地方,子类一定出现。
- 接口隔离原则:一个借口应该是一种角色,不该干的事情不敢,该干的都要干。简而言之就是降低耦合、减低依赖。
- 依赖翻转原则:针对接口编程,依赖抽象而不依赖具体。
JavaScript 中常用的是单一功能和开放封闭原则。
高内聚和低耦合
通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚 和 低耦合。
举例一个现实生活中的例子,例如一个公司,一般都是各个部门各司其职,互不干涉。各个部门需要沟通时通过专门的负责人进行对接。
在软件里面也是一样的 一个功能模块只是关注一个功能,一个模块最好只实现一个功能,这个是所谓的内聚。
模块与模块之间、系统与系统之间的交互,是不可避免的, 但是我们要尽量减少由于交互引起的单个模块无法独立使用或者无法移植的情况发生, 尽可能多的单独提供接口用于对外操作, 这个就是所谓的低耦合
封装变化
在实际开发过程中,不发生变化的代码基本是不存在的,所以我要将代码的变化最小化。
设计模式的核心就是去观察你整个逻辑里的变与不变,然后将不变分离,达到使变化的部分灵活、不变的地方稳定的目的。
设计模式的种类
常用的可以分为创建型、结构型、行为型三类,一共 23 种模式。
创建型:
- [x]工厂模式
- [x]单例模式
- [x]原型模式
- [x]构造器模式
- [x]抽象工厂模式
结构型:
- [x]装饰器模式
- [x]适配器模式
- [x]代理模式
- [x]桥接模式
- [x]外观模式
- [x]组合模式
- [x]享元模式
行为型:
- [x]迭代器模式
- [x]发布/订阅模式(观察者)
- [x]策略模式
- [x]状态模式
- [x]解释器模式
- [x]中介者模式
- [x]访问者模式
- [x]备忘录模式
- [x]模板方法模式
- [x]职责链模式
- [x]命令模式
创建型
工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 在 JS 中其实就是借助构造函数实现。
例子
某个班级要做一个录入系统,录入一个人,就要写一次。
let liMing = {
name: "李明",
age: 20,
sex: "男",
};
如果多个录入,则可以创建一个类。
class Student {
constructor(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
}
let zhangSan = new Student("张三", 19, "男");
工厂模式是将创建对象的过程单独封装,使用使只需要无脑传参就行了,就像一个工厂一样,只要给够原料,就可以轻易的制造出成品。
小结
- 构造函数和创建者分离,对 new 操作进行封装
- 符合开放封闭原则
单例模式
比如:Vuex、jQuery
例子
使用场景:一个单一对象,比如:弹窗,无论点击多少次,弹窗只应被创建一次,实现起来也很简单,用一个变量缓存就行了。
【点击查看Demo】:单例模式-在线例子
如上面这个弹框,只有在第一次点击按钮时才会创建弹框,之后都不会在创建,而是使用之前创建的弹框。
如此,便是实现了一个应用于单例模式的弹框。
小结
- 维持一个实例,如果已经创建,就直接返回
- 符合开放封闭原则
原型模式
例子
在 JavaScript 中,实现原型模式是在 ECMAscript5 中,提出的 Object.create 方法,使用现有的对象来提供创建的对象__proto__
。
var prototype = {
name: "Jack",
getName: function() {
return this.name;
},
};
var obj = Object.create(prototype, {
job: {
value: "IT",
},
});
console.log(obj.getName()); // Jack
console.log(obj.job); // IT
console.log(obj.__proto__ === prototype); //true
有原型就有原理性了
构造器模式
function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
// this.info = new CarDetail(model)
// 属性也可以通过 new 的方式产生
}
// 覆盖原型对象上的toString
Car.prototype.toString = function() {
return this.model + " has done " + this.miles + " miles";
};
// 使用:
var civic = new Car("Honda Civic", 2009, 20000);
var mondeo = new Car("Ford Mondeo", 2010, 5000);
console.log(civic.toString()); // Honda Civic has done 20000 miles
console.log(mondeo.toString()); // Ford Mondeo has done 5000 miles
其实就是利用原型链上被继承的特性,实现了构造器。
抽象工厂模式
JS 中是没有直接的抽象类的,abstract 是个保留字,但是还没有实现,因此我们需要在类的方法中抛出错误来模拟抽象类,如果继承的子类中没有覆写该方法而调用,就会抛出错误。
const Car = function() {};
Car.prototype.getPrice = function() {
return new Error("抽象方法不能调用");
};
面向对象的语言里有抽象工厂模式,首先声明一个抽象类作为父类,以概括某一类产品所需要的特征,继承该父类的子类需要实现父类中声明的方法而实现父类中所声明的功能:
/**
* 实现subType类对工厂类中的superType类型的抽象类的继承
* @param subType 要继承的类
* @param superType 工厂类中的抽象类type
*/
const VehicleFactory = function(subType, superType) {
if (typeof VehicleFactory[superType] === "function") {
function F() {
this.type = "车辆";
}
F.prototype = new VehicleFactory[superType]();
subType.constructor = subType;
subType.prototype = new F(); // 因为子类subType不仅需要继承superType对应的类的原型方法,还要继承其对象属性
} else throw new Error("不存在该抽象类");
};
VehicleFactory.Car = function() {
this.type = "car";
};
VehicleFactory.Car.prototype = {
getPrice: function() {
return new Error("抽象方法不可使用");
},
getSpeed: function() {
return new Error("抽象方法不可使用");
},
};
const BMW = function(price, speed) {
this.price = price;
this.speed = speed;
};
VehicleFactory(BMW, "Car"); // 继承Car抽象类
BMW.prototype.getPrice = function() {
// 覆写getPrice方法
console.log(`BWM price is ${this.price}`);
};
BMW.prototype.getSpeed = function() {
console.log(`BWM speed is ${this.speed}`);
};
const baomai5 = new BMW(30, 99);
baomai5.getPrice(); // BWM price is 30
baomai5 instanceof VehicleFactory.Car; // true
通过抽象工厂,就可以创建某个类簇的产品,并且也可以通过 instanceof 来检查产品的类别,也具备该类簇所必备的方法。
本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!