Cách đơn giản nhất / sạch nhất để triển khai singleton trong JavaScript?


300

Cách đơn giản nhất / sạch nhất để triển khai mẫu singleton trong JavaScript là gì?


15
Downvote cho câu trả lời được chấp nhận không phải là một singleton. Nó chỉ là một biến toàn cầu.
mlibby

5
Đây là một tấn thông tin, nhưng thực sự đưa ra sự khác biệt giữa các mẫu thiết kế JS khác nhau. Nó đã giúp tôi rất nhiều: addyosmani.com/resource/essentialjsdesignpotypes/book
Justin

Câu trả lời:


315

Tôi nghĩ cách dễ nhất là khai báo một đối tượng đơn giản theo nghĩa đen:

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

Nếu bạn muốn các thành viên riêng trên cá thể đơn lẻ của mình, bạn có thể làm một cái gì đó như thế này:

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

  function privateMethod () {
    // ...
  }

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

Điều này đã được gọi là các kiểu mô-đun , nó về cơ bản cho phép bạn đóng gói các thành viên tin về một đối tượng, bằng cách tận dụng việc sử dụng đóng cửa .

CẬP NHẬT: Tôi muốn thêm rằng nếu bạn muốn ngăn chặn việc sửa đổi đối tượng singleton, bạn có thể đóng băng nó , sử dụng Object.freezephương thức ES5 .

Điều đó sẽ làm cho đối tượng bất biến, ngăn chặn mọi sửa đổi đối với cấu trúc và giá trị của nó.

Ngoài ra, tôi muốn đề cập rằng nếu bạn đang sử dụng ES6, bạn có thể biểu diễn một singleton bằng Mô-đun ES rất dễ dàng và thậm chí bạn có thể giữ trạng thái riêng tư bằng cách khai báo các biến ở phạm vi mô-đun :

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

function privateFn () {
  // ...
}

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

Sau đó, bạn có thể chỉ cần nhập đối tượng singleton để sử dụng nó:

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

46
+1 Không có gì lạ khi tìm kiếm "mẫu Singleton" trong ngôn ngữ có biến toàn cục ???
Victor

4
Sử dụng mô-đun mô-đun, làm thế nào một thành viên công cộng sẽ truy cập một thành viên công cộng khác? Tức là, làm thế nào để publicMethod1gọi publicMethod2?
gõ vào

4
@Tom, yeah, mẫu được sinh ra trên các ngôn ngữ OOP dựa trên lớp - Tôi nhớ rất nhiều triển khai có liên quan đến một getInstancephương thức tĩnh và một hàm tạo riêng - nhưng IMO, đây là cách "đơn giản" nhất để xây dựng một đối tượng singleton trong Javascript và cuối cùng, nó đáp ứng cùng một mục đích - một đối tượng duy nhất, mà bạn không thể khởi tạo lại (không có hàm tạo, nó chỉ là một đối tượng) -. Về mã bạn đã liên kết, nó có một số vấn đề, trao đổi abkhai báo biến và kiểm tra a === window. Chúc mừng.
CMS

15
@Victor - Điều này không có gì lạ khi tìm kiếm một "mẫu đơn" trong ngôn ngữ như vậy. nhiều ngôn ngữ hướng đối tượng sử dụng các biến toàn cục và các singletons vẫn được sử dụng. Singleton không chỉ đảm bảo rằng sẽ chỉ có một đối tượng của lớp nhất định. Singleton có thêm một số tính năng: 1) nó nên được khởi tạo ở lần sử dụng đầu tiên (điều này không chỉ có nghĩa là khởi tạo chậm, mà còn đảm bảo rằng đối tượng đã thực sự sẵn sàng để sử dụng) 2) nó phải an toàn cho luồng. Mẫu mô-đun có thể được thay thế cho mẫu đơn nhưng chỉ trong trình duyệt (và không phải luôn luôn).
skalee

55
Đây không phải là câu trả lời được chấp nhận. Đây hoàn toàn không phải là một singleton! Đây chỉ là một biến toàn cầu. Có một thế giới của sự khác biệt giữa hai.
mlibby

172

Tôi nghĩ rằng cách tiếp cận sạch nhất là một cái gì đó như:

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;
        }
   };
})();

