We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
单例是什么?
全局变量和单例是一回事嘛?
export一个实例是单例嘛?
通过属性拦截防止实例重复创建就能实现单例嘛?
为什么我们要用单例?
如何实现一个单例?
前端开发需要用到单例嘛?
单例模式是一种创建型设计模式,它保证了只有一个实例,并提供一个全局访问点,供外部访问这个单一实例。
保证
class Single { name = ''; constructor(name) { this.name = name; } } window.SingleTon = new Single('s1')
等号左边是实例的作用域也就是可访问性,这是 静态词法作用域 决定的.
单例描述的是等号右边,需要保证实例对象全局唯一。
这是一个全局对象 它实现了全局可访问性并且可以实现状态共享,但是这并不能保证实例唯一。
const s1 = new Single('s1'); const s2 = new Single('s1'); s1 === s2 // false
// a.js const s1 = new Single('?s1'); export const SingleTon = s1;
现在外部在使用时直接使用实例化后的SingleTon对象,我们在a.js模块并没有暴露Single类,外部无法访问Single也不能new新对象,这样保证了实例唯一,那么这样实现是单例嘛?
这样依然不是... 虽然没有导出Single类,但是我们依然可以通过继承链 创建出新的Single实例。
import { SingleTon } from './a.js' const s2 = new SingleTon.constructor('s1'); // 通过构造函数 再次创建出新的实例 s2 === SingleTon // false
class Single { name = ''; constructor(name) { this.name = name; } static getInstance() { // 判断是否已经 new 过 1 个实例 if (!Single.instance) { // 若这个唯一的实例不存在,那么先创建它 Single.instance = new Single('s1') } // 如果这个唯一的实例已经存在,则直接返回 return Single.instance } } const s1 = Single.getInstance(); const s2 = Single.getInstance(); s1 === s2 // true
getInstance静态方法的作用就是创建Single实例。 在第一次调用getInstance时,它会检查Single类是否已经有一个名为instance的实例,如果没有,那么它会创建该实例。然后每次调用getInstance方法时,都会返回这个已经创建的唯一的实例。
Single.instance = null; const s3 = Single.getInstance(); s1 === s3 // false
因为拦截的标识是在构造函数上的所以并不安全,可以被重新赋值。
有的同学可能会说 使用symbol这样就不会被访问到了, 如下:
const instance = Symbol('instance') static getInstance() { // 判断是否已经 new 过 1 个实例 if (!Single[instance]) { // 若这个唯一的实例不存在,那么先创建它 Single[instance] = new Single('s1') } // 如果这个唯一的实例已经存在,则直接返回 return Single[instance] }
这种方式也是不对的。symbol 只是默认不能访问到 我们可以通过其他方式获取到。
const symbolSingle = Object.getOwnPropertySymbols(Single)[0]; Single[symbolSingle] = null; const s2 = Single.getInstance(); s1 === s2 // false
无论别人如何使用,都要保证实例唯一, 想要实现真正的单例模式,我们首先需要知道js创建对象的本质是构造函数调用,所以我们需要拦截构造函数,且拦截的标识不能被外部更改。
保证实例唯一
const SingleTon = (function(){ let single; function SingleObj (name){ if(!single){ single = this; } this.name = name; return single; } return SingleObj })(); const s1 = new SingleTon('s1'); const s2 = new SingleTon('s2'); const s3 = new s1.constructor('s3') s1 === s2 // true s1 === s3 // true
// A.js let instance; export class SingleTon { name = ''; constructor(name) { if (!instance) { instance = this; } this.name = name; return instance; } } const s1 = new SingleTon('s1'); const s2 = new SingleTon('s2'); const s3 = new s1.constructor('s3') s1 === s2 // true s1 === s3 // true
在构造函数constructor中,如果instance不存在,那么就将当前实例 this 赋值给 instance,否则直接返回已经存在的 instance。这样保证了一个类只有一个实例。
通过模块化防止instance被外部访问到 可以防止标识被清空。
使用 JavaScript 的 Proxy 来实现一个单例模式
function getSingleTon(className) { let instance; const p = new Proxy(className, { construct(target, arg) { if (!instance) { instance = Reflect.construct(target, arg); } return instance; }, }); p.prototype.constructor = p; return p; } const SingleTon = getSingleTon(Single); const s1 = new SingleTon(); const s2 = new SingleTon(); const s3 = new s1.constructor() s1 === s2 // true s1 === s2 // true
在处理程序对象中,定义了一个 construct 属性,它是一个函数,当代理对象被构造时调用。Reflect.construct 用于构造一个新对象,其行为类似于 new 操作符。
construct
Reflect.construct
new
在 construct 函数中,首先检查 instance 是否已经存在。如果不存在,使用 Reflect.construct 构造一个新的实例,并将其存储在 instance 中。如果 instance 已经存在,就直接返回这个实例。
instance
将代理对象的 prototype.constructor 属性设置为代理对象本身,这样通过 constructor 属性访问的构造函数也会指向代理对象。
prototype.constructor
constructor
对于纯前端的项目 一般基于react/vue这些框架做开发 前端面临的主要功能是UI绘制 响应交互 与服务端,客户端通信等逻辑。一般通过es 模块化是可以达到和单例相同作用的。所以除非是框架设计或者是公共库开发,实际业务中并不常用...
The text was updated successfully, but these errors were encountered:
No branches or pull requests
先有问题再有答案
单例是什么?
全局变量和单例是一回事嘛?
export一个实例是单例嘛?
通过属性拦截防止实例重复创建就能实现单例嘛?
为什么我们要用单例?
如何实现一个单例?
前端开发需要用到单例嘛?
什么是单例
单例模式是一种创建型设计模式,它
保证
了只有一个实例,并提供一个全局访问点,供外部访问这个单一实例。全局对象是单例嘛?
等号左边是实例的作用域也就是可访问性,这是 静态词法作用域 决定的.
单例描述的是等号右边,需要保证实例对象全局唯一。
这是一个全局对象 它实现了全局可访问性并且可以实现状态共享,但是这并不能保证实例唯一。
export一个实例是单例嘛?
现在外部在使用时直接使用实例化后的SingleTon对象,我们在a.js模块并没有暴露Single类,外部无法访问Single也不能new新对象,这样保证了实例唯一,那么这样实现是单例嘛?
这样依然不是...
虽然没有导出Single类,但是我们依然可以通过继承链 创建出新的Single实例。
静态属性创建拦截
getInstance静态方法的作用就是创建Single实例。
在第一次调用getInstance时,它会检查Single类是否已经有一个名为instance的实例,如果没有,那么它会创建该实例。然后每次调用getInstance方法时,都会返回这个已经创建的唯一的实例。
因为拦截的标识是在构造函数上的所以并不安全,可以被重新赋值。
symbol属性拦截
有的同学可能会说 使用symbol这样就不会被访问到了, 如下:
这种方式也是不对的。symbol 只是默认不能访问到 我们可以通过其他方式获取到。
单例应用场景
实现单例的方式
无论别人如何使用,都要
保证实例唯一
, 想要实现真正的单例模式,我们首先需要知道js创建对象的本质是构造函数调用,所以我们需要拦截构造函数,且拦截的标识不能被外部更改。函数式
OOP
在构造函数constructor中,如果instance不存在,那么就将当前实例 this 赋值给 instance,否则直接返回已经存在的 instance。这样保证了一个类只有一个实例。
通过模块化防止instance被外部访问到 可以防止标识被清空。
代理方式
使用 JavaScript 的 Proxy 来实现一个单例模式
在处理程序对象中,定义了一个
construct
属性,它是一个函数,当代理对象被构造时调用。Reflect.construct
用于构造一个新对象,其行为类似于new
操作符。在
construct
函数中,首先检查instance
是否已经存在。如果不存在,使用Reflect.construct
构造一个新的实例,并将其存储在instance
中。如果instance
已经存在,就直接返回这个实例。将代理对象的
prototype.constructor
属性设置为代理对象本身,这样通过constructor
属性访问的构造函数也会指向代理对象。实际业务中的应用
对于纯前端的项目 一般基于react/vue这些框架做开发 前端面临的主要功能是UI绘制 响应交互 与服务端,客户端通信等逻辑。一般通过es 模块化是可以达到和单例相同作用的。所以除非是框架设计或者是公共库开发,实际业务中并不常用...
The text was updated successfully, but these errors were encountered: