Найпростіший / найпростіший спосіб реалізації синглтона в JavaScript?


300

Який найпростіший / найпростіший спосіб реалізувати однотонний візерунок у JavaScript?


15
Оголошення щодо прийнятої відповіді взагалі не є одинаком. Це просто глобальна змінна.
mlibby

5
Це багато інформації, але дійсно викладає відмінності між різними моделями дизайну JS. Це мені дуже допомогло: addyosmani.com/resources/essentialjsdesignpatterns/book
Джастін

Відповіді:


315

Я думаю, що найпростішим способом є оголошення простого об'єкта буквальним:

var myInstance = {
  method1: function () {
    // ...
  },
  method2: function () {
    // ...
  }
};

Якщо ви хочете приватних членів у вашому одиночному екземплярі, ви можете зробити щось подібне:

var myInstance = (function() {
  var privateVar = '';

  function privateMethod () {
    // ...
  }

  return { // public interface
    publicMethod1: function () {
      // all private members are accessible here
    },
    publicMethod2: function () {
    }
  };
})();

Це було названо в шаблон модуля , це в принципі дозволяє инкапсулировать приватних користувачів на об'єкті, скориставшись використанням затворів .

ОНОВЛЕННЯ: Я хотів би додати, що якщо ви хочете запобігти модифікації одиночного об'єкта, ви можете заморозити його , використовуючи ES5Object.freeze метод .

Це зробить об'єкт непорушним, запобігаючи будь-яким змінам його структури та значень.

Додатково хотілося б зазначити, що якщо ви використовуєте ES6, ви можете легко представляти сингтон за допомогою модулів ES , і ви навіть можете утримувати приватний стан , оголошуючи змінні в області модуля :

// my-singleton.js
const somePrivateState = []

function privateFn () {
  // ...
}

export default {
  method1() {
    // ...
  },
  method2() {
    // ...
  }
}

Тоді ви можете просто імпортувати одиночний об'єкт для його використання:

import myInstance from './my-singleton.js'
// ...

46
+1 Хіба не дивно шукати "шаблон Singleton" мовою із глобальними змінними ???
Віктор

4
Як користуватися модульним шаблоном, яким чином публічний член має доступ до іншого публічного члена? Тобто, як би publicMethod1зателефонував publicMethod2?
typeof

4
@ Так, так, модель народилася на мовах OOP на основі класу - я пам’ятаю багато реалізацій, які включали статичний getInstanceметод і приватний конструктор, але IMO, це самий «простий» спосіб побудови одиночного об’єкта в Javascript, і в кінці він відповідає тій самій цілі - єдиному об'єкту, який ви не можете ініціалізувати знову (конструктора немає, це просто об’єкт) -. Щодо коду, який ви зв'язали, він має деякі проблеми, поміняйте оголошеннями aта bзмінними декларацій та тестуйтеa === window . Ура.
CMS

15
@Victor - Це не дивно, щоб шукати "однотонний візерунок" такою мовою. багато мов, орієнтованих на об'єкти, використовують глобальні змінні, і все ще використовуються одинаки. Singleton - це не тільки гарантія того, що буде лише один об'єкт даного класу. У Singleton є ще кілька функцій: 1) його слід ініціалізувати при першому використанні (що означає не тільки затримку ініціалізації, але й гарантує, що об’єкт дійсно готовий до використання) 2) він повинен бути безпечним для потоків. Шаблон модуля може бути заміною однотонного шаблону, але тільки в браузері (і не завжди).
скеле

55
Це не має бути прийнятою відповіддю. Це зовсім не сингл! Це просто глобальна змінна. Між ними є світ різниці.
млібі

172

Я думаю, що найчистіший підхід - це щось на кшталт:

var SingletonFactory = (function(){
    function SingletonClass() {
        //do stuff
    }
    var instance;
    return {
        getInstance: function(){
            if (instance == null) {
                instance = new SingletonClass();
                // Hide the constructor so the returned object can't be new'd...
                instance.constructor = null;
            }
            return instance;
        }
   };
})();

Після цього ви можете викликати функцію як

var test = SingletonFactory.getInstance();

4
Зауваження: Оригінальний конструктор можна прочитати ще раз, використовуючи delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W

var test = SingletonClass.getInstance () - виглядає не дуже чистим або схожим на JS. Інші душі, які закінчуються a = новим Foo (); b = новий Foo (); a === b // вірно
Маттіас

3
Пахне мені як фабрика цілою частиною "getInstance".
Лайош Мецарос

1
Це не синглтон, тому що ви можете створити кілька примірників його за допомогою Object.create.
AndroidDev


103

