Câu trả lời:
Các lớp Singleton trong TypeScript thường là một mẫu chống. Bạn chỉ có thể sử dụng không gian tên thay thế.
class Singleton {
/* ... lots of singleton logic ... */
public someMethod() { ... }
}
// Using
var x = Singleton.getInstance();
x.someMethod();
export namespace Singleton {
export function someMethod() { ... }
}
// Usage
import { SingletonInstance } from "path/to/Singleton";
SingletonInstance.someMethod();
var x = SingletonInstance; // If you need to alias it for some reason
export default new Singleton()
?
Kể từ TS 2.0, chúng tôi có khả năng xác định các công cụ sửa đổi khả năng hiển thị trên các hàm tạo , vì vậy bây giờ chúng tôi có thể thực hiện các singletons trong TypeScript giống như chúng ta đã quen với các ngôn ngữ khác.
Ví dụ đã cho:
class MyClass
{
private static _instance: MyClass;
private constructor()
{
//...
}
public static get Instance()
{
// Do you need arguments? Make it a regular static method instead.
return this._instance || (this._instance = new this());
}
}
const myClassInstance = MyClass.Instance;
Cảm ơn bạn @Drenai đã chỉ ra rằng nếu bạn viết mã bằng cách sử dụng javascript được biên dịch thô, bạn sẽ không được bảo vệ chống lại nhiều lần khởi tạo, vì các ràng buộc của TS biến mất và hàm tạo sẽ không bị ẩn.
Cách tốt nhất tôi đã tìm thấy là:
class SingletonClass {
private static _instance:SingletonClass = new SingletonClass();
private _score:number = 0;
constructor() {
if(SingletonClass._instance){
throw new Error("Error: Instantiation failed: Use SingletonClass.getInstance() instead of new.");
}
SingletonClass._instance = this;
}
public static getInstance():SingletonClass
{
return SingletonClass._instance;
}
public setScore(value:number):void
{
this._score = value;
}
public getScore():number
{
return this._score;
}
public addPoints(value:number):void
{
this._score += value;
}
public removePoints(value:number):void
{
this._score -= value;
}
}
Đây là cách bạn sử dụng nó:
var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10);
scoreManager.addPoints(1);
scoreManager.removePoints(2);
console.log( scoreManager.getScore() );
https://codebelt.github.io/blog/typescript/typescript-singleton-potype/
Cách tiếp cận sau đây tạo ra một lớp Singleton có thể được sử dụng một cách kỳ lạ như một lớp thông thường:
class Singleton {
private static instance: Singleton;
//Assign "new Singleton()" here to avoid lazy initialisation
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
this. member = 0;
Singleton.instance = this;
}
member: number;
}
Mỗi new Singleton()
hoạt động sẽ trả về cùng một ví dụ. Điều này tuy nhiên có thể gây bất ngờ bởi người dùng.
Ví dụ sau minh bạch hơn với người dùng nhưng yêu cầu sử dụng khác nhau:
class Singleton {
private static instance: Singleton;
//Assign "new Singleton()" here to avoid lazy initialisation
constructor() {
if (Singleton.instance) {
throw new Error("Error - use Singleton.getInstance()");
}
this.member = 0;
}
static getInstance(): Singleton {
Singleton.instance = Singleton.instance || new Singleton();
return Singleton.instance;
}
member: number;
}
Sử dụng: var obj = Singleton.getInstance();
new Class(...)
cú pháp.
Tôi ngạc nhiên khi không thấy mô hình sau đây, mà thực sự trông rất đơn giản.
// shout.ts
class ShoutSingleton {
helloWorld() { return 'hi'; }
}
export let Shout = new ShoutSingleton();
Sử dụng
import { Shout } from './shout';
Shout.helloWorld();
Shout
lớp mới
Bạn có thể sử dụng các biểu thức lớp cho điều này (kể từ 1.6 tôi tin).
var x = new (class {
/* ... lots of singleton logic ... */
public someMethod() { ... }
})();
hoặc với tên nếu lớp của bạn cần truy cập vào loại bên trong
var x = new (class Singleton {
/* ... lots of singleton logic ... */
public someMethod(): Singleton { ... }
})();
Một tùy chọn khác là sử dụng một lớp cục bộ bên trong singleton của bạn bằng cách sử dụng một số thành viên tĩnh
class Singleton {
private static _instance;
public static get instance() {
class InternalSingleton {
someMethod() { }
//more singleton logic
}
if(!Singleton._instance) {
Singleton._instance = new InternalSingleton();
}
return <InternalSingleton>Singleton._instance;
}
}
var x = Singleton.instance;
x.someMethod();
Thêm 6 dòng sau vào bất kỳ lớp nào để biến nó thành "Singleton".
class MySingleton
{
private constructor(){ /* ... */}
private static _instance: MySingleton;
public static getInstance(): MySingleton
{
return this._instance || (this._instance = new this());
};
}
var test = MySingleton.getInstance(); // will create the first instance
var test2 = MySingleton.getInstance(); // will return the first instance
alert(test === test2); // true
[Chỉnh sửa]: Sử dụng câu trả lời của Alex nếu bạn muốn lấy ví dụ thông qua một thuộc tính thay vì một phương thức.
new MySingleton()
, nói 5 lần? mã của bạn có bảo lưu một thể hiện không?
tôi nghĩ có thể sử dụng thuốc generic
class Singleton<T>{
public static Instance<T>(c: {new(): T; }) : T{
if (this._instance == null){
this._instance = new c();
}
return this._instance;
}
private static _instance = null;
}
cách sử dụng
bước 1
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
bước 2
MapManager.Instance(MapManager).init();
Bạn cũng có thể sử dụng hàm Object .ze () . Nó đơn giản và dễ dàng:
class Singleton {
instance: any = null;
data: any = {} // store data in here
constructor() {
if (!this.instance) {
this.instance = this;
}
return this.instance
}
}
const singleton: Singleton = new Singleton();
Object.freeze(singleton);
export default singleton;
if (!this.instance)
hàm tạo? Có phải đó chỉ là một biện pháp phòng ngừa bổ sung trong trường hợp bạn đã tạo nhiều phiên bản trước khi xuất?
Tôi đã tìm thấy một phiên bản mới này mà trình biên dịch Typecript hoàn toàn ổn và tôi nghĩ là tốt hơn bởi vì nó không yêu cầu gọi một getInstance()
phương thức liên tục.
import express, { Application } from 'express';
export class Singleton {
// Define your props here
private _express: Application = express();
private static _instance: Singleton;
constructor() {
if (Singleton._instance) {
return Singleton._instance;
}
// You don't have an instance, so continue
// Remember, to set the _instance property
Singleton._instance = this;
}
}
Điều này không đi kèm với một nhược điểm khác nhau. Nếu bạn Singleton
không có bất kỳ thuộc tính nào, thì trình biên dịch typecript sẽ phù hợp trừ khi bạn khởi tạo chúng với một giá trị. Đó là lý do tại sao tôi bao gồm một _express
thuộc tính trong lớp ví dụ của mình bởi vì trừ khi bạn khởi tạo nó với một giá trị, ngay cả khi bạn gán nó sau trong hàm tạo, typecript sẽ nghĩ rằng nó chưa được xác định. Điều này có thể được khắc phục bằng cách vô hiệu hóa chế độ nghiêm ngặt, nhưng tôi không muốn nếu có thể. Ngoài ra còn có một nhược điểm khác của phương thức này mà tôi nên chỉ ra, bởi vì hàm tạo thực sự được gọi, mỗi lần nó thực hiện một thể hiện khác được tạo ra về mặt kỹ thuật, nhưng không thể truy cập được. Về lý thuyết, điều này có thể gây rò rỉ bộ nhớ.
Đây có lẽ là quá trình dài nhất để tạo ra một singleton trong bản thảo, nhưng trong các ứng dụng lớn hơn là một trong những hoạt động tốt hơn đối với tôi.
Trước tiên, bạn cần một lớp Singleton, giả sử "./utils/Singleton.ts" :
module utils {
export class Singleton {
private _initialized: boolean;
private _setSingleton(): void {
if (this._initialized) throw Error('Singleton is already initialized.');
this._initialized = true;
}
get setSingleton() { return this._setSingleton; }
}
}
Bây giờ hãy tưởng tượng bạn cần một bộ định tuyến singleton "./navulation/Router.ts" :
/// <reference path="../utils/Singleton.ts" />
module navigation {
class RouterClass extends utils.Singleton {
// NOTICE RouterClass extends from utils.Singleton
// and that it isn't exportable.
private _init(): void {
// This method will be your "construtor" now,
// to avoid double initialization, don't forget
// the parent class setSingleton method!.
this.setSingleton();
// Initialization stuff.
}
// Expose _init method.
get init { return this.init; }
}
// THIS IS IT!! Export a new RouterClass, that no
// one can instantiate ever again!.
export var Router: RouterClass = new RouterClass();
}
Nice!, Bây giờ khởi tạo hoặc nhập bất cứ nơi nào bạn cần:
/// <reference path="./navigation/Router.ts" />
import router = navigation.Router;
router.init();
router.init(); // Throws error!.
Điều tuyệt vời khi thực hiện singletons theo cách này là bạn vẫn sử dụng tất cả vẻ đẹp của các lớp bản thảo, nó mang lại cho bạn sự tinh tế tốt đẹp, logic singleton giữ tách biệt và dễ dàng loại bỏ nếu cần.
Giải pháp của tôi cho nó:
export default class Modal {
private static _instance : Modal = new Modal();
constructor () {
if (Modal._instance)
throw new Error("Use Modal.instance");
Modal._instance = this;
}
static get instance () {
return Modal._instance;
}
}
return Modal._instance
. Bằng cách này, nếu bạn new
lớp đó, bạn có được đối tượng hiện có, không phải là một đối tượng mới.
Trong Typecript, người ta không nhất thiết phải tuân theo new instance()
phương pháp Singleton. Một lớp tĩnh không có hàm tạo được nhập cũng có thể hoạt động như nhau.
Xem xét:
export class YourSingleton {
public static foo:bar;
public static initialise(_initVars:any):void {
YourSingleton.foo = _initvars.foo;
}
public static doThing():bar {
return YourSingleton.foo
}
}
Bạn có thể nhập lớp và tham khảo YourSingleton.doThing()
trong bất kỳ lớp nào khác. Nhưng hãy nhớ, vì đây là một lớp tĩnh, nó không có hàm tạo nên tôi thường sử dụng một intialise()
phương thức được gọi từ một lớp nhập Singleton:
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
Đừng quên rằng trong một lớp tĩnh, mọi phương thức và biến cũng cần phải tĩnh để thay vì this
bạn sẽ sử dụng tên lớp đầy đủ YourSingleton
.
Đây là một cách khác để làm điều đó với cách tiếp cận javascript thông thường hơn bằng cách sử dụng IFFE :
module App.Counter {
export var Instance = (() => {
var i = 0;
return {
increment: (): void => {
i++;
},
getCount: (): number => {
return i;
}
}
})();
}
module App {
export function countStuff() {
App.Counter.Instance.increment();
App.Counter.Instance.increment();
alert(App.Counter.Instance.getCount());
}
}
App.countStuff();
Xem bản demo
Instance
biến là gì? Bạn coukd chỉ cần đặt biến và các hàm trực tiếp bên dưới App.Counter
.
Một tùy chọn khác là sử dụng Biểu tượng trong mô-đun của bạn. Bằng cách này, bạn có thể bảo vệ lớp của mình, nếu người dùng cuối cùng của API của bạn đang sử dụng Javascript bình thường:
let _instance = Symbol();
export default class Singleton {
constructor(singletonToken) {
if (singletonToken !== _instance) {
throw new Error("Cannot instantiate directly.");
}
//Init your class
}
static get instance() {
return this[_instance] || (this[_instance] = new Singleton(_singleton))
}
public myMethod():string {
return "foo";
}
}
Sử dụng:
var str:string = Singleton.instance.myFoo();
Nếu người dùng đang sử dụng tệp js API đã biên dịch của bạn, cũng sẽ gặp lỗi nếu anh ta cố gắng khởi tạo thủ công lớp của bạn:
// PLAIN JAVASCRIPT:
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
Đây là cách đơn giản nhất
class YourSingletoneClass {
private static instance: YourSingletoneClass;
private constructor(public ifYouHaveAnyParams: string) {
}
static getInstance() {
if(!YourSingletoneClass.instance) {
YourSingletoneClass.instance = new YourSingletoneClass('If you have any params');
}
return YourSingletoneClass.instance;
}
}
namespace MySingleton {
interface IMySingleton {
doSomething(): void;
}
class MySingleton implements IMySingleton {
private usePrivate() { }
doSomething() {
this.usePrivate();
}
}
export var Instance: IMySingleton = new MySingleton();
}
Bằng cách này, chúng ta có thể áp dụng một giao diện, không giống như trong câu trả lời được chấp nhận của Ryan Cavanaugh.
Sau khi quét chủ đề này và chơi xung quanh với tất cả các tùy chọn ở trên - tôi đã giải quyết với một Singleton có thể được tạo bằng các hàm tạo thích hợp:
export default class Singleton {
private static _instance: Singleton
public static get instance(): Singleton {
return Singleton._instance
}
constructor(...args: string[]) {
// Initial setup
Singleton._instance = this
}
work() { /* example */ }
}
Nó sẽ yêu cầu thiết lập ban đầu (trong main.ts
, hoặc index.ts
), có thể dễ dàng thực hiện bằng
new Singleton(/* PARAMS */)
Sau đó, bất cứ nơi nào trong mã của bạn, chỉ cần gọi Singleton.instnace
; trong trường hợp này, để work
hoàn thành, tôi sẽ gọiSingleton.instance.work()