单例模式是 JavaScript 项目中最常用的设计模式之一, 下面罗列了 JavaScript 实现设计模式中的单例模式的一些技巧总结, 包括惰性加载与分支技术等, 需要的朋友可以参考下.
Javascript 是一种由 Netscape 的 LiveScript 发展而来的原型化继承的基于对象的动态类型的区分大小写的客户端脚本语言,主要目的是为了解决服务器端语言,比如 Perl,遗留的速度问题,为客户提供更流畅的浏览效果。
一、使用全局变量保存单例
这是最简单的实现方法
- function Person(){
- this.createTime=new Date();
- }
- var instance=new Person();
- function getInstance(){
- return instance;
- }
加载该 js 时就创建一个 Person 对象,保存到 instance 全局变量中,每次使用都取这个对象。如果一次都没使用,那么创建的这个对象则浪费了,我们可以优化一下,
- var instance
- function getInstance(){
- if(!instance){
- instance=new Person();
- }
- return instance;
- }
这样,第一次使用时才创建对象。
这个方法的缺点是,instance 是全局的变量,在多人合作或者开发周期比较长的情况下,很难保证 instance 不会被其它代码修改或覆盖,很可能到调用的时候,发现 instance 根本就不是 Person 对象。
我们考虑下使用闭包来封装起 instance,使它不再是全局变量就可以解决这个问题了
二、闭包创建对象
- var getInstance(){
- var instance;
- return function(){
- if(!instance){
- instance=new Person();
- }
- return instance;
- }
- }();
这样,instance 就被封装起来了,不用担心被修改了。
现在通过 getInstance() 函数可以获得单例了。新的问题,如果我通过 new Person() 来创建对象,获得的还是多个对象,javascript 又不可以像 java 一样把构造器私有化。那怎么样可以让多次 new 出来的对象都是一个实例呢?
三、构造函数的静态属性缓存实例
先看代码
- function Person(){
- //如果已经缓存了实例,则直接返回缓存的实例
- if(typeof Person.instance==='object'){
- return Person.instance;
- }
- this.createTime=new Date();
- //缓存实例
- Person.instance=this;
- return this;
- }
从代码可以看到,第一次 new 时,if 的条件返回 false,会往下走,初始化对象,然后保存对象到 Person.instance 这个静态属性中。
第二次 new 时,if 的条件返回 true,直接返回 Person.instance,不会再往下运行初始化的代码。所以不管 new 几次,返回的都是第一次创建的对象。
这个方法的缺点和方法一的缺点一样,Person.instance 也是公开属性,有可能会被修改。
我们参考方法二,使用闭包来封装一个,也许就能解决该问题了
四、重写构造函数
这个方法要使用闭包,但不能像方法二那么简单,我们需要重写构造函数。
- function Person(){
- //缓存实例
- var instance=this;
- this.createTime=new Date();
- //重写构造函数
- Person=function(){
- return instance;
- }
- }
第一次 new 时,调用原始构造函数先缓存该实例,然后再初始化,同时,重写该构造函数。以后再 new 时,永远调用不到原始的构造函数了,只能调用到重写后的构造函数,而这个函数总是返回缓存的 instance.
上面的方法似乎没什么问题,但通过下面的测试,可以发现问题
- //向原型添加属性
- Person.prototype.prop1=true;
- var p1=new Person();
- //在创建初始化对象后,再次向该原型添加属性
- Person.prototype.prop2=true;
- var p2=new Person();
- //开始测试
- console.log(p1.prop1);//结果为true
- console.log(p2.prop1);//结果为true
- console.log(p1.prop2);//结果为undefined
- console.log(p2.prop2);//结果为undefined
- console.log(p1.constructor===Person);//结果为false
- console.log(p2.constructor===Person);//结果为false
我们预期中的结果,应该是全都是 true。
分析一下上述测试代码
Person.prototype.prop1=true; 是在原始构造函数的原型下增加了 prop1 这个属性,并赋值
而在执行 var p1=new Person(); 之后,Person 这个构造函数已经被重写了
所以 Person.prototype.prop2=true; 是在新的原型下增加 prop2 这个属性
var p2=new Person(); p2 和 p1 实际上是同一个对象,即原始构造函数创建的对象
所以 p1 p2 都有 prop1 这个属性,而没有 prop2 这个属性
同样的,p1 p2 的 constructor 指向的也是原始的构造函数,而 Person 此时已不是原来那个函数了
为了能按预期的结果那样运行,可以通过一些修改来实现
- function Person(){
- //缓存实例
- var instance=this;
- //重写构造函数
- Person=function(){
- return instance;
- }
- //保留原型属性
- Person.prototype=this;
- //实例
- instance=new Person();
- //重置构造函数引用
- instance.constructor=Person;
- //其他初始化
- instance.createTime=new Date();
- return instance;
- }
再运行前面的测试代码,结果都是 true 了。
五、惰性加载:
在大型或复杂的项目中,起到了优化的作用:那些开销较大却很少用到的组件可以被包装到惰性加载单例中,示例程序:
- /* Singleton with Private Members, step 3. */
- MyNamespace.Singleton = (function() {
- // Private members.
- var privateAttribute1 = false;
- var privateAttribute2 = [1, 2, 3];
- function privateMethod1() {
- ...
- }
- function privateMethod2(args) {
- ...
- }
- return { // Public members.
- publicAttribute1: true,
- publicAttribute2: 10,
- publicMethod1: function() {
- ...
- },
- publicMethod2: function(args) {
- ...
- }
- };
- })();
- /* General skeleton for a lazy loading singleton, step 1. */
- MyNamespace.Singleton = (function() {
- function constructor() { // All of the normal singleton code goes here.
- // Private members.
- var privateAttribute1 = false;
- var privateAttribute2 = [1, 2, 3];
- function privateMethod1() {
- ...
- }
- function privateMethod2(args) {
- ...
- }
- return { // Public members.
- publicAttribute1: true,
- publicAttribute2: 10,
- publicMethod1: function() {
- ...
- },
- publicMethod2: function(args) {
- ...
- }
- }
- }
- })();
- /* General skeleton for a lazy loading singleton, step 2. */
- MyNamespace.Singleton = (function() {
- function constructor() { // All of the normal singleton code goes here.
- ...
- }
- return {
- getInstance: function() {
- // Control code goes here.
- }
- }
- })();
- /* General skeleton for a lazy loading singleton, step 3. */
- MyNamespace.Singleton = (function() {
- var uniqueInstance; // Private attribute that holds the single instance.
- function constructor() { // All of the normal singleton code goes here.
- ...
- }
- return {
- getInstance: function() {
- if(!uniqueInstance) { // Instantiate only if the instance doesn't exist.
- uniqueInstance = constructor();
- }
- return uniqueInstance;
- }
- }
- })();
六、使用分支单例:
针对特定环境的代码可以被包装到分支型单例中,示例程序:
- /* SimpleXhrFactory singleton, step 1. */
- var SimpleXhrFactory = (function() {
- // The three branches.
- var standard = {
- createXhrObject: function() {
- return new XMLHttpRequest();
- }
- };
- var activeXNew = {
- createXhrObject: function() {
- return new ActiveXObject('Msxml2.XMLHTTP');
- }
- };
- var activeXOld = {
- createXhrObject: function() {
- return new ActiveXObject('Microsoft.XMLHTTP');
- }
- };
- })();
- /* SimpleXhrFactory singleton, step 2. */
- var SimpleXhrFactory = (function() {
- // The three branches.
- var standard = {
- createXhrObject: function() {
- return new XMLHttpRequest();
- }
- };
- var activeXNew = {
- createXhrObject: function() {
- return new ActiveXObject('Msxml2.XMLHTTP');
- }
- };
- var activeXOld = {
- createXhrObject: function() {
- return new ActiveXObject('Microsoft.XMLHTTP');
- }
- };
- // To assign the branch, try each method; return whatever doesn't fail.
- var testObject;
- try {
- testObject = standard.createXhrObject();
- return standard; // Return this if no error was thrown.
- }
- catch(e) {
- try {
- testObject = activeXNew.createXhrObject();
- return activeXNew; // Return this if no error was thrown.
- }
- catch(e) {
- try {
- testObject = activeXOld.createXhrObject();
- return activeXOld; // Return this if no error was thrown.
- }
- catch(e) {
- throw new Error('No XHR object found in this environment.');
- }
- }
- }
- })();
来源: http://www.phperz.com/article/17/0227/265713.html