Я не впевнений, що я згоден з тим, що модель модуля використовується як заміна однотонної схеми. Я часто бачив одинаків, які використовуються та зловживають в місцях, де вони зовсім непотрібні, і я впевнений, що модель модуля заповнює багато прогалин, коли програмісти інакше використовуватимуть одинарний, однак шаблон модуля не є сингтон.

модель модуля:

var foo = (function () {
    "use strict";
    function aPrivateFunction() {}
    return { aPublicFunction: function () {...}, ... };
}());

Все, ініціалізоване в шаблоні модуля, відбувається при Fooоголошенні. Крім того, модель модуля може бути використана для ініціалізації конструктора, який потім може бути ініційований кілька разів. Хоча модель модуля є правильним інструментом для багатьох завдань, він не еквівалентний одиночному.

однотонний візерунок:

коротка форма
var Foo = function () {
    "use strict";
    if (Foo._instance) {
        //this allows the constructor to be called multiple times
        //and refer to the same instance. Another option is to
        //throw an error.
        return Foo._instance;
    }
    Foo._instance = this;
    //Foo initialization code
};
Foo.getInstance = function () {
    "use strict";
    return Foo._instance || new Foo();
}
довга форма, використовуючи шаблон модуля
var Foo = (function () {
    "use strict";
    var instance; //prevent modification of "instance" variable
    function Singleton() {
        if (instance) {
            return instance;
        }
        instance = this;
        //Singleton initialization code
    }
    //instance accessor
    Singleton.getInstance = function () {
        return instance || new Singleton();
    }
    return Singleton;
}());

В обох версіях схеми Singleton, яку я надав, сам конструктор може використовуватися як аксесуар:

var a,
    b;
a = new Foo(); //constructor initialization happens here
b = new Foo();
console.log(a === b); //true

Якщо ви не відчуваєте себе комфортно таким чином, використовуючи конструктор, ви можете викинути помилку у if (instance)виписці і дотримуватися довгої форми:

var a,
    b;
a = Foo.getInstance(); //constructor initialization happens here
b = Foo.getInstance();
console.log(a === b); //true

Я також повинен зазначити, що одинарний візерунок добре поєднується з неявною схемою функції конструктора:

function Foo() {
    if (Foo._instance) {
        return Foo._instance;
    }
    //if the function wasn't called as a constructor,
    //call it as a constructor and return the result
    if (!(this instanceof Foo)) {
        return new Foo();
    }
    Foo._instance = this;
}
var f = new Foo(); //calls Foo as a constructor
-or-
var f = Foo(); //also calls Foo as a constructor

4
Я ніколи нічого не говорив про те, що одинаки є поганою чи хорошою ідеєю. Я сказав, що реалізація синглтона набагато складніша, ніж це має бути, оскільки ви плутаєте обмеження Java з шаблоном так, як ніби ви цього зовсім не розумієте. Це як реалізація структури стратегії шляхом створення конструкторських функцій та методів, коли ви можете просто використовувати анонімні функції для цього в Javascript.
Есаїлія

11
@Esailija, мені здається, що ти не розумієш однотонну схему. Однотонний візерунок - це модель дизайну, яка обмежує Моментальність класу одним об'єктом. var singleton = {}не відповідає цьому визначенню.
zzzzBov

9
Це стосується лише таких мов, як Java через їх обмеження. var singleton = {}це те, як ти реалізуєш синглтон у Javascript .
Есаїлія

2
@Esailija, "У мові програмування на основі прототипу, де використовуються об'єкти, але не класи ..." У JavaScript є поняття класів, так що це не застосовується.
zzzzBov

3
До речі. Singleton - це анти-шаблон, і ніколи його не слід використовувати в керованому середовищі з пісочницею, як JavaScript. misko.hevery.com/2008/08/17/singletons-are-pathological-liars - youtube.com/watch?v=G32acYVd9LY blogs.msdn.com/b/scottdensmore/archive/2004/05/25/140827.aspx - jalf.dk/blog/2010/03/… - kore-nordmann.de/blog/0103_static_considered_harmful.html - phparch.com/2010/03/static-methods-vs-singletons-choose-neither
Бенджамін

18

В es6:

class Singleton {
  constructor () {
    if (!Singleton.instance) {
      Singleton.instance = this
    }
    // Initialize object
    return Singleton.instance
  }
  // Properties & Methods
}

const instance = new Singleton()
Object.freeze(instance)

export default instance

1
Заморожування мало б сенс, якщо Сінглтон буде обгорткою навколо якогось іншого класу і матиме лише instanceполе. Оскільки в даний час ( instanceвстановлено this), у цього класу можуть бути й інші поля, а заморожування не має сенсу imo.
thisismydesign

10

Наступні роботи у вузлі v6