Sau đó, bạn có thể gọi hàm như

var test = SingletonFactory.getInstance();

4
Ghi chú: Có thể đọc lại hàm tạo ban đầu bằng cách sử dụng delete instance.constructor:x = SingletonClass.getInstance();delete x.constructor;new x.constructor;
Rob W

var test = SingletonClass.getInstance () - trông không được sạch sẽ hay giống như JS. Các linh hồn khác kết thúc bằng a = new Foo (); b = Foo mới (); a === b // đúng
Matthias

3
Có mùi giống nhà máy hơn đối với tôi với toàn bộ phần "getInstance".
Lajos Meszaros

1
Đây không phải là một singleton vì bạn có thể tạo nhiều phiên bản của nó với Object.create.
AndroidDev

Fiddle bị hỏng
HoffZ

103

Tôi không chắc chắn tôi đồng ý với mẫu mô-đun được sử dụng để thay thế cho mẫu đơn. Tôi thường thấy các singleton được sử dụng và lạm dụng ở những nơi hoàn toàn không cần thiết và tôi chắc chắn rằng mô-đun sẽ lấp đầy nhiều khoảng trống trong đó các lập trình viên sẽ sử dụng một singleton, tuy nhiên mô hình mô-đun không phải là một singleton.

mô hình mô-đun:

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

Mọi thứ khởi tạo trong mô hình mô đun xảy ra khi Foođược khai báo. Ngoài ra, mẫu mô-đun có thể được sử dụng để khởi tạo một hàm tạo, sau đó có thể được khởi tạo nhiều lần. Mặc dù mẫu mô-đun là công cụ phù hợp cho nhiều công việc, nhưng nó không tương đương với một mẫu đơn.

mô hình đơn lẻ:

hình thức ngắn
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();
}
hình thức dài, sử dụng mô hình mô-đun
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;
}());

Trong cả hai phiên bản của mẫu Singleton mà tôi đã cung cấp, chính hàm tạo có thể được sử dụng làm trình truy cập:

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

Nếu bạn không cảm thấy thoải mái khi sử dụng hàm tạo theo cách này, bạn có thể đưa ra lỗi trong if (instance)câu lệnh và sử dụng biểu mẫu dài:

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

Tôi cũng nên đề cập rằng mẫu singleton rất phù hợp với mẫu hàm xây dựng ẩn:

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
Tôi chưa bao giờ nói bất cứ điều gì về singletons là một ý tưởng tồi hay tốt. Tôi đã nói rằng việc triển khai singleton của bạn phức tạp hơn nhiều so với mức cần thiết bởi vì bạn nhầm lẫn các hạn chế của Java với mẫu như thể bạn hoàn toàn không hiểu nó. Nó giống như triển khai mẫu chiến lược bằng cách tạo các hàm và phương thức của hàm tạo khi bạn chỉ cần sử dụng các hàm ẩn danh cho Javascript đó.
Esailija

11
@Esailija, có vẻ như tôi không hiểu mẫu singleton. Mẫu đơn là một mẫu thiết kế giới hạn tức thời của một lớp đối với một đối tượng. var singleton = {}không phù hợp với định nghĩa đó.
zzzzBov

9
Điều đó chỉ áp dụng cho các ngôn ngữ như Java vì những hạn chế của chúng. var singleton = {}là cách bạn triển khai singleton trong Javascript .
Esailija

2
@Esailija, "Trong ngôn ngữ lập trình dựa trên nguyên mẫu, trong đó các đối tượng nhưng không sử dụng các lớp ..." JavaScript có khái niệm về các lớp, do đó không áp dụng.
zzzzBov


18

Trong 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
Đóng băng sẽ có ý nghĩa nếu Singleton sẽ là một lớp bọc xung quanh một số lớp khác và chỉ có instancetrường. Vì hiện tại ( instanceđược đặt thành this) lớp này cũng có thể có các trường khác và việc đóng băng không có nghĩa là imo.
thisismydesign

10

Các hoạt động sau đây trong nút v6

