小册 JavaScript 设计模式核⼼原理与应⽤实践
- 重要: 工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)、单例模式、原型模式、构造器模式
⭐工厂模式
简单工厂模式
工厂方法模式
工厂模式其实就是将创建对象的过程单独封装。工厂模式的目的,就是为了实现无脑传参
// 构造函数
function User(name , age, career, work) {
this.name = name
this.age = age
this.career = career
this.work = work
}
function Factory(name, age, career) {
let work
switch(career) {
case 'coder':
work = ['写代码','写系分', '修Bug']
break
case 'product manager':
work = ['订会议室', '写PRD', '催更']
break
case 'boss':
work = ['喝茶', '看报', '见客户']
case 'xxx':
// 其它工种的职责分配
...
return new User(name, age, career, work)
}
将创建对象的过程单独封装,这样的操作就是工厂模式。同时它的应用场景也非常容易识别:有构造函数的地方,我们就应该想到简单工厂;在写了大量构造函数、调用了大量的 new、自觉非常不爽的情况下,我们就应该思考是不是可以掏出工厂模式重构我们的代码了。
抽象工厂模式
抽象工厂目前来说在JS世界里也应用得并不广泛
抽象工厂模式的定义,是围绕一个超级工厂创建其他工厂。
- 抽象工厂: 用于声明最终目标产品的共性。
- 具体工厂: 继承自抽象工厂、实现了抽象工厂里声明的那些方法。
```javascript
// 定义操作系统这类产品的抽象产品类
class OS {
controlHardWare() {
} }throw new Error('抽象产品方法不允许直接调用,你需要将我重写!');
// 定义具体操作系统的具体产品类 class AndroidOS extends OS { controlHardWare() { console.log(‘我会用安卓的方式去操作硬件’) } }
class AppleOS extends OS { controlHardWare() { console.log(‘我会用🍎的方式去操作硬件’) } } …
class MobilePhoneFactory { // 提供操作系统的接口 createOS(){ throw new Error(“抽象工厂方法不允许直接调用,你需要将我重写!”); } // 提供硬件的接口 createHardWare(){ throw new Error(“抽象工厂方法不允许直接调用,你需要将我重写!”); } }
// 具体工厂继承自抽象工厂 class FakeStarFactory extends MobilePhoneFactory { createOS() { // 提供安卓系统实例 return new AndroidOS() } createHardWare() { // 提供高通硬件实例 return new QualcommHardWare() } }
// 这是我的手机 const myPhone = new FakeStarFactory() // 让它拥有操作系统 const myOS = myPhone.createOS() // 让它拥有硬件 const myHardWare = myPhone.createHardWare() // 启动操作系统(输出‘我会用安卓的方式去操作硬件’) myOS.controlHardWare() // 唤醒硬件(输出‘我会用高通的方式去运转’) myHardWare.operateByOrder()
> **抽象工厂是佐证“开放封闭原则”的良好素材**
<br />
<a name="C00YM"></a>
# ⭐单例模式
**单例模式就是保证一个类仅有一个实例,并提供一个访问它的全局访问点**
> 单例模式想要做到的是,**不管我们尝试去创建多少次,它都只给你返回第一次所创建的那唯一的一个实例**。
> 要做到这一点,就需要构造函数**具备判断自己是否已经创建过一个实例**的能力。
```javascript
class SingleDog {
show() {
console.log('我是一个单例对象')
}
static getInstance() {
// 判断是否已经new过1个实例
if (!SingleDog.instance) {
// 若这个唯一的实例不存在,那么先创建它
SingleDog.instance = new SingleDog()
}
// 如果这个唯一的实例已经存在,则直接返回
return SingleDog.instance
}
}
const s1 = SingleDog.getInstance()
const s2 = SingleDog.getInstance()
// true
s1 === s2
实现一个 Storage
实现Storage,使得该对象为单例,基于 localStorage 进行封装。实现方法 setItem(key,value) 和 getItem(key)。
//静态方法版本:
class Storage{
static getInstance(){
if(!Storage.instance){
Storage.instance = new Storage()
}
return Storage.instance
}
getItem(key){
return localStorage.getItem(key)
}
setItem(key,value){
localStorage.setItem(key,value)
}
}
const storage2 = Storage.getInstance()
storage1.setItem('name', '李雷')
// 李雷
storage1.getItem('name')
// 也是李雷
storage2.getItem('name')
// 返回true
storage1 === storage2
//闭包版
// 先实现一个基础的StorageBase类,把getItem和setItem方法放在它的原型链上
function StorageBase(){}
StorageBase.prototype.getItem = function(key){
return localStorage.getItem(key)
}
StorageBase.prototype.setItem = function(key,value){
return localStorage.setItem(key,value)
}
// 以闭包的形式创建一个引用自由变量的构造函数
const Storage = (functon(){
let instance = null
return function(){
if(!instance){
instance = new StorageBase()
}
return instance
}
})()
// 这里其实不用 new Storage 的形式调用,直接 Storage() 也会有一样的效果
const storage1 = new Storage()
const storage2 = new Storage()
storage1.setItem('name', '李雷')
// 李雷
storage1.getItem('name')
// 也是李雷
storage2.getItem('name')
// 返回true
storage1 === storage2
实现一个模态框
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>单例模式弹框</title>
</head>
<style>
#modal {
height: 200px;
width: 200px;
line-height: 200px;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border: 1px solid black;
text-align: center;
}
</style>
<body>
<button id='open'>打开弹框</button>
<button id='close'>关闭弹框</button>
</body>
<script>
// 核心逻辑,这里采用了闭包思路来实现单例模式
const Modal = (function() {
let modal = null
return function() {
if(!modal) {
modal = document.createElement('div')
modal.innerHTML = '我是一个全局唯一的Modal'
modal.id = 'modal'
modal.style.display = 'none'
document.body.appendChild(modal)
}
return modal
}
})()
// 点击打开按钮展示模态框
document.getElementById('open').addEventListener('click', function() {
// 未点击则不创建modal实例,避免不必要的内存占用;
//此处不用 new Modal 的形式调用也可以,和 Storage 同理
const modal = new Modal()
modal.style.display = 'block'
})
// 点击关闭按钮隐藏模态框
document.getElementById('close').addEventListener('click', function() {
const modal = new Modal()
if(modal) {
modal.style.display = 'none'
}
})
</script>
</html>
原型模式
原型模式不仅是一种设计模式,它还是一种编程范式(programming paradigm),是 JavaScript 面向对象系统实现的根基。
在原型模式下,当我们想要创建一个对象时,会先找到一个对象作为原型,然后通过克隆原型的方式来创建出一个与原型一样(共享一套数据/方法)的对象。
在 JavaScript 里,
Object.create
方法就是原型模式的天然实现——准确地说,只要我们还在借助Prototype来实现对象的创建和原型的继承,那么我们就是在应用原型模式。