Подтвердить что ты не робот

Javascript singleton question

Я просто прочитал несколько потоков в обсуждении дизайна singleton в javascript. Я на 100% новичок в материале Design Pattern, но, как я вижу, поскольку Singleton по определению не будет нуждаться в создании экземпляра, концептуально, если он не будет создан, по-моему, его не нужно рассматривать как обычные объекты, созданные из плана (классов). Поэтому я удивляюсь, почему бы не просто подумать об одном сингле, как о том, что статически доступно, которое обернуто в какой-то сфере, и это должно быть все.

Из потоков, которые я видел, большинство из них делают одноэлемент, хотя традиционный javascript

new function(){} 

за которым следует создание псевдоконструктора.

Ну, я просто думаю, что литерал объекта достаточно:

var singleton = {
   dothis: function(){},
   dothat: function(){}
}

правильно? Или кто-нибудь получил лучшее понимание?

[update]: Опять же моя точка зрения заключается в том, почему люди не просто используют более простой способ сделать синглтоны в javascript, как я показал во втором фрагменте, если есть абсолютная причина, пожалуйста, скажите мне. Обычно я боюсь такой ситуации, что я упрощаю многое: D

4b9b3361

Ответ 1

Я согласен с вами, самый простой способ - использовать литерал объекта, но если вы хотите частных членов, вы можете реализовать использование closures:

var myInstance = (function() {
  var privateVar;

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // private members can be accessed here
    },
    publicMethod2: function () {
      // ...
    }
  };
})();

О конструкции new function(){} будет просто использовать анонимную функцию как constructor function, контекст внутри этой функции будет новый объект, который будет возвращен.

Изменить: В ответ на комментарий @J5 это просто сделать, на самом деле я думаю, что это может быть хорошим примером для использования шаблона Lazy Function:

function singleton() {
  var instance = (function() {
    var privateVar;

    function privateMethod () {
      // ...
    }

    return { // public interface
      publicMethod1: function () {
          // private members can be accessed here
       },
      publicMethod2: function () {
        // ...
      }
    };
  })();

  singleton = function () { // re-define the function for subsequent calls
    return instance;
  };

  return singleton(); // call the new function
}

Когда функция вызывается в первый раз, я делаю экземпляр объекта и переназначаю singleton на новую функцию, у которой этот экземпляр объекта закрыт.

Перед окончанием первого вызова я выполняю переопределенную функцию singleton, которая вернет созданный экземпляр.

После вызова функции singleton будет просто возвращен instance, который хранится в нем, потому что новая функция будет выполнена.

Вы можете доказать, что сравнивая возвращаемый объект:

singleton() == singleton(); // true

Оператор == для объектов возвращает true только в том случае, если ссылка на объект обоих операндов одинакова, она вернет false, даже если объекты идентичны, но они представляют собой два разных экземпляра:

({}) == ({}); // false
new Object() == new Object(); // false

Ответ 2

Я использовал вторую версию (var singleton = {};) для всего от расширений Firefox до веб-сайтов, и она работает очень хорошо. Одна хорошая идея - не определять вещи внутри фигурных скобок, но вне нее, используя имя объекта, например:

var singleton = {};
singleton.dothis = function(){

};
singleton.someVariable = 5;

Ответ 3

Спецификация ES5 позволяет использовать Object.create():

var SingletonClass = (function() {
    var instance;
    function SingletonClass() {
        if (instance == null) {
            instance = Object.create(SingletonClass.prototype);
        }

        return instance;
    }

    return {
        getInstance: function() {
            return new SingletonClass();
        }
   };
})();

var x = SingletonClass.getInstance();
var y = SingletonClass.getInstance();
var z = new x.constructor();

Это хорошо, так как нам не нужно беспокоиться о утечке нашего конструктора, мы все равно всегда получаем один и тот же экземпляр.

Эта структура также имеет то преимущество, что наш Singleton не создает себя, пока он не понадобится. Кроме того, использование замыкания, как мы здесь делаем, не позволяет внешнему коду использовать нашу "экземплярную" переменную, случайно или иначе. Мы можем создавать более частные переменные в одном и том же месте, и мы можем определить все, что мы хотим экспортировать публично на прототипе нашего класса.

Ответ 4

Я тоже задавался вопросом об этом, но просто определение объекта с функциями в нем кажется мне разумным. Нет смысла создавать конструктор, который никто никогда не должен был вызывать, для создания объекта без прототипа, когда вы можете просто определить объект напрямую.

