原型模式、单例模式

1.Prototype 原型模式

JavaScript 是一种基于原型的面向对象编程语言。即便 JavaScript 现在也引入了类的概念,但它也只是基于原型的语法糖而已

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同), 在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新 对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式 (Prototype Design Pattern),简称原型模式。

创建成本大的对象比如:如果对象中的数据需要经过复杂的计算才能得到(比如排序、计算哈希值),或者需要从 RPC、网络、数据库、文件系统等非常慢速的 IO 中读取,这种情况下,我们就可以利用原型模式,从其他已有对象中直接拷贝得到,而不用每次在创建新对象的时候,都重复执 行这些耗时的操作。

在原型设计模式接口中,需要包含一个clone()方法来让使用使用该接口的类实现相应的clone方法,clone()方法的实现可根据情况自定义,同时还需要确定选择浅拷贝还是深拷贝。

基本原理

  • Prototype Interface:描述 clone() 方法的接口

  • Prototype :实现原型接口的对象

  • Client:使用和创建Prototype的客户端

在这里插入图片描述

interface IProtoType {
    clone(): this
}

class MyClass implements IProtoType {
    field: number[]

    constructor(field: number[]) {
        this.field = field 
    }

    clone() {
        return Object.assign({}, this) //浅拷贝
        // return JSON.parse(JSON.stringify(this)); //深拷贝
    }
}
// The Client
const OBJECT1 = new MyClass([1, 2, 3, 4]) //创建一个包含数组的对象
console.log(`OBJECT1: ${JSON.stringify(OBJECT1)}`)

const OBJECT2 = OBJECT1.clone() // Clone
console.log(`OBJECT2: ${JSON.stringify(OBJECT2)}`)
OBJECT2.field[1] = 101

//因为对象第一层数据为array(引用类型),所以浅拷贝的话改变该数据时会同时修改,采用深拷贝则不用
console.log(`OBJECT2: ${JSON.stringify(OBJECT2)}`)
console.log(`OBJECT1: ${JSON.stringify(OBJECT1)}`)

应用举例

下面分别使用浅拷贝和深拷贝克隆Document对象实例的属性和方法

interface IProtoType {
    clone(mode: number): Document
}

class Document implements ProtoType {
    name: string
    array: [number[], number[]]

    constructor(name: string, array: [number[], number[]]) {
        this.name = name
        this.array = array
    }

    clone(mode: number): Document {
        //可选深拷贝和浅拷贝两种
        let array
        if (mode === 2) {
            array = JSON.parse(JSON.stringify(this.array))//深拷贝
        } else {
            array = Object.assign([], this.array) //默认为浅拷贝
        }
        return new Document(this.name, array)
    }
}
// 创建一个Document对象包含两个数组
const ORIGINAL_DOCUMENT = new Document('Original', [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
])
console.log(ORIGINAL_DOCUMENT)

const DOCUMENT_COPY_1 = ORIGINAL_DOCUMENT.clone(1) //浅拷贝
DOCUMENT_COPY_1.name = 'Copy 1' //浅拷贝+原始类型,不会同时修改原对象
DOCUMENT_COPY_1.array[1][2] = 200 //浅拷贝+array引用类型,会同时修改原数据
console.log(DOCUMENT_COPY_1)
console.log(ORIGINAL_DOCUMENT)
console.log()

const DOCUMENT_COPY_2 = ORIGINAL_DOCUMENT.clone(1) //浅拷贝
DOCUMENT_COPY_2.name = 'Copy 2'
DOCUMENT_COPY_2.array[1] = [9, 10, 11, 12]
console.log(DOCUMENT_COPY_2)
console.log(ORIGINAL_DOCUMENT)
console.log()

const DOCUMENT_COPY_3 = ORIGINAL_DOCUMENT.clone(2) // deep copy
DOCUMENT_COPY_3.name = 'Copy 3'
DOCUMENT_COPY_3.array[1][0] = 1234
console.log(DOCUMENT_COPY_3)
console.log(ORIGINAL_DOCUMENT)
console.log()
Document {
  name: 'Original',
  array: [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]
}

Document {
  name: 'Copy 1',
  array: [ [ 1, 2, 3, 4 ], [ 5, 6, 200, 8 ] ]
}
Document {
  name: 'Original',
  array: [ [ 1, 2, 3, 4 ], [ 5, 6, 200, 8 ] ]
}