class Foo {
  constructor(msg) {

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

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

Chúng tôi kiểm tra:

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

8

Trong ES6, cách đúng đắn để làm điều này là:

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

Hoặc, nếu bạn không muốn ném lỗi khi tạo phiên bản thứ hai, bạn có thể trả về phiên bản cuối cùng, như sau:

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"


Xin chào, bạn có thể giúp tôi biết sự khác biệt giữa _instance và cá thể khi tôi đang sử dụng cá thể và mã không hoạt động
Abhinav bhardwaj

Không có sự khác biệt kỹ thuật trong instance_instance. Nó chỉ là một quy ước đặt tên trong các ngôn ngữ lập trình mà chúng tôi đặt tên cho các biến riêng được đặt trước một dấu gạch dưới. Tôi nghi ngờ lý do khiến mã của bạn không hoạt động là vì bạn đang sử dụng this.instancethay vìMyClass.instance
UtkarshPramodGupta

7

Có nhiều hơn một cách để nuôi mèo :) Tùy theo sở thích hoặc nhu cầu cụ thể của bạn, bạn có thể áp dụng bất kỳ giải pháp được đề xuất nào. Cá nhân tôi đi tìm giải pháp đầu tiên của CMS bất cứ khi nào có thể (khi bạn không cần sự riêng tư). Vì câu hỏi là đơn giản và sạch sẽ nhất, đó là người chiến thắng. Hoặc thậm chí:

var myInstance = {}; // done!

Điều này (trích dẫn từ blog của tôi) ...

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

không có ý nghĩa gì nhiều (ví dụ blog của tôi cũng không có) bởi vì nó không cần bất kỳ lọ riêng tư nào, vì vậy nó khá giống với:

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

Đoạn mã 2 chứa lỗi cú pháp. Bạn không thể viếtthis.f(){}
xoxox 6/11/2016

7

Tôi không tán thành câu trả lời của mình, xem cái khác của tôi .

Thông thường mẫu mô-đun (xem câu trả lời của CMS) KHÔNG phải mẫu đơn là đủ tốt. Tuy nhiên, một trong những tính năng của singleton là việc khởi tạo của nó bị trì hoãn cho đến khi cần đối tượng. Mô hình mô-đun thiếu tính năng này.

Đề xuất của tôi (CoffeeScript):

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

Được biên dịch thành cái này trong JavaScript:

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

Sau đó tôi có thể làm như sau:

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

Tại sao bạn sẽ tải mô-đun nếu không cần thiết? Và khi bạn cần tải mô-đun, sau đó bạn tải mô-đun, và nó khởi tạo.
Esailija

6

Câu trả lời ngắn:

Vì bản chất không chặn của JavaScript, Singletons trong JavaScript thực sự xấu khi sử dụng. Các biến toàn cục sẽ cung cấp cho bạn một phiên bản thông qua toàn bộ ứng dụng mà không cần tất cả các cuộc gọi lại này, mô-đun mô-đun nhẹ nhàng ẩn bên trong giao diện. Xem câu trả lời @CMS.

Nhưng, vì bạn muốn có một singleton

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);
    }
  };

};

Sử dụng:

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();
});

Giải trình:

Singletons cung cấp cho bạn nhiều hơn chỉ một trường hợp thông qua toàn bộ ứng dụng: việc khởi tạo của chúng bị trì hoãn cho đến lần sử dụng đầu tiên. Đây thực sự là một điều lớn khi bạn đối phó với các đối tượng có khởi tạo đắt tiền. Đắt tiền thường có nghĩa là I / O và trong JavaScript I / O luôn có nghĩa là gọi lại.

Đừng tin tưởng câu trả lời cung cấp cho bạn giao diện như thế nào instance = singleton.getInstance(), tất cả đều bỏ lỡ điểm.

Nếu họ không thực hiện cuộc gọi lại để chạy khi cá thể đã sẵn sàng, thì họ sẽ không hoạt động khi trình khởi tạo thực hiện I / O.

Vâng, các cuộc gọi lại luôn trông xấu hơn so với chức năng gọi ngay lập tức trả về thể hiện đối tượng. Nhưng một lần nữa: khi bạn thực hiện I / O, các cuộc gọi lại là bắt buộc. Nếu bạn không muốn thực hiện bất kỳ I / O nào, thì khởi tạo đủ rẻ để thực hiện khi bắt đầu chương trình.

Ví dụ 1, trình khởi tạo giá rẻ:

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);
});

