#81 单例模式


  • 0
    administrators

    单例模式(Singleton)是一种常用的软件设计模式,它保证我们系统中的某一个类在任何情况实例化的时候都获得同一个实例。例如:

    const root1 = new Root()
    const root2 = new Root()
    const root3 = new Root()
    
    root1 === root2 // true
    root2 === root3 // true
    

    我们构造一个名为 singletonify 方法,可以传入一个用户自定义的类,可以返回一个新的单例模式的类。例如:

    class A () {}
    const SingleA = singletonify(A)
    
    const a1 = new SingleA()
    const a2 = new SingleA()
    const a3 = new SingleA()
    
    a1 === a2 // => true
    a2 === a3 // => true
    

    注意,你要保证 singletonify 返回的类的实例也是原来的类的实例:

    a1 instanceof A // => true
    a1 instanceof SingleA // => true
    

    自定义的类属性也要保持一致,例如:

    class A () {}
    A.staticMethod = () => {}
    
    const SingleA = singletonify(A)
    SingleA.staticMethod === A.staticMethod // => true
    

    请你完成 singletonify 的编写。


  • 1

    proxy秒杀系列 2333

    const singletonify = fn => {
      const one = new fn()
      return new Proxy(fn, {
        construct(target, argumentsList, newTarget) {
          return one
        }
      })
    }
    

  • 1
    administrators

    @CodeHz 这是标准答案


  • 0
    管理员

    @ScriptOJ 这题不用Proxy的话,有其他解法吗?似乎除了将SingleA代理到A,想不到其他办法能实现 SingleA.name === A.name


  • 0
    administrators

    @陈小俊 这题考的就是 Proxy。但是可以更灵活一些,可以把 name 限制去掉。让解法更多一些


  • 0
    administrators

    @陈小俊 去除了限制,可以尝试一些其他方法。


  • 1
    管理员

    @ScriptOJ 嗯,感觉解法多一点挺好,可以有个对比。


  • 0
    管理员

    const singletonify = OriginalClass => {
      const one = new OriginalClass();
      class NewClass extends OriginalClass {
        constructor() {
          super();
          return one;
        }
      }
      // 将 one 的原型链指向 NewClass 的原型对象
      one.__proto__ = NewClass.prototype;
      return NewClass;
    }
    

  • 0
    administrators

    @陈小俊 __proto__ 不属于 ES 标准属性,尽量少用,换了环境可能就用不了了。这里可以用 Object.setPrototypeOf


  • 0
    管理员

    @ScriptOJ 嗯,更新下

    const singletonify = OriginalClass => {
      const one = new OriginalClass()
      class NewClass extends OriginalClass {
        constructor() {
          super();
          return one;
        }
      }
      // 将 one 的原型链指向 NewClass 的原型对象
      Object.setPrototypeOf(one, NewClass.prototype);
      return NewClass;
    }
    

  • 0

    const singletonify = (OriginalClass) => {
      let inst;
      return new Proxy(OriginalClass, {
        construct: function(target) {
          return inst || (inst = new target)
        }
      })
    }
    

    一个你用了还想用的前端表单验证工具 https://github.com/WLDragon/SMValidator


  • 0

    @CodeHz#81 单例模式 中说:

    proxy秒杀系列 2333

    const singletonify = fn => {
      const one = new fn()
      return new Proxy(fn, {
        construct(target, argumentsList, newTarget) {
          return one
        }
      })
    }
    

    这种写法是每次执行singletonify方法都会产生一个新的单例,而我的那种写法是一个类对应永远只有一个单例,感觉还是前者比较灵活


  • 0

    @WLDragon @CodeHz 你们多次执行singletonify的话都会多次创建新实例,看我的,哈

    const singletonify = (OriginalClass) => new Proxy(OriginalClass, {
      construct(target, args) {
        if (!target.__singleInstance__) {
          target.__singleInstance__ = new target(...args)
        }
        return target.__singleInstance__
      }
    })
    

  • 0

    @终天霸主 我提交的代码多次执行不会创建新的实例,但是我觉得应该要创建新的实例才比较灵活。singletonify就相当一个工厂,产生单例。

    而且我觉得这道题只是考察Proxy而已,实际上单例不应该这样用的,单例应该用静态属性来引用,避免new关键字的歧义。例如这样XClass.instance而不是let xx = new XClass()


  • 0

    互相学习啦~

    const singletonify = (OriginalClass) => {
      function OriginalClassInherit(){
        if(!OriginalClassInherit.prototype.single){
          OriginalClassInherit.prototype.single = new OriginalClass()
          return OriginalClassInherit.prototype.single;
        }else{
          return OriginalClassInherit.prototype.single
        }
      }
      Object.setPrototypeOf(OriginalClassInherit, OriginalClass);
      OriginalClassInherit.prototype = new OriginalClass();
      return OriginalClassInherit
    }
    

登录后回复
 

与 ScriptOJ 的连接断开,我们正在尝试重连,请耐心等待