Tìm kiếm trang web

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


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

Câu trả lời #1

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

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

Nếu bạn muốn các thành viên riêng tư trên thể hiện singleton của mình, bạn có thể làm điều gì đó như sau:

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à mẫu mô-đun, và về cơ bản nó cho phép bạn bao đóng các thành phần riêng tư trên một đối tượng, bằng cách tận dụng việc sử dụng closures.

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 phương thức Object.freeze của ES5.

Điều đó sẽ làm cho đối tượng không thể thay đổi, ngăn cản bất kỳ sự sửa đổi nào đối với cấu trúc và giá trị của nó.

Nếu bạn đang sử dụng ES6, bạn có thể biểu diễn một singleton bằng ES Modules rất dễ dàng, và bạn thậm chí có thể giữ trạng thái riêng tư bằng cách khai báo các biến ở phạm vi module:

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

function privateFn () {
  // ...
}

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

Sau đó bạn có thể đơn giản nhập đối tượng singleton để sử dụng nó:

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

Câu trả lời #2

Tôi nghĩ cách tiếp cận sạch sẽ nhất là kiểu 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();

Câu trả lời #3

Trong ES6 cách đúng để 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 lỗi được ném ra khi tạo instance thứ hai, bạn có thể chỉ cần trả về instance 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"

Câu trả lời #4

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

Mẫu mô-đun:

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

Mọi thứ được khởi tạo trong mô hình module xảy ra khi Foo được khai báo. Ngoài ra, mô hình module có thể được sử dụng để khởi tạo một constructor, sau đó có thể được tạo thể hiện nhiều lần. Mặc dù mô hình module 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 singleton.

Mẫu đơn thể:

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

Trong cả hai phiên bản của mẫu singleton mà tôi đã cung cấp, chính constructor có thể được sử dụng như 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 thoải mái khi sử dụng constructor theo cách này, bạn có thể ném một lỗi trong câu lệnh if (instance), và tiếp tục sử dụng dạng 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 đơn thể (singleton) phù hợp với mẫu hàm khởi tạo ngầm định:

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

Câu trả lời số 5

Trong ECMAScript 2015 (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

Câu trả lời số 6

Nếu bạn đang sử dụng node.JS thì bạn có thể tận dụng cơ chế lưu trữ đệm của node.JS và Singleton của bạn sẽ đơn giản như sau:

class Singleton {
    constructor() {
        this.message = 'I am an instance';
    }
}
module.exports = new Singleton();

Xin lưu ý rằng chúng tôi xuất không phải lớp Singleton mà là thể hiện Singleton().

Node.JS sẽ lưu vào bộ nhớ đệm và tái sử dụng cùng một đối tượng mỗi lần nó được yêu cầu.

Để biết thêm chi tiết, vui lòng xem: Node.JS và Mẫu Đơn nhất

Câu trả lời số 7

Những điều sau đây hoạt động trong Node.js phiên bản 6:

class Foo {
  constructor(msg) {

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

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

Chúng tôi thử nghiệm:

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

Câu trả lời số 8

Đơn giản nhất/sạch sẽ nhất đối với tôi cũng có nghĩa là dễ hiểu và không có chi tiết rườm rà như đã được bàn nhiều trong phiên bản Java của cuộc thảo luận:

Cách hiệu quả để triển khai mẫu đơn (singleton) trong Java là gì?

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

Câu trả lời của Jonathan cho Cách nào hiệu quả để triển khai mẫu đơn (singleton) trong Java?

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

  • các hàm tạo không thể là riêng tư
  • 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ể tiếp cận gần với:

Mô hình Singleton như ví dụ về lớp trong 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;
  }
}

Ví dụ sử dụng

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

Kết quả ví dụ

DefaultField1
DefaultField2

Câu trả lời số 9

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 phần 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 cách sử dụng hàm

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 phần trên sẽ là:

console.log(a.name, a.age, b.name, b.age) // s 1 s 1

Câu trả lời số 10

Sử dụng các lớp ES6 và các trường tĩnh riêng tư. Gọi các thể hiện mới của lớp Singleton sẽ trả về cùng một thể hiện. Biến thể hiện cũng là riêng tư và không thể truy cập bên ngoài lớp.

class Singleton {
  // # is a new Javascript feature that denotes private
  static #instance;

  constructor() {
    if (!Singleton.#instance) {
      Singleton.#instance = this
    } 
    return Singleton.#instance
  }

  get() {
    return Singleton.#instance;
  }
}

const a = new Singleton();
const b = new Singleton();
console.log(a.get() === b.get()) // true
console.log(Singleton.instance === undefined) // true

Bài viết liên quan