JavaScript设计模式之单例模式详解
作者:Alo365
什么是单例模式
单,单个;例,实例。顾名思义,单例设计模式就是限制一个类必须只能有一个类,如果第二次创建的时候,我们可以抛出错误或者返回第一次的实例。
我们知道当new一个构造函数时就会得到一个实例对象,new
的执行原理如下:
function Person() { // let obj = { // 1 // name: 'Tom' // 3的效果 // } // Person.call(obj) // 2 让Person构造函数的this指向obj // obj.__proto__ = Person.prototype // 4 //让实例对象的隐式原型等于构造函数的显示原型 // return obj // 5 返回实例对象 this.name = 'Tom' // 3 往obj上添加属性 let p1 = new Person() let p2 = new Person() console.log(p1 === p2);//false }
当使用 new
关键字创建一个对象时,每次都会生成一个新的实例,这意味着每个对象都有自己独立的内存空间和引用地址。因此,即使两个对象是由同一个类或构造函数创建的,它们的引用地址也是不同的。
而单例模式就是要我们不管new多少次,都得到相同的这个对象。
实现方法
一、采用static静态方法
静态方法在类本身上定义,不依赖于类的实例。所以静态方法只能被类访问,不能被实例对象访问
全局访问:静态方法提供了一个类级别的全局访问点,可以在不实例化类的情况下调用。
实例管理:静态方法可以操作静态属性来存储和管理唯一实例,从而确保实例的唯一性。
示例代码
class Person { constructor() { this.name = 'Tom' //初始化name属性 } static getInstance() {//静态方法,用于创建或获取 Person的唯一实例 if (!Person.instance) {//如果不存在,则创建一个新的 实例 Person.instance = new Person() } return Person.instance//将其赋值给Person.instance } } let p1 = Person.getInstance() let p2 = Person.getInstance() console.log(p1 === p2)//true
二、闭包
1. 闭包的基本概念
闭包是一个函数和其相关的词法环境的组合,这个环境包含了该函数的作用域中的所有变量。
在JavaScript中,闭包允许函数访问其外部作用域中的变量,即使函数在外部作用域之外被调用时,也可以保留对这些变量的访问权限。
2. 单例模式的需求
单例模式需要以下特性:
- 唯一实例:保证类在整个应用程序中只有一个实例。
- 全局访问:提供一个全局访问点来获取这个唯一实例。
3. 使用闭包实现单例模式
闭包可以通过封装实例的创建和管理逻辑来实现单例模式。具体来说,闭包可以帮助我们创建一个私有作用域来保存唯一实例,从而控制实例的唯一性。
示例代码
class Person { constructor() { this.name = 'Tom' } static getInstance() { let instance = null return function() { if (!instance) { instance = new Person() } return instance } } } const simple = Person.getInstance() let p1 = simple() let p2 = simple() console.log(p1 === p2);
这段代码中关于闭包的使用:
- 每次调用返回的匿名函数时,闭包会记住
instance
变量的值。 - 如果
instance
为null
,则创建一个新的Person
实例,并将其赋值给instance
变量。 - 如果
instance
已经存在,则直接返回这个实例。 - 闭包创建了一个私有作用域,在这个作用域内管理
instance
变量,使其不会被外部代码直接访问或修改。这确保了只有一个实例能够被创建,并且所有对该实例的访问都通过同一个方法。
应用场景
这里我们用一段 HTML 进行简单演示,展示如何通过单例模式来管理浏览器的 localStorage
数据存取。在某些场景下,我们需要确保对 localStorage
的操作是由同一个实例来执行的,避免重复创建对象,确保数据的一致性。
<body> <button id="save">存储</button> <button id="get">取值</button> <script> let save = document.getElementById('save') let get = document.getElementById('get') // 定义一个Storage类,用单例模式封装LocalStorage操作 class Storage { // 使用静态方法实现单例模式 static getInstance() { if (!Storage.instance) { Storage.instance = new Storage(); } return Storage.instance; } // 获取LocalStorage中的数据 getItem(key) { return localStorage.getItem(key); } // 存储数据到LocalStorage setItem(key, val) { return localStorage.setItem(key, val); } } // 获取唯一的Storage实例 const storage = Storage.getInstance(); const storage2 = Storage.getInstance(); // 点击存储按钮,保存数据到LocalStorage save.onclick = function() { storage.setItem('name', '高老师'); } // 点击取值按钮,弹出LocalStorage中的数据 get.onclick = function() { alert(storage2.getItem('name')); } </script> </body> </html>
代码解读
HTML 结构:
页面中包含两个按钮,分别用于“存储”和“取值”操作。通过id
获取按钮元素,并绑定相应的点击事件。Storage 类的定义:
Storage
类封装了对localStorage
的操作,并通过静态方法getInstance
实现单例模式。这个方法确保Storage
类在整个应用中只有一个实例,这样对localStorage
的访问都是通过同一个对象进行的。按钮事件处理:
- 点击“存储”按钮时,
Storage
实例的setItem
方法将'高老师'
存储在localStorage
中,键名为'name'
。 - 点击“取值”按钮时,
Storage
实例的getItem
方法读取localStorage
中存储的'name'
,并通过弹窗显示出来。
- 点击“存储”按钮时,
结果验证
在这段代码中,无论你点击多少次“存储”或“取值”按钮,所有的操作都会通过同一个 Storage
实例进行。这保证了数据的一致性,并且利用单例模式避免了不必要的对象创建。
应用优势
通过这种封装方式,我们可以更方便地管理和扩展与 localStorage
相关的操作,同时避免了全局状态的混乱。此外,单例模式的使用确保了在整个应用程序中,只会存在一个 Storage
实例,简化了管理,减少了内存消耗。
以上就是JavaScript设计模式之单例模式详解的详细内容,更多关于JavaScript单例模式的资料请关注脚本之家其它相关文章!