TODO: 分别用普通函数和Class编写
工厂模式
最常见的提供同类的操作方法集合和一些内部变量的一个类就是工厂模式了
js
class Man {
constructor(name) {
this.name = name
}
alertName() {
alert(this.name)
}
}
class Factory {
static create(name) {
return new Man(name)
}
}
Factory.create('me').alertName()
工厂的作用就是隐藏了创建实例的复杂度,只需要提供一个接口,简单清晰。
Vue的创建异步组件
ts
function createComponent (
Ctor,
data,
context,
children,
tag,
) {
// 逻辑处理...
const vnode = new VNode(
`vue-component-${Ctor.cid}${name ? `-${name}` : ''}`,
data, undefined, undefined, undefined, context,
{ Ctor, propsData, listeners, tag, children },
asyncFactory
)
return vnode
}
createComponent 内部创建一个组件实例(复杂过程) 工厂帮助我们隐藏了这个复杂的过程,只需要一句代码调用就能实现功能
单例模式
单例模式的核心就是保证全局只有一个对象可以访问 用一个变量确保实例只创建一次就行
js
class Singleton {
constructor() {}
}
Singleton.getInstance = (function() {
let instance
return function() {
if (!instance) {
instance = new Singleton()
}
return instance
}
})()
let s1 = Singleton.getInstance()
let s2 = Singleton.getInstance()
console.log(s1 === s2) // true
Vuex单例模式控制只安装一次
js
let Vue // bind on install
export function install (_Vue) {
if (Vue && _Vue === Vue) {
// 如果发现 Vue 有值,就不重新创建实例了
return
}
Vue = _Vue
applyMixin(Vue)
}
👆 不用class,而是用函数和一个变量记录是否给这个vue实例挂载过vuex
适配器模式
适配器用来解决两个接口不兼容的情况,不需要改变已有的接口,通过包装一层的方式实现两个接口的正常协作。
js
class Plug {
getName() {
return '港版插头'
}
}
class Target {
constructor() {
this.plug = new Plug() // 不一定要new一个类,可以是普通的赋值
}
// getname方法即内部逻辑不变 把变量转移到constructor
getName() {
return this.plug.getName() + ' 适配器转二脚插头'
}
}
let target = new Target()
target.getName() // 港版插头 适配器转二脚插头
TODO: 展开 子组件使用 computed 来处理 prop 数据,这个过程就使用到了适配器模式
装饰器模式
不改变已有的接口,作用是给对象添加功能
js
function readonly(target, key, descriptor) {
descriptor.writable = false
return descriptor
}
class Test {
@readonly
name = 'yck'
}
let t = new Test()
t.yck = '111' // 不可修改
在 React 中,装饰模式其实随处可见
js
import { connect } from 'react-redux'
class MyComponent extends React.Component {
// ...
}
export default connect(mapStateToProps)(MyComponent)
代理模式
代理是为了控制对对象的访问,不让外部直接访问到对象
在实际代码中其实代理的场景很多,也就不举框架中的例子了,比如事件代理就用到了代理模式。
html
<ul id="ul">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
let ul = document.querySelector('#ul')
ul.addEventListener('click', (event) => {
console.log(event.target);
})
</script>
太多的 li,不可能每个都去绑定事件。这时候可以通过给父节点绑定一个事件,让父节点作为代理去拿到真实点击的节点。
发布订阅模式(观察者模式)
外观模式
外观模式提供了一个接口,隐藏了内部的逻辑,更加方便外部调用 不就工厂模式?
如需要实现一个兼容多种浏览器的添加事件方法
js
// dom节点 事件类型 回调函数
function addEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) {
// 1. 支持 addEventListener
elm.addEventListener(evType, fn, useCapture)
return true
} else if (elm.attachEvent) {
// 2. 支持 attachEvent
var r = elm.attachEvent("on" + evType, fn)
return r
} else {
// 3. 支持 on[事件类型]
elm["on" + evType] = fn
}
}
👆 就是普通的函数逻辑抽离吧,这也算设计模式?