class Foo {
  constructor(msg) {

    if (Foo.singleton) {
      return Foo.singleton;
    }

    this.msg = msg;
    Foo.singleton = this;
    return Foo.singleton;
  }
}

Ми тестуємо:

const f = new Foo('blah');
const d = new Foo('nope');
console.log(f); // => Foo { msg: 'blah' }
console.log(d); // => Foo { msg: 'blah' }

8

В ES6 правильний спосіб зробити це:

class MyClass {
  constructor() {
    if (MyClass._instance) {
      throw new Error("Singleton classes can't be instantiated more than once.")
    }
    MyClass._instance = this;

    // ... your rest of the constructor code goes after this
  }
}

var instanceOne = new MyClass() // Executes succesfully 
var instanceTwo = new MyClass() // Throws error

Або, якщо ви не хочете, щоб помилка була видана при створенні другої інстанції, ви можете просто повернути останній екземпляр, наприклад:

class MyClass {
  constructor() {
    if (MyClass._instance) {
      return MyClass._instance
    }
    MyClass._instance = this;

    // ... your rest of the constructor code goes after this
  }
}

var instanceOne = new MyClass()
var instanceTwo = new MyClass()

console.log(instanceOne === instanceTwo) // logs "true"


Привіт, ви можете допомогти мені дізнатись про різницю між _instance та екземпляром, оскільки я використовував екземпляр і код не працював
Abhinav bhardwaj

Немає технічної різниці в instanceі _instance. Це лише умова іменування в мовах програмування, які ми називаємо приватними змінними, попередньо підкресленими. Я підозрюю, що ваш код не працює в тому, що ви використовуєте this.instanceзамістьMyClass.instance
UtkarshPramodGupta

7

Існує кілька способів зняти шкіру з кішкою :) Залежно від вашого смаку або конкретної потреби можна застосувати будь-яке із запропонованих рішень. Я особисто звертаюся за першим рішенням CMS, коли це можливо (коли вам не потрібна конфіденційність). Оскільки питання стосувалося найпростішого та найчистішого, то це переможець. Або навіть:

var myInstance = {}; // done!

Це (цитата з мого блогу) ...

var SingletonClass = new function() { 
    this.myFunction() { 
        //do stuff 
    } 
    this.instance = 1; 
}

не має великого сенсу (мій приклад блогу теж не має), тому що він не потребує приватних варіантів, тому він майже такий же, як:

var SingletonClass = { 
    myFunction: function () { 
        //do stuff 
    },
    instance: 1 
}

Фрагмент коду 2 містить синтаксичну помилку. Ви не можете писатиthis.f(){}
xoxox

7

Я зневажую свою відповідь, дивіться іншу .

Зазвичай шаблон модуля (див. Відповідь CMS), який НЕ однотонний шаблон досить хороший. Однак однією з особливостей синглтона є те, що його ініціалізація затримується до появи об'єкта. Модель модуля не має цієї функції.

Моя пропозиція (CoffeeScript):

window.singleton = (initializer) ->
  instance = undefined
  () ->
    return instance unless instance is undefined
    instance = initializer()

Що складено до цього в JavaScript:

window.singleton = function(initializer) {
    var instance;
    instance = void 0;
    return function() {
        if (instance !== void 0) {
            return instance;
        }
        return instance = initializer();
    };
};

Тоді я можу зробити наступне:

window.iAmSingleton = singleton(function() {
    /* This function should create and initialize singleton. */
    alert("creating");
    return {property1: 'value1', property2: 'value2'};
});


alert(window.iAmSingleton().property2); // "creating" will pop up; then "value2" will pop up
alert(window.iAmSingleton().property2); // "value2" will pop up but "creating" will not
window.iAmSingleton().property2 = 'new value';
alert(window.iAmSingleton().property2); // "new value" will pop up

Навіщо ви завантажуєте модуль, якщо він не потрібен? І коли вам потрібно завантажити модуль, ви завантажуєте модуль, і він ініціалізується.
Есаїлія

6

Коротка відповідь:

Оскільки JavaScript не блокує характер, Singletons у JavaScript справді некрасиві. Глобальні змінні дадуть вам і один екземпляр через всю програму, без усіх цих зворотних викликів, модель модуля акуратно ховає внутрішні інтерфейси за інтерфейсом. Дивіться відповідь @CMS.

Але, оскільки ви хотіли одиноку ...

var singleton = function(initializer) {

  var state = 'initial';
  var instance;
  var queue = [];

  var instanceReady = function(createdInstance) {
    state = 'ready';
    instance = createdInstance;
    while (callback = queue.shift()) {
      callback(instance);
    }
  };

  return function(callback) {
    if (state === 'initial') {
      state = 'waiting';
      queue.push(callback);
      initializer(instanceReady);
    } else if (state === 'waiting') {
      queue.push(callback);
    } else {
      callback(instance);
    }
  };

};