Ví dụ 2, khởi tạo với I / O:

Trong ví dụ này setTimeoutgiả mạo một số hoạt động I / O đắt tiền. Điều này minh họa tại sao singletons trong JavaScript thực sự cần gọi lại.

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.");

Thông qua các thử nghiệm và khổ nạn với các câu trả lời đơn lẻ khác mà không cắt được, tôi đã tìm được mã kết quả tương tự như thế này.
mheyman

Vì lý do này hay lý do khác, đây là câu trả lời duy nhất có ý nghĩa với tôi. Các câu trả lời khác đều làm tôi nhớ đến tập chương trình goon trong đó ba người đàn ông cố gắng trèo tường cao bốn người, bằng cách trèo lên vai nhau một cách đệ quy.
Tim Ogilvy

Xếp chồng calback là điều tôi thực sự cần! Cảm ơn!!
Tim Ogilvy

Cách tiếp cận này thực sự không bao giờ cung cấp cho bạn một singleton như: singleton (singletonInitializer)! == singleton (singletonInitializer) chúng là hai trường hợp khác nhau. Hàm kết quả mà bạn trả về có thể được sử dụng để đính kèm nhiều cuộc gọi lại vào thể hiện, nhưng không chỉ định nghiêm ngặt rằng chỉ có thể tạo một thể hiện của loại này. Đó là toàn bộ quan điểm của một singleton.
Owen

6

Tôi đã lấy ví dụ này từ các mẫu JavaScript Xây dựng các ứng dụng tốt hơn với mã hóa và thiết kế mẫu của cuốn sách của Stoyan Stefanov trong trường hợp bạn cần một số lớp triển khai đơn giản như đối tượng singl tone, bạn có thể sử dụng chức năng ngay lập tức như sau:

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
        };
    };
}());

Và bạn có thể kiểm tra ví dụ này bằng cách kiểm tra trường hợp sau:

//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 

Cách tiếp cận này vượt qua tất cả các trường hợp thử nghiệm trong khi triển khai tĩnh riêng sẽ thất bại khi sử dụng phần mở rộng nguyên mẫu (có thể sửa nhưng không đơn giản) và triển khai tĩnh công khai ít được khuyến khích hơn do cá thể được phơi bày ra công chúng.

bản demo jsFiddly.


5

Tôi nghĩ rằng tôi đã tìm thấy cách lập trình sạch nhất trong JavaScript, nhưng bạn sẽ cần một chút trí tưởng tượng. Tôi có ý tưởng này từ một kỹ thuật làm việc trong cuốn sách "javascript các phần tốt".

Thay vì sử dụng từ khóa mới, bạn có thể tạo một lớp như thế này:

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;
}

Bạn có thể khởi tạo đối tượng trên bằng cách nói:

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

Bây giờ với phương pháp làm việc này, bạn có thể tạo một singleton như thế này:

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 };

}();

Bây giờ bạn có thể lấy ví dụ của bạn bằng cách gọi

var obj = ClassSingleton.getInstance();

Tôi nghĩ rằng đây là cách gọn gàng nhất vì "Lớp" hoàn chỉnh thậm chí không thể truy cập được.


Nhưng với kỹ thuật này, bạn có thể có nhiều hơn một ví dụ. Điều đó không đúng.
nicolascolman

1
Tôi sẽ không nghĩ như vậy, bạn thậm chí không thể truy cập vào lớp mà không thông qua getInstance. Bạn có thể giải thích?
David

David Maes Xin lỗi nhưng tôi không nhận thấy xác nhận trong ví dụ thứ hai. Tôi xin lỗi.
nicolascolman

4

@CMS và @zzzzBov đều đã đưa ra những câu trả lời tuyệt vời, nhưng chỉ để thêm cách giải thích của riêng tôi dựa trên việc tôi đã chuyển sang phát triển node.js nặng từ PHP / Zend Framework trong đó các mẫu singleton là phổ biến.

Các mã tài liệu nhận xét sau đây dựa trên các yêu cầu sau:

  • một và chỉ một thể hiện của đối tượng hàm có thể được khởi tạo
  • cá thể không có sẵn công khai và chỉ có thể được truy cập thông qua một phương thức công khai
  • hàm tạo không có sẵn công khai và chỉ có thể được khởi tạo nếu chưa có sẵn một thể hiện
  • khai báo của hàm tạo phải cho phép sửa đổi chuỗi nguyên mẫu của nó. Điều này sẽ cho phép hàm tạo kế thừa từ các nguyên mẫu khác và cung cấp các phương thức "công khai" cho ví dụ