С другой стороны, если вы хотите, чтобы ваш синглтон был экземпляром какого-либо существующего "класса", то есть вы хотите, чтобы в качестве его прототипа был какой-то другой объект, тогда вам нужно использовать функцию-конструктор, так что вы можете установить его свойство prototype, прежде чем вызывать его.

Ответ 5

Синтаксический шаблон реализуется путем создания класса с помощью метода, который создает новый экземпляр класса, если он не существует. Если экземпляр уже существует, он просто возвращает ссылку на этот объект. 1

(function (global) {

     var singleton;

     function Singleton () {
         // singleton does have a constructor that should only be used once    
         this.foo = "bar";
         delete Singleton; // disappear the constructor if you want
     }

     global.singleton = function () {
         return singleton || (singleton = new Singleton());
     };

})(window);

var s = singleton();
console.log(s.foo);

var y = singleton();
y.foo = "foo";
console.log(s.foo);

Вы не просто объявляете singleton как объект, потому что он его запускает, он не объявляет его. Он также не обеспечивает механизм для кода, который не знает о предыдущей ссылке на синглтон, чтобы получить его. Синглтон не является объектом/классом, который возвращается одиночным тоном, это структура. Это похоже на то, как закрытые переменные не являются закрытием, область функций, обеспечивающая закрытие, является закрытием.

Ответ 6

В последнем поле кода показано, что я видел, как разработчики JS называют их версию OO-дизайна в Javascript.

Singetons предназначены для особых объектов, которые нельзя построить (за исключением, я полагаю, в исходном определении. У вас есть один глобальный экземпляр одноэлементного.

Ответ 7

Точка использования "псевдоконструктора" заключается в том, что она создает новую область переменных. Вы можете объявить локальные переменные внутри функции, доступные внутри любых вложенных функций, но не из глобальной области.

Есть два способа сделать это. Вы можете вызвать функцию с помощью new, как в вашем примере, или просто вызвать функцию напрямую. Есть небольшие различия в том, как вы будете писать код, но они по сути эквивалентны.

Ваш второй пример можно написать следующим образом:

var singleton = new function () {
    var privateVariable = 42; // This can be accessed by dothis and dothat

    this.dothis = function () {
        return privateVariable;
    };

    this.dothat = function () {};
}; // Parentheses are allowed, but not necessary unless you are passing parameters

или

var singleton = (function () {
    var privateVariable = 42; // This can be accessed by dothis and dothat

    return {
        dothis: function () {
            return privateVariable;
        },

        dothat: function () {}
    };
})(); // Parentheses are required here since we are calling the function

Вы также можете передать аргументы любой функции (вам нужно будет добавить круглые скобки в первый пример).

Ответ 9

Как насчет этого:

function Singleton() {

    // ---------------
    // Singleton part.
    // ---------------

    var _className = null;
    var _globalScope = null;

    if ( !(this instanceof arguments.callee) ) {
        throw new Error("Constructor called as a function.");
    }

    if ( !(_className = arguments.callee.name) ) {
        throw new Error("Unable to determine class name.")
    }

    _globalScope = (function(){return this;}).call(null);

    if ( !_globalScope.singletons ) {
        _globalScope.singletons = [];
    }

    if ( _globalScope.singletons[_className] ) {
        return _globalScope.singletons[_className];
    } else {
        _globalScope.singletons[_className] = this;
    }

    // ------------
    // Normal part.
    // ------------

    var _x = null;

    this.setx = function(val) {
        _x = val;
    }; // setx()

    this.getx = function() {
        return _x;
    }; // getx()

    function _init() {
        _x = 0; // Whatever initialisation here.
    } // _init()
    _init();

} // Singleton()

    var p = new Singleton;
    var q = new Singleton;

    p.setx(15);
    q.getx(); // returns 15

Ответ 10

Я украл это из CMS/ответа CMS и изменил его, чтобы он мог вызывается как:

MySingleton.getInstance().publicMethod1();

С небольшим чередованием:

var MySingleton = {                // These two lines

    getInstance: function() {      // These two lines

      var instance = (function() {

        var privateVar;

        function privateMethod () {
          // ...
          console.log( "b" );
        }

        return { // public interface
          publicMethod1: function () {
              // private members can be accessed here
              console.log( "a" );
           },
          publicMethod2: function () {
            // ...
            privateMethod();
          }
        };
      })();

      singleton = function () { // re-define the function for subsequent calls
        return instance;
      };

      return singleton(); // call the new function
    }
}