javascript设计模式

1. 面向对象设计SOLID五大原则

(参考:https://www.cnblogs.com/zhutianpeng/p/3456776.html)

1.S(single):单一职责原则

一个类或者程序有且只有一个职责。 如果功能过于复杂,就拆分开,每个部分保持独立

2.O(open)开放封闭原则

软件实体(类,模块,函数等等)应当对扩展开放,对修改闭合。
增加需求时,扩展新代码,而非区修改已有代码

3.L:李氏置换原则(js中使用较少)

子类型必须能够替换它们基类型,子类能覆盖父类

4.I(interface)接口独立原则(js中使用较少)

接口应该仅包含必要的方法,而不该包含其它的。避免出现“胖接口”(类似于单一职责,但是这里更关注接口)
比如:
注意到IBird接口包含很多鸟类的行为,包括Fly()行为.现在如果一个Bird类(如Ostrich鸵鸟)实现了这个接口,那么它需要实现不必要的Fly()行为(Ostrich不会飞). 这个”胖接口”应该拆分未两个不同的接口,IBird和IFlyingBird,IFlyingBird继承自IBird. 这里如果一种鸟不会飞(如Ostrich),那它实现IBird接口。如果一种鸟会飞(如KingFisher),那么它实现IFlyingBird

5.D(dependency)依赖导致原则(js中使用较少)

面向接口编程,依赖于抽象不依赖于具体,
使用方只关注接口,而不是具体类的实现。

2.简单工厂模式

将new单独封装

遇到new时,就要考虑是否该考虑使用工厂模式
示例:你要吃汉堡,需要new 汉堡,汉堡店将new 汉堡的过程封装了,我们直接到汉堡店中去获取汉堡即可,而不用直接new 汉堡,但是要借助 ”汉堡店“这个对象。

class Product {
  constructor(name){
    this.name=name;
  }
  init(){
    console.log("init")
  }
}


class Creator{
  create(name){
    return new Product(name)
  }
}

测试如下:

在这里插入图片描述

在前端中遇到的场景:

jQuery中的$

class jQuery {
    constructor(selector) {
        let slice = Array.prototype.slice
        let dom = slice.call(document.querySelectorAll(selector))
        let len = dom ? dom.length : 0
        for (let i = 0; i < len; i++) {
            this[i] = dom[i]
        }
        this.length = len
        this.selector = selector || ''
    }
    append(node) {

    }
    addClass(name) {

    }
    html(data) {

    }
    // 此处省略若干 API
}
window.$ = function (selector) {
    return new jQuery(selector)
}

React.reateElement

在这里插入图片描述
在这里插入图片描述

Vue异步组件

在这里插入图片描述

3.单例模式

一个类只有一个实例

系统中被唯一使用

示例:
登录框
购物车

代码:

class SingleObject {
    login() {
        console.log('login...')
    }
}
SingleObject.getInstance = (function () {
    let instance
    return function () {
        if (!instance) {
            instance = new SingleObject();
        }
        return instance
    }
})()

// 测试
let obj1 = SingleObject.getInstance()
obj1.login()
let obj2 = SingleObject.getInstance()
obj2.login()
console.log(obj1 === obj2)

前端使用场景:

jQuery只有一个$

if(window.jQuery!==null){
    return window.jQuery
}else{
    //初始化
}

模拟登录框:

class LoginForm {
    constructor() {
        this.state = 'hide'
    }
    show() {
        if (this.state === 'show') {
            alert('已经显示')
            return
        }
        this.state = 'show'
        console.log('登录框已显示')
    }
    hide() {
        if (this.state === 'hide') {
            alert('已经隐藏')
            return
        }
        this.state = 'hide'
        console.log('登录框已隐藏')
    }
}
LoginForm.getInstance = (function () {
    let instance
    return function () {
        if (!instance) {
            instance = new LoginForm();
        }
        return instance
    }
})()

// 一个页面中调用登录框
let login1 = LoginForm.getInstance()
login1.show()
// login1.hide()

// 另一个页面中调用登录框
let login2 = LoginForm.getInstance()
login2.show()

// 两者是否相等
console.log('login1 === login2', login1 === login2)

vuex和redux中的store

4.代理模式

代码:

class Adaptee{
  specificRequest(){
    return "外国插头"
  }
}


class Target{
  constructor(){
    this.adaptee=new Adaptee()
  }
  request(){
    let info=this.adaptee.specificRequest()
      return info+'-->转化器-->中国标准的插头'
  }
}

测试:

在这里插入图片描述

前端中常见使用场景

封装旧接口

//封装自己的ajax,使用方式如下。
ajax({
  url:'/getData',
  type:"Post",
  dataType:'json',
  data:{
    id:"123"
  }
})
.done(function(){})

假设:由于理食原因,代码中的使用方式要使用
$.ajax({....})
那么我们需要再次封装上边的ajax
var $={
  ajax:function(options){
    return this.ajax(option);
  }
}

vue的computed的作用

一般在vue中,要对传入的参数进行特定改造,就可以使用computed

5.装饰器模式

作用

为对象添加新功能
不改变其原有的结构和功能

代码:

class Circle{
  draw(){
    console.log("画圆")
  }
}


//我们想实现一个功能,是画圆,同时打印一句话
class Decorator{
  constructor(circle){
    this.circle=circle
  }
  draw(){
    this.circle.draw()
    console.log('画完圆后,我还要做很多其他功能')
  }
}

测试

在这里插入图片描述

前端常见使用场景

1,ES7装饰器

2.core-decorators(第三方开源lib)

提供常用的装饰器

// import { readonly } from 'core-decorators'

// class Person {
//     @readonly
//     name() {
//         return 'zhang'
//     }
// }

// let p = new Person()
// alert(p.name())
// // p.name = function () { /*...*/ }  // 此处会报错


import { deprecate } from 'core-decorators';

class Person {
  @deprecate
  facepalm() {}

  @deprecate('We stopped facepalming')
  facepalmHard() {}

  @deprecate('We stopped facepalming', { url: 'http://knowyourmeme.com/memes/facepalm' })
  facepalmHarder() {}
}

let person = new Person();

person.facepalm();
// DEPRECATION Person#facepalm: This function will be removed in future versions.

person.facepalmHard();
// DEPRECATION Person#facepalmHard: We stopped facepalming

person.facepalmHarder();
// DEPRECATION Person#facepalmHarder: We stopped facepalming
//
//     See http://knowyourmeme.com/memes/facepalm for more details.

6.代理模式

特点:

使用者无权访问目标对象
中间加代理,通过代理做授权和控制

示例

科学上网
明星经纪人

代码:

class ReadImg{
  constructor(fileName){
    this.fileName=fileName;
    this.loadFromDist()//初始化,从硬盘中加载,模拟
  }
  display(){
    console.log("展示。。。"+this.fileName)
  }
  loadFromDist(){
    console.log("从硬盘加载"+this.fileName)
  }
}


//输入参数是一样的。都是fileName,
class ProxyImg{
  constructor(fileName){
    this.realImg=new ReadImg(fileName)
  }
  display(){
    this.realImg.display()
  }
}

前端常见使用场景

网页事件代理(addEventListener)

jQuery $.proxy

ES6 Proxy

// 明星
let star = {
    name: '张XX',
    age: 25,
    phone: '13910733521'
}

// 经纪人
let agent = new Proxy(star, {
    get: function (target, key) {
        if (key === 'phone') {
            // 返回经纪人自己的手机号
            return '18611112222'
        }
        if (key === 'price') {
            // 明星不报价,经纪人报价
            return 120000
        }
        return target[key]
    },
    set: function (target, key, val) {
        if (key === 'customPrice') {
            if (val < 100000) {
                // 最低 10w
                throw new Error('价格太低')
            } else {
                target[key] = val
                return true
            }
        }
    }
})

// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)

// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 150000
// agent.customPrice = 90000  // 报错:价格太低
console.log('customPrice', agent.customPrice)

代理模式vs适配器模式

适配器模式:提供一个不同的接口(如不同版本的插头)
代理模式:提供一模一样的接口

代理模式vs装饰模式

装饰器模式:扩展功能,原有功能不变,可以直接使用
代理模式:显示原有功能,但是经过限制或者阉割之后。

7.外观模式

特点:

为子系统中第一组接口提供一个高层接口
使用者使用这个高层接口

使用场景

函数参数的变化

function bindEvent(elem,type,selector,fn){
  if(fn===null){
    fn=selector
    selector=null
  }
}

//调用
bindEvent(elem,"click","#div1",fn)
bindEvent(elem,"click",fn)

8.观察者模式(!!!重点)

特点:

发布订阅
一对多

生活示例

点咖啡,点之后,等被叫

// 主题,接收状态变化,触发每个观察者
class Subject {
    constructor() {
        this.state = 0
        this.observers = []
    }
    getState() {
        return this.state
    }
    setState(state) {
        this.state = state
        this.notifyAllObservers()
    }
    attach(observer) {
        this.observers.push(observer)
    }
    notifyAllObservers() {
        this.observers.forEach(observer => {
            observer.update()
        })
    }
}

// 观察者,等待被触发
class Observer {
    constructor(name, subject) {
        this.name = name
        this.subject = subject
        this.subject.attach(this)
    }
    update() {
        console.log(this.name+"update", "state: "+this.subject.getState())
    }
}

// 测试代码
let s = new Subject()
let o1 = new Observer('o1', s)
let o2 = new Observer('o2', s)
let o3 = new Observer('o3', s)

s.setState(1)
s.setState(2)
s.setState(3)


测试结果:

在这里插入图片描述

前端常用场景

网页事件绑定
Promise
jQuery callbacks
nodejs自定义事件
nodejs中处理http请求,
多进程通讯
vue和react组件生命周期触发
vue 的watch

9.迭代器模式

class Iterator {
    constructor(conatiner) {
        this.list = conatiner.list
        this.index = 0
    }
    next() {
        if (this.hasNext()) {
            return this.list[this.index++]
        }
        return null
    }
    hasNext() {
        if (this.index >= this.list.length) {
            return false
        }
        return true
    }
}

class Container {
    constructor(list) {
        this.list = list
    }
    getIterator() {
        return new Iterator(this)
    }
}

// 测试代码
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
    console.log(iterator.next())
}

前端常用场景

jQuery 的each
ES6的Iterator

let arr = [1, 2, 3, 4]
let nodeList = document.getElementsByTagName('p')
let m = new Map()
m.set('a', 100)
m.set('b', 200)

// function each(data) {
//     // 生成遍历器
//     let iterator = data[Symbol.iterator]()

//     // console.log(iterator.next())  // 有数据时返回 {value: 1, done: false}
//     // console.log(iterator.next())
//     // console.log(iterator.next())
//     // console.log(iterator.next())
//     // console.log(iterator.next())  // 没有数据时返回 {value: undefined, done: true}

//     let item = {done: false}
//     while (!item.done) {
//         item = iterator.next()
//         if (!item.done) {
//             console.log(item.value)
//         }
//     }
// }

function each(data) {
    for (let item of data) {
        console.log(item)
    }
}

each(arr)
each(nodeList)
each(m)

状态模式

原型模式

桥接模式

组合模式

享元模式

策略模式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值