Mã của tôi rất giống với @ zzzzBov, ngoại trừ tôi đã thêm một chuỗi nguyên mẫu vào hàm tạo và nhiều nhận xét sẽ giúp những người đến từ PHP hoặc một ngôn ngữ tương tự dịch OOP truyền thống sang bản chất nguyên mẫu của Javascripts. Nó có thể không phải là "đơn giản nhất" nhưng tôi tin rằng nó là đúng đắn nhất.

// 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'

Lưu ý rằng về mặt kỹ thuật, chức năng ẩn danh tự thực hiện chính nó là một Singleton như được thể hiện độc đáo trong mã được cung cấp bởi @CMS. Điều hấp dẫn duy nhất ở đây là không thể sửa đổi chuỗi nguyên mẫu của hàm tạo khi bản thân hàm tạo là ẩn danh.

Hãy nhớ rằng với Javascript, các khái niệm về công khai và các trò chơi riêng tư không được áp dụng như PHP hay Java. Nhưng chúng tôi đã đạt được hiệu quả tương tự bằng cách tận dụng các quy tắc về tính khả dụng của phạm vi chức năng của Javascript.


Nhiều trường hợp có thể được tạo từ mã của bạn:var a = Singleton.getInstance('foo'); var b = new a.constructor('bar');
zzzzBov

@zzzzBov: Tôi chỉ gặp lỗi khi thử điều đó trong Fiddle của mình: jsfiddle.net/rxMu8
cincodenada

4

Không chắc chắn tại sao không ai đưa ra điều này, nhưng bạn chỉ có thể làm:

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

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

Đây có vẻ là một cách gọn gàng để bỏ qua phương thức getInstance và có được một giải pháp đơn giản hơn. Nhưng hãy nhớ rằng singleton sẽ thực thi ngay khi tệp được phân tích cú pháp, nghĩa là người nghe DOM phải được gói trong hàm $ (tài liệu). Đã sẵn sàng
HoffZ

4

Câu trả lời rõ ràng nhất phải là câu hỏi này từ cuốn sách Học các mẫu thiết kế JavaScript của Addy Osmani.

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

Tôi tin rằng đây là cách đơn giản nhất / sạch nhất và trực quan nhất mặc dù nó yêu cầu ES7:

export default class Singleton {

  static instance;

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

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

}

Mã nguồn là từ: adam-bien.com


Điều này là hoàn toàn sai và sẽ gây ra lỗi khi gọinew Singleton()
UtkarshPramodGupta

2

Có chuyện gì với cái này vậy?

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

Test = Klass; t1 = new Test(); t2 = new Test();- không có cơ hội đổi tên lớp hoặc chọn một không gian tên khác.
zzzzBov

2

Tôi có thể đặt 5 đồng tiền của tôi. Tôi có một hàm xây dựng, ví dụ.

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

Điều tôi cần làm chỉ là mọi đối tượng được tạo bởi CF này sẽ giống nhau.

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

kiểm tra

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

2

Tôi đã tìm thấy sau đây là mẫu Singleton dễ nhất, bởi vì sử dụng toán tử mới làm cho điều này ngay lập tức có sẵn trong hàm, loại bỏ nhu cầu trả về một đối tượng theo nghĩa đen:

var singleton = new (function () {

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

singleton.printSomething();


2

Dưới đây là ví dụ đơn giản để giải thích mẫu singleton trong 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

Tôi cần một vài singletons với:

  • khởi tạo lười biếng
  • thông số ban đầu

và đây là những gì tôi nghĩ ra:

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 phải là Array để điều này hoạt động, vì vậy nếu bạn có các biến trống, chỉ cần chuyển vào []

  • Tôi đã sử dụng đối tượng cửa sổ trong hàm nhưng tôi có thể đã truyền vào một tham số để tạo phạm vi của riêng mình

  • Các tham số tên và cấu trúc chỉ là Chuỗi cho cửa sổ [] để hoạt động nhưng với một số cách kiểm tra kiểu đơn giản, window.name và window.construct cũng có thể.


1

Làm thế nào về cách này, chỉ cần đảm bảo lớp không thể mới một lần nữa.

Bằng cách này, bạn có thể sử dụng instanceofop, đồng thời, bạn có thể sử dụng chuỗi nguyên mẫu để kế thừa lớp, đó là một lớp thông thường, nhưng không thể mới, nếu bạn muốn lấy cá thể chỉ cần sử dụnggetInstance

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();

Nếu bạn không muốn để lộ instancethành viên, chỉ cần đóng nó lại.


1

sau đây là đoạn trích từ bước đi của tôi để thực hiện một mô hình đơn lẻ. Điều này xảy ra với tôi trong quá trình phỏng vấn và tôi cảm thấy rằng tôi nên nắm bắt điều này ở đâu đó.

/*************************************************
   *     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
  *******************************************************************/

cùng có thể được tìm thấy trên của tôi ý chính trang


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

Đây cũng không phải là một singleton sao?

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

Bạn có thể làm điều đó với các trình trang trí như trong ví dụ dưới đây cho TypeScript:

class YourClass {

    @Singleton static singleton() {}

}

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

Sau đó, bạn sử dụng singleton của bạn như thế này:

var myInstance = YourClass.singleton();

Theo văn bản này, các trình trang trí không có sẵn trong các công cụ JavaScript. Bạn sẽ cần đảm bảo thời gian chạy JavaScript của bạn có các trình trang trí thực sự được kích hoạt hoặc sử dụng các trình biên dịch như Babel và TypeScript.

Cũng lưu ý rằng cá thể singleton được tạo ra "lười biếng", tức là nó chỉ được tạo khi bạn sử dụng lần đầu tiên.


1

Mô hình mô-đun: trong "phong cách dễ đọc hơn". Bạn có thể dễ dàng thấy phương thức nào là công khai và phương thức nào là tư nhân

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");

bây giờ bạn có thể sử dụng các phương thức công khai như

mô-đun.method2 (); // -> Tôi đang gọi một phương thức riêng tư trong một cảnh báo phương thức công khai ("hi im một phương thức riêng tư")

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


1

Người độc thân:

Đảm bảo một lớp chỉ có một thể hiện và cung cấp một điểm truy cập toàn cầu vào nó.

Mẫu Singleton giới hạn số lượng phiên bản của một đối tượng cụ thể chỉ là một đối tượng. Trường hợp duy nhất này được gọi là singleton.

  • định nghĩa getInstance () trả về thể hiện duy nhất.
  • chịu trách nhiệm tạo và quản lý đối tượng thể hiện.

Đối tượng Singleton được triển khai như một chức năng ẩn danh ngay lập tức. Hàm thực thi ngay lập tức bằng cách gói nó trong ngoặc theo sau hai dấu ngoặc bổ sung. Nó được gọi là ẩn danh vì nó không có tên.

Chương trình mẫu,

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

Đơn giản nhất / Sạch nhất đối với tôi có nghĩa là cũng đơn giản để hiểu và không có tiếng chuông & còi như được thảo luận nhiều trong phiên bản Java của cuộc thảo luận:

Một cách hiệu quả để thực hiện một mẫu đơn trong Java là gì?

Câu trả lời phù hợp nhất / đơn giản nhất theo quan điểm của tôi là:

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

Và nó chỉ có thể được dịch một phần sang JavaScript. Một số khác biệt trong Javascript là:

  • nhà xây dựng không thể riêng tư
  • Các lớp không thể có các trường khai báo

Nhưng với cú pháp ECMA mới nhất, có thể kết hợp với:

Mẫu đơn như ví dụ lớp 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;
  }
}

Cách sử dụng ví dụ

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

Kết quả ví dụ

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;
}

Nếu bạn vào lớp:

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

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

        this.name = name;
    }
}

Kiểm tra:

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

Nếu bạn muốn sử dụng các lớp:

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);

Đầu ra cho ở trên sẽ là:

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

Một cách khác để viết Singleton bằng chức năng

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);

Đầu ra cho ở trên sẽ là:

console.log(a.name,a.age,b.name,b.age)// s 1 s 1
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.