Використання:

var singletonInitializer = function(instanceReady) {
  var preparedObject = {property: 'value'};
  // calling instanceReady notifies singleton that instance is ready to use
  instanceReady(preparedObject);
}
var s = singleton(singletonInitializer);

// get instance and use it
s(function(instance) {
  instance.doSomething();
});

Пояснення:

Singletons дає вам більше, ніж один екземпляр у всій програмі: їх ініціалізація затримується до першого використання. Це дійсно велика річ, коли ви маєте справу з об'єктами, ініціалізація яких дорога. Дороге зазвичай означає введення / виведення, а в JavaScript введення / виведення завжди означає зворотний зв'язок.

Не довіряйте відповідям, які дають вам інтерфейс, як instance = singleton.getInstance()вони всі пропускають суть.

Якщо вони не приймуть зворотний виклик, щоб його запустили, коли екземпляр готовий, вони не працюватимуть, коли ініціалізатор зробить I / O.

Так, зворотні виклики завжди виглядають потворніше, ніж виклик функції, який негайно повертає екземпляр об'єкта. Але знову ж таки: коли ви робите I / O, зворотні дзвінки обов'язкові. Якщо ви не хочете робити жодного вводу-виводу, то інстанція досить дешева, щоб зробити це при запуску програми.

Приклад 1, дешевий ініціалізатор:

var simpleInitializer = function(instanceReady) {
  console.log("Initializer started");
  instanceReady({property: "initial value"});
}

var simple = singleton(simpleInitializer);

console.log("Tests started. Singleton instance should not be initalized yet.");

simple(function(inst) {
  console.log("Access 1");
  console.log("Current property value: " + inst.property);
  console.log("Let's reassign this property");
  inst.property = "new value";
});
simple(function(inst) {
  console.log("Access 2");
  console.log("Current property value: " + inst.property);
});

Приклад 2: Ініціалізація з вводу / виводу:

У цьому прикладі setTimeoutпідроблені деякі дорогі операції вводу / виводу. Це ілюструє, чому одиночним кнопкам у JavaScript справді потрібні зворотні дзвінки.

var heavyInitializer = function(instanceReady) {
  console.log("Initializer started");
  var onTimeout = function() {
    console.log("Initializer did his heavy work");
    instanceReady({property: "initial value"});
  };
  setTimeout(onTimeout, 500);
};

var heavy = singleton(heavyInitializer);

console.log("In this example we will be trying");
console.log("to access singleton twice before it finishes initialization.");

heavy(function(inst) {
  console.log("Access 1");
  console.log("Current property value: " + inst.property);
  console.log("Let's reassign this property");
  inst.property = "new value";
});

heavy(function(inst) {
  console.log("Access 2. You can see callbacks order is preserved.");
  console.log("Current property value: " + inst.property);
});

console.log("We made it to the end of the file. Instance is not ready yet.");

Завдяки випробуванням і негараздам ​​з іншими відповідями на одиночку, які не перерізали її, я приземлився на отриманий код, надзвичайно подібний до цього.
mheyman

З тих чи інших причин це єдина відповідь, яка має для мене сенс. Інші відповіді всі нагадують мені епізод гон-шоу, коли троє чоловіків намагаються піднятися на стіну чотирьох людей у ​​висоту, піднімаючись на плечі один одного рекурсивно.
Тім Огілві

Укладання зворотного зв'язку - це те, що мені дуже потрібно! Дякую!!
Тім Огілві

Такий підхід насправді ніколи не дає вам одинакову як: singleton (singletonInitializer)! == singleton (singletonInitializer) це два різні екземпляри. Отримана вами функція, яку ви повернулися, може використовуватися для приєднання більше екземплярів викликів до екземпляра, але не вказує строго, що можна створити лише один екземпляр цього типу. Яка вся суть одинака.
Оуен

6

Я отримав цей приклад з JavaScript Patterns Build Better Applications з шаблонами кодування та дизайну Книгою Стояна Стефанова у випадку, якщо вам потрібен простий клас реалізації, наприклад об’єкт singltone, ви можете скористатись негайною функцією, як описано нижче:

var ClassName;

(function() {
    var instance;
    ClassName = function ClassName() {
        //If private instance variable already initialized return reference
        if(instance) {
            return instance;   
        }
        //If instance does not created save pointer of original reference
        //to private instance variable. 
        instance = this;

        //All constructor initialization will be here
        // i.e.: 
        this.someProperty = 0;
        this.someMethod = function() {
            //Some action here
        };
    };
}());

І ви можете перевірити цей приклад, виконавши такий тестовий випадок:

