前面几天,写了一些面试题,应付一下后续的面试。
最近打算把自己的缺点优化一下,怎么说呢,其实很喜欢学习,接触自己不懂的东西,我一开始会有一个缺点,就是我会走的很快,这个走的很快是指,学习很东西,没有去把自己学习的东西进行一个深度的学习,之前自己学习的就是皮毛,我接下来会进行一个深入的学习,之前学习的东西进行一个深度的思考。
思考-如果没有设计模式会怎么样?
刚开始接触前端的时候,写代码是没有使用设计模式的,比如一个简单的
if else
判断自己会写很长的判断,在可读性来说是很差的,后续的同事接手代码的时候,看到很长的if else
也会很难理解。
写代码的基本要求就是自己看的懂,别人也要看的懂。
总结上面的废话来说,针对业务逻辑变得复杂多样了,就需要提高代码的可读性和维护性
设计模式的五种基本原则
开放-封闭原则
开放指的是在新增,对扩展开放
封闭指的是 不要修改我们之前的源码
总结就是不用修改源码就能扩展功能
单一职责原则
我们的模块只做一件事件,模块职责越单一越好
依赖倒置原则
上层模块不能依赖具体的下层模块,应该依赖抽象模块
// 具体层 (下层模式)
function Like1 () {}
function Like2 () {}
function Like3 () {}
// 抽象模式
function likeItem(like) {
let res = {
like1: new Like1(),
like2: new Like2(),
like3: new Like3(),
}
return res[like]
}
// 上层模式
function likes (like) {
return likeItem(like1)
}
likes('like1')
接口隔离原则
接口要细化,功能要单一,一个接口不要调用太多方法
单一职责原则主要关注于模块本身,接口隔离原则关注于接口;
我们尽量细化接口,每个接口做的事情尽量单一化。
里氏替换原则
主要用于继承,它的意义就是任何使用父类的地方都可以使用子类去替换,更加直白的说,子类在继承父类的时候,子类必须完全保证继承父类的属性和方法,这样的话,可以使用父类的地方,也可以使用子类替换。
工厂模式
- 原则
符合开放-封闭原则
构造函数和创建者分离
- 使用场景
jq - $(‘div’)
React.createElement
vue 异步组件
简单的工厂模式
class Father {
getInfo(val) {
console.log(val);
}
}
class Click extends Father {
}
class Confirm extends Father {
}
class Cancel extends Father {
}
class Popup {
static buildPopup(val) {
switch (val) {
case 'add':
return new Click().getInfo('数值加法')
case 'sub':
return new Confirm().getInfo('数值减法')
case 'take':
return new Cancel().getInfo('数值乘法')
default:
break;
}
}
}
let p = Popup.buildPopup('add')
let p1 = Popup.buildPopup('sub')
let p2 = Popup.buildPopup('take')
抽象的工厂模式
class Product {
constructor(num1, num2) {
this.num1 = num1
this.num2 = num2
}
add() {
console.log('数值相加', this.num1 + this.num2);
}
sub() {
console.log('数值相减', this.num1 - this.num2);
}
take() {
console.log('数值相乘', this.num1 * this.num2);
}
}
class CreatCount {
create(val1, val2) {
return new Product(val1, val2)
}
}
let c = new CreatCount()
let p = c.create(2, 3)
p.add()
p.sub()
p.take()
单例模式
单例模式是我接触的第一模式
使用场景
登录框的显示和隐藏,还是就是组件重用的弹出框的显示和隐藏
单例模式的经典是: 只会创建一个对象,公用一个实例对象
class SingleObject {
show() {
console.log('展示弹出框');
}
hide() {
console.log('隐藏弹出框');
}
}
SingleObject.getInstance = (function () {
let instance = null
return function () {
if (!instance) {
instance = new SingleObject()
}
return instance
}
})()
let p = SingleObject.getInstance()
p.show()
p.hide()
console.log(p.show() === p.hide());
策略模式
策略模式在工作中可能是用的最多的了
原则:
- 符合开放封闭原则
- 不同策略分开处理
function strategy(type, a, b) {
let typeData = {
'add': function (a, b) {
return a + b
},
'sub': function (a, b) {
return a - b
},
}
return typeData[type](a, b)
}
let res = strategy('add', 2, 3)
console.log(res);
外观模式
外观模式是提供了一个统一的接口来简化接口
不符合单一原则,和开放封闭原则
下面有两个案例可以看下
// 接口A
function getName() {
return 'zs'
}
// 接口B
function getAge() {
return 18
}
// 统一管理接口A 和 B
function getAllInfo(FnName, Fnage) {
let info = FnName() + '-' + Fnage()
return info
}
getAllInfo(getName, getAge)
class E {
stopPropagation() {
console.log('阻止冒泡事件');
}
preventDefault() {
console.log('阻止默认行为');
}
}
var stopEvent = function (e) { //同时阻止事件默认行为和冒泡
e.stopPropagation();
e.preventDefault();
}
stopEvent(new E())
迭代器模式
适用于
顺序遍历有序集合
使用者不必知道集合的内部结构
迭代器对象和目标对象分离
迭代器将使用者与目标对象隔开
符合开放封闭原则
实现的代码
class Iterator {
constructor(param) {
this.list = param.list
this.index = 0
}
next() {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext() {
if (this.list.length > this.index) {
return true
}
return false
}
}
class Container {
constructor(list) {
this.list = list
}
getIterator() {
return new Iterator(this)
}
}
let res = [1, 2, 3, 4]
let p = new Container(res)
p = p.getIterator()
while (p.hasNext()) {
console.log(p.next());
}
发布订阅模式
事件模拟场景
有这样的一个事件场景 张三 李四 王五 关注了一个中介,
张三和李四关注的是 10-20万 王五关注的是 30-50万
中介公司如果发布了 10-20 只通知张三和李四不通知王五
class E {
constructor() {
this._callback = []
}
on(fn) {
this._callback.push(fn)
}
emit(value, type) {
this._callback.forEach((callback) => {
if (callback.type === type) {
callback.fn(value)
}
})
}
}
// 分类别订阅 type 1 中央 2 财经
let p = new E()
p.on({
type: 1,
fn: function (value) {
console.log('张三订阅了10-20', value);
}
})
p.on({
type: 1,
fn: function (value) {
console.log('李四订阅了10-20', value);
}
})
p.on({
type: 2,
fn: function (value) {
console.log('王五订阅了30-50', value);
}
})
p.emit('中介发布了10-20万房屋信息', 1)
p.emit('中介发布了30-50万房屋信息', 2)
观察者模式
有这样的一个场景
父母观察孩子的心情, 父母是观察者 孩子是被观察者
// 被观察者 孩子
class Subscribe {
constructor(name, state) {
this.name = name
this.state = state
this.observe = []
}
// 父亲和母亲观察孩子
attach(that) {
this.observe.push(that)
}
// 孩子心情改变时 父母是要被通知到的
setState(state) {
this.state = state
this.observe.forEach((v) => {
v.updata(this)
})
}
}
// 观察者 也就是孩子的父亲和母亲
class Observe {
constructor(name) {
this.name = name
}
// 孩子心情发生改变时,这个函数被调用,父母就会知道孩子的心情变化
updata(that) {
console.log(`${this.name} 知道了 ${that.name} ${that.state}`);
}
}
let baby = new Subscribe('宝贝', '开心')
let father = new Observe('父亲')
let monther = new Observe('母亲')
baby.attach(father)
baby.attach(monther)
baby.setState('不开心')
tip: 在一定程度上来说,发布订阅就是 观察者模式,由于发布订阅模式的流行,就被区分开来了
观察者: 观察者直接订阅主题,当主题被激活的时候,就会触发观察者里的事件
发布订阅模式:订阅者把自己想要订阅的事件注册到调度中心,当发布者发布该事件到调度中心,也就是该事件触发时,由调度中心统一调度,订阅者注册到调度中心的处理代码