博客 / 詳情

返回

js 單例模式 6 種實現方式

JavaScript 中的單例模式確保一個類只有一個實例,並提供全局訪問點。以下是幾種常見的實現方式:

1.對象字面量(最簡單的方式)

const Singleton = {
  property: 'value',
  method() {
    // 使用 this(在方法被解構調用時會丟失上下文)
    // console.log(this.property);
    
    // 使用 Singleton(更安全)
    console.log(Singleton.property);
  }
};

// 使用
Singleton.method();

JavaScript 可以使用對象字面量快速創建一個對象,創建的對象本身就是單例。這種方式最為簡單,但是需要注意 this 的引用可能出問題。

為了解決 this 的問題,可以直接引用單例對象本身。

2.閉包實現

const Singleton = (function() {
  let instance;
  
  function createInstance() {
    const object = new Object('I am the instance');
    return object;
  }
  
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

// 使用
const instance1 = Singleton.getInstance();

這裏利用了閉包的特性實現了模塊的封裝和單例對象的引用,返回一個 getInstance 方法用於獲取實例對象。

3.ES6 Class 實現

class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    
    this.data = 'Singleton Data';
    Singleton.instance = this;
  }
  
  getData() {
    return this.data;
  }
  
  setData(data) {
    this.data = data;
  }
}

// 使用
const s1 = new Singleton();
const s2 = new Singleton();
console.log(s1 === s2); // true
s1.setData('New Data');
console.log(s2.getData()); // 'New Data'

這裏的 instance 是一個靜態變量,這種方式在 constructor 的最後將 this 賦值給 instance

4.改進的 class 實現(typescript 版本)

class Singleton {
  private static instance?: Singleton;

  private constructor() {}

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}

// 使用
const s1 = Singleton.getInstance();
const s2 = Singleton.getInstance();
console.log(s1 === s2); // true

typescript 版本抽取 getInstance 方法,讓代碼更可讀。使用 private 關鍵字實現私有屬性和方法,保證代碼不會被隨意篡改。

5.ES6 模塊模式的單例

// 在模塊文件中
let instance = null;

class Database {
  constructor(config) {
    if (instance) {
      return instance;
    }
    
    this.connection = this.connect(config);
    instance = this;
  }
  
  connect(config) {
    return { connected: true, config };
  }
}

// 導出一個獲取實例的函數
export const getInstance= (() => {
  let instance = null;
  return (config) => {
    if (!instance) {
      instance = new Database(config);
    }
    return instance;
  };
})();

這種方式利用 ES6 模塊變量的引用共享特性,保證 instance 唯一。導出一個 getInstance 方法。

6.ES6 模塊本身就是單例

class Database {
  constructor(config) {
    this.connection = this.connect(config);
  }
  
  connect(config) {
    return { connected: true, config };
  }
}

// 直接導出一個實例
export default new Database({ host: 'localhost' });

實測證明,導出的實例在多個模塊間是共享的。

總結

JavaScript 實現單例模式的方式很多,這裏介紹常用的6種,主要分4大類:對象字面量、閉包實現、 ES6 class 實現、ES6 模塊模式實現。

選擇哪種實現方式取決於具體需求,簡單場景可以使用對象字面量,複雜場景建議使用 ES6 Class 或 ES6 模塊模式實現。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.