JavaScript 如何实现单例

平时我们用网页的时候,总会遇到一些“只需要有一份就行”的功能——比如网站的全局配置工具、弹窗管理器,或者聊天软件的状态管理器。如果不小心多创建了几份,要么会浪费内存,要么会出现“两份配置打架”的问题,这时候“单例模式”就派上用场了。它简单说就是:不管怎么调用,只生成一个实例,用完就共享这一份。

先搞懂最基础的单例写法

单例的核心是“记录有没有创建过实例”,用一个变量存实例就行。举个生活化的例子:小区门口的快递驿站,不管几个人去寄快递,驿站本身只有一个,不会因为有人来就多开一个。对应的JS代码可以这样写:

JavaScript 如何实现单例

`javascript

// 用闭包存实例,避免全局变量污染

const CourierStation = (function() {

let instance; // 专门存唯一的驿站实例

return function() {

// 如果已经有实例,就直接返回

if (instance) return instance;

// 没有的话就创建新实例,存起来

instance = { name: "小区快递驿站", address: "1号楼底商" };

return instance;

};

})();

// 测试:不管new多少次,都是同一个实例

const station1 = new CourierStation();

const station2 = new CourierStation();

console.log(station1 === station2); // 输出true,说明是同一个

`

这个写法的关键是用闭包把instance藏起来,不会被外部随便修改,保证了唯一性。如果不用闭包,把instance放全局,很容易被误改成别的东西,就失去单例的意义了。

适合对象的单例:用类来实现

如果你的单例要带方法,比如驿站有“存快递”“取快递”的功能,用ES6类来写会更清晰。就像小区驿站不仅有地址,还能提供服务,对应的单例类可以这样写:

`javascript

class ParkStation {

constructor() {

// 把构造函数改成只允许创建一次实例

if (ParkStation.instance) {

return ParkStation.instance;

}

ParkStation.instance = this;

this.name = "小区停车场";

this.cars = [];

}

// 存车方法

parkCar(car) {

this.cars.push(car);

console.log(${car}已停放,当前共${this.cars.length}辆车);

}

}

// 测试:创建两次,还是同一个实例

const park1 = new ParkStation();

const park2 = new ParkStation();

park1.parkCar("特斯拉"); // 输出:特斯拉已停放,当前共1辆车

park2.parkCar("比亚迪"); // 输出:比亚迪已停放,当前共2辆车

console.log(park1 === park2); // 输出true

`

这里的ParkStation.instance就是用来“记账”的,第一次创建实例后,下次再new就直接返回已存的实例,不会重复生成。这个写法比闭包更直观,适合需要定义多个属性和方法的单例。

灵活的单例:通用的“创建工具”

有时候你需要不同的单例——比如有的是驿站,有的是停车场,这时候可以写一个通用的单例生成函数,就像一个“单例工厂”,不管你要创建什么单例,都能用它来做。举个例子,要做两个不同的单例:小区的超市和楼下的咖啡店:

`javascript

// 通用单例生成器,接收一个对象作为实例模板

function createSingleton(template) {

let instance;

return function() {

if (instance) return instance;

instance = Object.create(template);

return instance;

};

}

// 生成超市单例

const Supermarket = createSingleton({ type: "超市", goods: ["蔬菜", "零食"] });

// 生成咖啡店单例

const CoffeeShop = createSingleton({ type: "咖啡店", drinks: ["拿铁", "美式"] });

// 测试:两个单例完全独立

const market1 = new Supermarket();

const market2 = new Supermarket();

const coffee1 = new CoffeeShop();

console.log(market1 === market2); // true

console.log(market1 === coffee1); // false

`

这个工厂函数把“创建唯一实例”的逻辑抽出来,不管要做什么单例,只要传入对应的模板就行,不用每次都重新写判断逻辑,适合项目里要用到多个单例的场景。

单例的小注意事项

单例虽然好用,但不能滥用。比如把每个工具都做成单例,会让代码灵活性变差——比如你想同时用两个不一样的弹窗,就会做不到。就像你不能把小区所有的店都只开一家,不然想买不同的东西都不方便。所以单例只适合“全局唯一”的场景,比如全局的配置、状态管理器,用的时候要想好是不是真的需要“只有一个”。

上一篇 深入了解Swift编程语言的核心特性与广泛应用场景