//Extending defined class like Singltone object using new prototype property
ClassName.prototype.nothing = true;
var obj_1 = new ClassName();
//Extending defined class like Singltone object using new prototype property
ClassName.prototype.everything = true; 
var obj_2 = new ClassName();

//Testing does this two object pointing to same instance
console.log(obj_1 === obj_2); //Result is true, it points to same instance object

//All prototype properites work
//no matter when they were defined
console.log(obj_1.nothing && obj_1.everything 
            && obj_2.nothing && obj_2.everything); //Result true


//Values of properties which is defined inside of constructor
console.log(obj_1.someProperty);// output 0
console.log(obj_2.someProperty);// output 0 
//Changing property value 
obj_1.someProperty = 1;

console.log(obj_1.someProperty);// output 1
console.log(obj_2.someProperty);// output 1

console.log(obj_1.constructor === ClassName); //Output true 

Цей підхід проходить усі тестові випадки, в той час як приватна статична реалізація не вдасться, коли використовується розширення прототипу (це може бути виправлено, але це не буде просто), а загальнодоступна статична реалізація, менш доцільна через екземпляр, піддається публіці.

демонстрація jsFiddly


5

Я думаю, що я знайшов найчистіший спосіб програмування в JavaScript, але вам знадобиться трохи уяви. Цю ідею я отримав із робочої техніки в книзі "javas the good parts".

Замість використання нового ключового слова ви можете створити такий клас:

function Class()
{
    var obj = {}; // Could also be used for inheritence if you don't start with an empty object.

    var privateVar;
    obj.publicVar;

    obj.publicMethod= publicMethod;
    function publicMethod(){} 

    function privateMethod(){} 

    return obj;
}

Ви можете описати вищезазначений об'єкт, сказавши:

var objInst = Class(); // !!! NO NEW KEYWORD

Тепер, маючи на увазі цей метод роботи, ви можете створити подібний сингл:

ClassSingleton = function()
{
    var instance= null;

    function Class() // This is the class like the above one
    {
        var obj = {};
        return obj;
    }

    function getInstance()
    {
        if( !instance )
            instance = Class(); // Again no new keyword;

        return instance;
    }   

    return { getInstance : getInstance };

}();

Тепер ви можете отримати свій примірник, зателефонувавши

var obj = ClassSingleton.getInstance();

Я думаю, що це найновіший спосіб, оскільки повний "Клас" навіть недоступний.


Але з цією технікою у вас може бути більше одного екземпляра. Це не правильно.
nicolascolman

1
Я б не думав, що ви навіть не можете отримати доступ до класу, не пройшовши getInstance. Не могли б ви детальніше?
Девід

Девід Мейс Вибачте, але я не помітив перевірку в другому прикладі. Прошу пробачення.
nicolascolman

4

@CMS і @zzzzBov дали чудові відповіді, але просто для додання моєї власної інтерпретації, заснованої на тому, що я перейшов у важку розробку node.js від PHP / Zend Framework, де єдинолітні моделі були поширеними.

Наступний, задокументований коментарями код, ґрунтується на таких вимогах:

  • один і лише один екземпляр функціонального об'єкта може бути екземпляром
  • екземпляр не є загальнодоступним та доступ до нього можна отримати лише за допомогою публічного методу
  • конструктор не є загальнодоступним та може бути створений лише тоді, коли ще не існує примірника
  • декларація конструктора повинна дозволяти змінювати його прототипний ланцюг. Це дозволить конструктору успадкувати інші прототипи та запропонувати для публічного примірника "загальнодоступні" методи

Мій код дуже схожий на @ zzzzBov, за винятком того, що я додав до конструктора ланцюжок прототипів і більше коментарів, які повинні допомогти тим, хто надходить з PHP або подібною мовою, перекласти традиційний OOP на прототипний характер Javascripts. Це може бути не найпростішим, але я вважаю, що це найправильніше.

// declare 'Singleton' as the returned value of a self-executing anonymous function
var Singleton = (function () {
    "use strict";
    // 'instance' and 'constructor' should not be availble in a "public" scope
    // here they are "private", thus available only within 
    // the scope of the self-executing anonymous function
    var _instance=null;
    var _constructor = function (name) {
        this.name = name || 'default';
    }

    // prototypes will be "public" methods available from the instance
    _constructor.prototype.getName = function () {
        return this.name;
    }

    // using the module pattern, return a static object
    // which essentially is a list of "public static" methods
    return {
        // because getInstance is defined within the same scope
        // it can access the "private" 'instance' and 'constructor' vars
        getInstance:function (name) {
            if (!_instance) {
                console.log('creating'); // this should only happen once
                _instance = new _constructor(name);
            }
            console.log('returning');
            return _instance;
        }
    }

})(); // self execute

