设计模式、垃圾回收机制

119 阅读8分钟

设计模式

什么是设计模式

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。 设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。以前程序员在开发项目过程中遇到问题总结出来的一套解决方案(特定代码写法)。总共有 23 种经典设计模式。这些模式可以分为三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)、行为型模式(Behavioral Patterns)

设计模式分类

创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

  • 工厂模式(Factory Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 单例模式(Singleton Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)

结构型模式
这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。

  • 适配器模式(Adapter Pattern)
  • 桥接模式(Bridge Pattern)
  • 过滤器模式(Filter、Criteria Pattern)
  • 组合模式(Composite Pattern)
  • 装饰器模式(Decorator Pattern)
  • 外观模式(Facade Pattern)
  • 享元模式(Flyweight Pattern)
  • 代理模式(Proxy Pattern)

行为型模式
这些设计模式特别关注对象之间的通信。

  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 解释器模式(Interpreter Pattern)
  • 迭代器模式(lterator Pattern)
  • 介者模式(Mediator Pattern)
  • 备忘录模式(Memento Pattern)
  • 观察者模式(Observer Pattern)
  • 状态模式(State Pattern)
  • 空对象模式(Null Object Pattern)
  • 策略模式(Strategy Pattern)
  • 模板模式(Template Pattern)
  • 访问者模式(Visitor Pattern)

单例模式

单例模式(Singleton Pattern)是最简单常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。确保整个应用中只有单个对象存在。

const singleton = (function () {  
    //真正的构造函数体,用于实例化对象
    function Person(name) {
        this.name = name
    }
    Person.prototype.say = function () {
        console.log(this.name, "说话")
    }
    //这个变量因为在一个不被销毁的函数执行空间中
    //所以会一直存在
    let instance = null
    return function getInstance() {
        if (instance == null) {
            instance = new Person()
        }
        return instance
    }
})()
let p1 = singleton()
let p2 = singleton()
console.log(p1 === p2)

静态方法

static关键字解释:类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为"静态方法"。

class Person{
    static say () {
        //通过类名直接调影
    }
}
person.say()

class封装单例

class CreateDiv{
    static getInsance(){
        if(this.instance == null){
            this.instance = new CreateDiv()
        }
        return this.instance
    }
    constructor() {
        this.instance = null
        this.divEle = document.createElement('div')
        document.body.appendChild(this.divEle)
    }
    init(txt) {
        this.divEle.innerHTML = txt
    }
}

let div1 = CreateDiv.getInsance()
div1.init('这是一条信息")

let div2 = createDiv.getInsance()
div2.init('这是一条新信息,今天是周五')

let div3 = CreateDiv.getInsance()
div3.init('这是一条新信息,明天是周六,还有一天放假休息")

观察者

观察者模式 英文名称叫做 Observer
官方解释: 当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新,解决了主体对象与观察者之间功能的耦合,即一个对象状态改变给其他对象通知的问题

subject:功能:添加观察者,通知消息
observer功能:接收消息

/*
    主体,被观察者,明星
*/
class Subject{
    constructor(name){
        this.name = name
        this.oberverList =[]
    }
    updateMessage(message){
        this.oberverList.forEach(o => {
            o.nodify(this.name + message)
        }
    }
    add(observer){
        this.oberverList.push(observer)
    }
}

/*
    观察者,粉丝
*/
class observer{
    constructor(name){
        this.name = name
    }
    nodify(message){
        console.log(message, this.name, '很伤心')
    }
}

发布/订阅模式

发布/订阅模式由 订阅者、发布者、信号中心构成
假定,存在一个”信号中心”,某个任务执行完成,就向信号中心”发布”(publish)一个信号, 其他任务可以向信号中心”订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。 这就叫做”发布/订阅模式”(publish-subscribe pattern)

class EventEmitter {
    constructor () {
        // { eventType: [handler1,handler2] }
        this.subs = 0
    )
    //订阅通知
    $on (eventType,handler) {
        this.subs[eventType] = this.subs[eventType] || []
        this.subs[eventType].push(handler)
    }
    //发布通知
    $emit (eventType){
        if (this.subs[eventType]){
            this.subs[eventType].forEach(handler => {
                handler()
            })
        }
    }
    //测试
    var bus = new EventEmitter()
    //注册事件
    bus. $on("click", function () {
        console.log('click')
    })
    bus.$on("click", function () {
        console.log('click1')
    })
    //触发事件
    bus.$emit('click ')

Vue的自定义事件

let vm = new vue()
vm.$on("datachange", () => {
    console.log( " datachange ')
})
vm.$on("datachange", () => {
    console.log( " datachange1 ')
})
vm.$emit("datachange")

兄弟组件通信过程

// eventBus.js
//事件中心
let eventHub = new vue() 
//componentA.vue
//发布者
addTodo: function () {
    //发布消息(事件)
    eventHub.$emit('add-todo', {text: this.newTodoText})
    this.newTodoText = ''
}
//ComponentB.vue
//订阅者
created: function () {
    //订阅消息(事件)
    eventHub. $on('add-todo', this.addTodo)
}

与观察者区别

观察者模式是由具体目标调度,比如当事件触发,Dep 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。

image.png

组合模式

组合模式,就是把几个构造函数的启动方式组合再一起,然后用一个 ”遥控器“ 进行统一调用

class GetHome {
    init () {
        console.log('到家了)
    }
}
class OpenComputer {
    init () {
        console.log('打开电脑')
    }
}
class PlayGame {
    init () {
        console.log('玩游戏')
    }
}
//上面几个构造函数的创造的实例化对象的启动方式都一致
//那么我们就可以把这几个函数以组合模式的情况书写
//然后统一启动

准备一个 组合模式 的构造函数

class Compose {
    constructor  {
        this.compose = []
    }

    //添加任务的方法
    add (task) {
        this.compose.push(task )
    }
    
    //一个执行任务的方法
    execute () {
        this.compose.forEach(item => {
            item.init()
        })
    }
}

组合模式构造函数把前面的几个功能组合起来

const c = new Compose()
//把所有要完成的任务都放在队列里面
c.add(new GetHome())
c.add(new OpenComputer)
c.add(new PlayGame)
//直接器动任务队列
c.execute()
//就会按照顺序执行三个对象中的init函数

垃圾回收机制

垃圾回收

在计算机编程中用于描述查找和删除那些不再被其他对象引用的对象处理过程。换句话说,垃圾回收是删除任何其他对象未使用的对象的过程。垃圾收集通常缩写为 "GC",是JavaScript中使用的内存管理系统的基本组成部分。简单理解: 回收释放不在使用的"对象"占用的内存空间

内存管理

不管什么程序语言,内存生命周期基本是一致的:分配你所需要的内存;使用分配到的内存(读、写);不需要时将其释放、归还

var n = 123;//给数值变量分配内存
var s = "azerty"; //给字符串分配内存
var o = {
    a: 1,
    b: null
};//给对象及其包含的值分配内存

//给数组及其包含的值分配内存(就像对象一样)
var a = [1, null, "abra"];

当内存不再需要使用时释放:
大多数内存管理要求开发人员来确定在程序中哪一块内存不再需要并且释放它。像 C 语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()free()
JavaScript 高级语言解释器嵌入了“垃圾回收器”, 它的主要工作是跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。

垃圾回收算法

引用计数垃圾收集
这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

var o = {
    a: {
        b:2
    }
}:
//两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
//很显然,没有一个可以被垃圾收集

var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1;//现在,“这个对象”只有一个O2变量的引用了,“这个对象”的原始引用。已经没有

Var oa = o2.a;//引用“这个对象”的a属性
               //现在,“这个对象”有两个引用了,一个是O2,一个是a

o2 = "yo";//虽然最初的对象现在已经是零引用了,可以被垃圾回收了
           //但是它的属性a的对象还在被oa引用,所以还不能回收

oa = null; //a属性的那个对象现在也是零引用了
           //它可以被垃圾回收了

该算法有个限制:无法处理循环引用的事例。在下面的例子中,两个对象被创建,并互相引用,形成了一个循环。它们被调用之后会离开函数作用域,所以它们已经没有用了,可以被回收了。然而,引用计数算法考虑到它们互相都有至少一次引用,所以它们不会被回收。

function f () {
    var o = {};
    var o2= {};
    o.a = o2; // o 引用o2
    o2.a = o; // o2引用o
    
    return "azerty";
}

f() ;

标记 - 清除算法

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。这个算法假定设置一个叫做根(root)的对象(在 Javascript 里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。从 2012 年起,所有现代浏览器都使用了标记 - 清除垃圾回收算法。