Document {
  name: 'Copy 2',
  array: [ [ 1, 2, 3, 4 ], [ 9, 10, 11, 12 ] ]
}
Document {
  name: 'Original',
  array: [ [ 1, 2, 3, 4 ], [ 5, 6, 200, 8 ] ]
}

Document {
  name: 'Copy 3',
  array: [ [ 1, 2, 3, 4 ], [ 1234, 6, 200, 8 ] ]
}
Document {
  name: 'Original',
  array: [ [ 1, 2, 3, 4 ], [ 5, 6, 200, 8 ] ]
}

关于网上的一张关于赋值、浅拷贝、深拷贝的区分图:

在这里插入图片描述

浅拷贝只会复制对象中基本数据类型数据和引 用对象的内存地址,不会递归地复制引用对象,以及引用对象的引用对象……而深拷贝得到 的是一份完完全全独立的对象。所以,深拷贝比起浅拷贝来说,更加耗时,更加耗内存空间

如果要拷贝的对象是不可变对象,浅拷贝共享不可变对象是没问题的,但对于可变对象来说,浅拷贝得到的对象和原始对象会共享部分数据,就有可能出现数据被修改的风险,也就 变得复杂多了

2.Singleton 单例模式

单例设计模式(Singleton Design Pattern):一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

基本原理

在调用Singleton类时构造函数就会检查静态instance是否以及被赋值了,如果被赋值有了

在这里插入图片描述

export class Singleton {
    
    static instance: Singleton
    id: number

    /* */
    constructor(id: number) {
        this.id = id
        if (Singleton.instance) {
            return Singleton.instance
        }
        Singleton.instance = this
    }
}

//所以单例模式创建的所有对象都指向一个原始对象
const OBJECT1 = new Singleton(1) 
const OBJECT2 = new Singleton(2) 
console.log(OBJECT1 === OBJECT2) // true
console.log(OBJECT1.id) // returns 1
console.log(OBJECT2.id) // returns 1 因为对象实例以及被船舰过了,直接返回的是第一创建按的对象实例

应用举例

实现一个关于游戏排行榜的应用,游戏对象是由相应的游戏类创建,而排行榜又分出来作为一个单例类,这样创建多个游戏对象时就可以直接引用同一个单例类Singleton了

在这里插入图片描述

class Leaderboard {
    static instance: Leaderboard
    #table: { [id: number]: string } = {}

    constructor() {
        if (Leaderboard.instance) { //单例
            return Leaderboard.instance
        }
        Leaderboard.instance = this
    }

    public addWinner(position: number, name: string): void {
        this.#table[position] = name
    }

    public print(): void {
        console.log('-----------Leaderboard-----------')
        for (const key in this.#table) {
            console.log(`|\t${key}\t|\t${this.#table[key]}\t|`)
        }
        console.log()
    }
}

interface IGame {
    addWinner(position: number, name: string): void
}

class Game1 implements IGame {
    leaderboard: Leaderboard

    constructor() {
        this.leaderboard = new Leaderboard()
    }

    addWinner(position: number, name: string): void {
        this.leaderboard.addWinner(position, name)
    }
}

class Game2 implements IGame {
    leaderboard: Leaderboard

    constructor() {
        this.leaderboard = new Leaderboard()
    }

    addWinner(position: number, name: string): void {
        this.leaderboard.addWinner(position, name)
    }
}

class Game3 implements IGame {
    leaderboard: Leaderboard

    constructor() {
        this.leaderboard = new Leaderboard()
    }

    addWinner(position: number, name: string): void {
        this.leaderboard.addWinner(position, name)
    }
}



const GAME1 = new Game1()
GAME1.addWinner(2, 'Cosmo')

const GAME2 = new Game2()
GAME2.addWinner(3, 'Sean')

const GAME3 = new Game3()
GAME3.addWinner(1, 'Emmy')

GAME1.leaderboard.print() 
GAME2.leaderboard.print()
GAME3.leaderboard.print()

这样一来GAME类就用创建多个leaderboard而是共同引用一个leaderboard实例

-----------Leaderboard-----------
|       1       |       Emmy    |
|       2       |       Cosmo   |
|       3       |       Sean    |

-----------Leaderboard-----------
|       1       |       Emmy    |
|       2       |       Cosmo   |
|       3       |       Sean    |

-----------Leaderboard-----------
|       1       |       Emmy    |
|       2       |       Cosmo   |
|       3       |       Sean    |
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

seeooco

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值