// ensure 'instance' and 'constructor' are unavailable 
// outside the scope in which they were defined
// thus making them "private" and not "public"
console.log(typeof _instance); // undefined
console.log(typeof _constructor); // undefined

// assign instance to two different variables
var a = Singleton.getInstance('first');
var b = Singleton.getInstance('second'); // passing a name here does nothing because the single instance was already instantiated

// ensure 'a' and 'b' are truly equal
console.log(a === b); // true

console.log(a.getName()); // "first"
console.log(b.getName()); // also returns "first" because it's the same instance as 'a'

Зауважимо, що технічно самовиконання анонімної функції є самою Singleton, як це добре продемонстровано в коді, наданому @CMS. Єдиний замах тут полягає в тому, що неможливо змінити прототип ланцюга конструктора, коли сам конструктор є анонімним.

Майте на увазі, що до Javascript поняття "загальнодоступні" та "приватні" не застосовуються як у PHP чи Java. Але ми домоглися того ж ефекту, використовуючи правила доступності функціональних областей Javascript.


З вашого коду можна створити кілька примірників:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov

@zzzzBov: У моїй скрипці я просто отримую помилки: jsfiddle.net/rxMu8
cincodenada

4

Не впевнений, чому цього ніхто не підніс, але ви могли просто зробити:

var singleton = new (function() {
  var bar = 123

  this.foo = function() {
    // whatever
  }
})()

Здається, це акуратний спосіб пропустити метод getInstance і отримати більш просте рішення. Але майте на увазі, що одиночний файл виконається, як тільки файл буде розібраний, це означає, що слухачі DOM повинні бути загорнені у функцію $ (документ). Вже
HoffZ

4

Найяснішою відповіді має бути ця відповідь із книги «Навчання шаблонів дизайну JavaScript» Адді Османі.

var mySingleton = (function () {
 
  // Instance stores a reference to the Singleton
  var instance;
 
  function init() {
 
    // Singleton
 
    // Private methods and variables
    function privateMethod(){
        console.log( "I am private" );
    }
 
    var privateVariable = "Im also private";
 
    var privateRandomNumber = Math.random();
 
    return {
 
      // Public methods and variables
      publicMethod: function () {
        console.log( "The public can see me!" );
      },
 
      publicProperty: "I am also public",
 
      getRandomNumber: function() {
        return privateRandomNumber;
      }
 
    };
 
  };
 
  return {
 
    // Get the Singleton instance if one exists
    // or create one if it doesn't
    getInstance: function () {
 
      if ( !instance ) {
        instance = init();
      }
 
      return instance;
    }
 
  };
 
})();


3

Я вважаю, що це найпростіший / найчистіший та інтуїтивно зрозумілий спосіб, хоча для нього потрібен ES7:

export default class Singleton {

  static instance;

  constructor(){
    if(instance){
      return instance;
    }

    this.state = "duke";
    this.instance = this;
  }

}

Вихідний код: adam-bien.com


Це абсолютно неправильно і призведе до помилки при викликуnew Singleton()
UtkarshPramodGupta

2

Що з цим погано?

function Klass() {
   var instance = this;
   Klass = function () { return instance; }
}

Test = Klass; t1 = new Test(); t2 = new Test();- немає можливості перейменувати клас або вибрати інший простір імен.
zzzzBov

2

чи можу я поставити свої 5 монет. У мене функція конструктора, напр.

var A = function(arg1){
  this.arg1 = arg1  
};

Що мені потрібно зробити, це просто кожен об'єкт, створений цим КФ, буде однаковим.

var X = function(){
  var instance = {};
  return function(){ return instance; }
}();

тест

var x1 = new X();
var x2 = new X();
console.log(x1 === x2)

2

Я знайшов наступне як найпростіший шаблон Singleton, оскільки використання нового оператора робить це одразу доступним у межах функції, усуваючи необхідність повернення об'єкта в буквальному сенсі:

var singleton = new (function () {

  var private = "A private value";
  
  this.printSomething = function() {
      console.log(private);
  }
})();

singleton.printSomething();


2

Ось простий приклад пояснення однотонної картини в JavaScript.

 var Singleton=(function(){
      var instance;
      var init=function(){
           return {
             display:function(){
             alert("This is a Singleton patern demo");
              }
            };
           }; 
            return {
              getInstance:function(){
                   if(!instance){
                     alert("Singleton check");
                      instance=init();
                       }
               return instance;
             }
         };

    })();

   // In this call first display alert("Singleton check")
  // and then alert("This is a Singleton patern demo");
  // It means one object is created

    var inst=Singleton.getInstance();
    inst.display();

    // In this call only display alert("This is a Singleton patern demo")
   //  it means second time new object is not created, 
   //  it uses the already created object 

    var inst1=Singleton.getInstance();
    inst1.display();

1

Мені було потрібно кілька синглів з:

  • ледача ініціалізація
  • початкові параметри

і ось що я придумав:

createSingleton ('a', 'add', [1, 2]);
console.log(a);

function createSingleton (name, construct, args) {
    window[name] = {};
    window[construct].apply(window[name], args);
    window[construct] = null;
}

function add (a, b) {
    this.a = a;
    this.b = b;
    this.sum = a + b;
}
  • args повинен бути Array, щоб це працювало, тому якщо у вас порожні змінні, просто введіть []

  • Я використовував об'єкт вікна у функції, але міг перейти в параметр, щоб створити власну область

  • Параметри імені та конструкції - це лише String для роботи вікна [], але також можливі деякі прості перевірки типу, window.name та window.construct.


1

Як щодо цього способу, просто застрахуйте, що клас знову не може новий.

Таким чином, ви можете використовувати instanceofop, також ви можете використовувати прототип ланцюга для успадкування класу, це звичайний клас, але не можете створити його новий, якщо yuu хочете отримати екземпляр просто використовуватиgetInstance

function CA()
{
    if(CA.instance)
    {
        throw new Error('can not new this class');
    }else{
        CA.instance = this;
    }

}
/**
 * @protected
 * @static
 * @type {CA}
 */
CA.instance = null;
/** @static */
CA.getInstance = function()
{
    return CA.instance;
}

CA.prototype = 
/** @lends CA#*/
{
    func: function(){console.log('the func');}
}
// initilize the instance
new CA();

// test here
var c = CA.getInstance()
c.func();
console.assert(c instanceof CA)
// this will failed
var b = new CA();

Якщо ви не хочете виставляти instanceчлена, просто покладіть його на закриття.


1

далі - фрагмент з моєї прогулянки по впровадженню одиночної картини. Це трапилося зі мною під час інтерв'ю, і я відчував, що мені це потрібно десь захопити.

/*************************************************
   *     SINGLETON PATTERN IMPLEMENTATION          *
   *************************************************/

  //since there are no classes in javascript, every object is technically a singleton
  //if you don't inherit from it or copy from it.
  var single = {};
  //Singleton Implementations
  //Declaring as a Global Object...you are being judged!


  var Logger = function() {
    //global_log is/will be defined in GLOBAL scope here
    if(typeof global_log === 'undefined'){
      global_log = this;
    }
    return global_log;
  };


  //the below 'fix' solves the GLOABL variable problem but
  //the log_instance is publicly available and thus can be 

  //tampered with.
  function Logger() {
    if(typeof Logger.log_instance === 'undefined'){
      Logger.log_instance = this;
    }


    return Logger.log_instance;
   };


  //the correct way to do it to give it a closure!


  function logFactory() {
    var log_instance; //private instance
    var _initLog = function() { //private init method
      log_instance = 'initialized';
      console.log("logger initialized!")
    }
    return {
      getLog : function(){ //the 'privileged' method 
        if(typeof log_instance === 'undefined'){
          _initLog();
        }
        return log_instance;
      }
    };
  }

  /***** TEST CODE ************************************************
  //using the Logger singleton
  var logger = logFactory();//did i just gave LogFactory a closure?
  //create an instance of the logger
  var a = logger.getLog();
  //do some work
  //get another instance of the logger
  var b = logger.getLog();


  //check if the two logger instances are same?
  console.log(a === b); //true
  *******************************************************************/

те саме можна знайти на моїй суті сторінки


1
function Unicode()
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  //Loop through code points
  while (i < max) {
    //Convert decimal to hex value, find the character, then pad zeroes to the codepoint
    unicode[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);
    i = i + 1;
    }

  //Replace this function with the resulting lookup table
  Unicode = unicode;
  }

//Usage
Unicode();
//Lookup
Unicode["%"]; //returns 0025

1

Хіба це теж не сингл?

function Singleton() {
    var i = 0;
    var self = this;

    this.doStuff = function () {
        i = i + 1;
        console.log( 'do stuff',i );
    };

    Singleton = function () { return self };
    return this;
}

s = Singleton();
s.doStuff();

1

Ви можете це зробити з декораторами, як у цьому прикладі нижче для TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

function Singleton(target, name, descriptor) {
    var instance;
    descriptor.value = () => {
        if(!instance) instance = new target;
        return instance;
    };
}

Тоді ви використовуєте синглтон так:

var myInstance = YourClass.singleton();

На цей час, декоратори не доступні в двигунах JavaScript. Вам потрібно переконатися, що у JavaScript під час виконання дійсно включені декоратори або використовувати компілятори, такі як Babel і TypeScript.

Також зауважте, що одиночний екземпляр створюється "ледачим", тобто він створюється лише тоді, коли ви його використовуєте вперше.


1

Шаблон модуля: у "більш читаному стилі". Ви легко бачите, які методи є загальнодоступними, а які - приватними

var module = (function(_name){
   /*Local Methods & Values*/
   var _local = {
      name : _name,
      flags : {
        init : false
      }
   }

   function init(){
     _local.flags.init = true;
   }

   function imaprivatemethod(){
     alert("hi im a private method");
   }

   /*Public Methods & variables*/

   var $r = {}; //this object will hold all public methods.

   $r.methdo1 = function(){
       console.log("method1 call it");
   }

   $r.method2 = function(){
      imaprivatemethod(); //calling private method
   }

   $r.init = function(){
      inti(); //making init public in case you want to init manually and not automatically
   }

   init(); //automatically calling init method

   return $r; //returning all publics methods

})("module");

тепер ви можете використовувати такі публічні методи

module.method2 (); // -> Я викликаю приватний метод через попередження про публічний метод ("привіт, приватний метод")

http://jsfiddle.net/ncubica/xMwS9/


1

Singleton:

Переконайтесь, що у класу є лише один екземпляр і забезпечити глобальну точку доступу до нього.

Шаблон Singleton обмежує кількість примірників певного об'єкта лише одним. Цей єдиний екземпляр називається синглтон.

  • визначає getInstance (), який повертає унікальний екземпляр.
  • відповідальний за створення та керування об'єктом екземпляра.

Об'єкт Singleton реалізується як негайна анонімна функція. Функція виконується негайно, загортаючи її в дужки, а потім дві додаткові дужки. Його називають анонімним, оскільки він не має імені.

Зразок програми,

var Singleton = (function () {
    var instance;
 
    function createInstance() {
        var object = new Object("I am the instance");
        return object;
    }
 
    return {
        getInstance: function () {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();
 
function run() {
 
    var instance1 = Singleton.getInstance();
    var instance2 = Singleton.getInstance();
 
    alert("Same instance? " + (instance1 === instance2));  
}

run()


1

Найпростіший / найпростіший для мене означає також просто зрозуміти і без дзвінків, як багато обговорюється в Java-версії дискусії:

Який ефективний спосіб реалізувати однотонний візерунок на Java?

Відповідь, яка б могла відповідати найпростішому / найчистішому з моєї точки зору, це:

https://stackoverflow.com/a/70824/1497139

І це можна лише частково перекласти на JavaScript. Деякі відмінності Javascript:

  • конструктори не можуть бути приватними
  • Класи не можуть задекларувати поля

Але, враховуючи останній синтаксис ECMA, можна зблизитися з:

Однотонний візерунок як приклад класу JavaScript

 class Singleton {

  constructor(field1,field2) {
    this.field1=field1;
    this.field2=field2;
    Singleton.instance=this;
  }

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance=new Singleton('DefaultField1','DefaultField2');
    }
    return Singleton.instance;
  }
}

Приклад використання

console.log(Singleton.getInstance().field1);
console.log(Singleton.getInstance().field2);

Приклад Результат

DefaultField1
DefaultField2

1
function Once() {
    return this.constructor.instance || (this.constructor.instance = this);
}

function Application(name) {
    let app = Once.call(this);

    app.name = name;

    return app;
}

Якщо ви відвідуєте заняття:

class Once {
    constructor() {
        return this.constructor.instance || (this.constructor.instance = this);
    }
}

class Application extends Once {
    constructor(name) {
        super();

        this.name = name;
    }
}

Тест:

console.log(new Once() === new Once());

let app1 = new Application('Foobar');
let app2 = new Application('Barfoo');

console.log(app1 === app2);
console.log(app1.name); // Barfoo

1

Якщо ви хочете використовувати класи:

class Singleton {
  constructor(name, age) {
    this.name = name;
    this.age = age;
    if(this.constructor.instance)
      return this.constructor.instance;
    this.constructor.instance = this;
  }
}
let x = new Singleton('s',1);
let y = new Singleton('k',2);

Вихід для вищезазначеного буде:

console.log(x.name,x.age,y.name,y.age) // s 1 s 1

Ще один спосіб запису синглтона за допомогою функції

function AnotherSingleton (name,age) {
  this.name = name;
  this.age = age;
  if(this.constructor.instance)
    return this.constructor.instance;
  this.constructor.instance = this;
}

let a = new AnotherSingleton('s',1);
let b = new AnotherSingleton('k',2);

Вихід для вищезазначеного буде:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.