在javascript中从任何给定的类创建单例

时间:2009-09-23 13:02:36

标签: javascript constructor singleton arguments new-operator

我编写了以下函数,允许从给定的类创建单例类:

function SingletonFrom(Constructor) {
    return function() {
        var self = arguments.callee;
        if (self._instance === undefined) {
            switch (arguments.length) { // this is ugly
                case  0: self._instance = new Constructor(); break;
                case  1: self._instance = new Constructor(arguments[0]); break;
                case  2: self._instance = new Constructor(arguments[0], arguments[1]); break;
                // [...]
                case 10: // ...
                default: throw new Error('Error in Singleton: Constructor may take at most 10 arguments'); break;
            }
        }
        return self._instance;
    }
}

示例:

var Array_Singleton = new SingletonFrom(Array);
var x = new Array_Singleton(1,2,3);   // [1,2,3]
var y = new Array_Singleton(0,0,0,0); // [1,2,3]
alert(x === y); // true

效果很好,但我对switch语句不太满意。问题是将可变数量的参数传递给使用“new”关键字调用的构造函数是不可能的。因此,我的构造函数不能超过10个参数。例如,这将失败:

new Array_Singleton(1,2,3,4,5,6,7,8,9,10,11);

有什么方法可以解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

Javascript有这种烦人的限制无法使用数组作为参数列表调用构造函数。当函数作为函数调用时,Function.prototype.apply通常可以实现的功能(例如foo(...)),当它被称为构造函数(例如new foo(...))时,不能轻易地应用于函数。

我猜这正是你在那里诉诸switch的原因。

function foo(a,b) {
  return a+b;
}
foo.apply(null, [1,2]); // 3

但构造函数不是那么容易:

function Person(fname, lname) {
  this.fname = fname;
  this.lname = lname;
}
Person.prototype.speak = function() {
  return 'Hi. My name is: ' + this.fname + ' ' + this.lname;
}

new Person.apply(null, ['John', 'Appleseed']); // doesn't work!

要解决这个问题,您可以创建一个简单的帮助程序,实际上模拟new做什么,这次仅使用apply。该算法很简单:

  1. 使用适当的原型链创建一个空对象,但不要在其上调用构造函数。
  2. 使用apply在这个新创建的对象的上下文中调用构造函数,并使用apply传递参数列表。
  3. 它看起来像这样:

    function newApply(ctr, array) {
      function F(){}
      F.prototype = ctr.prototype;
      var obj = new F();
      ctr.apply(obj, array);
      return obj;
    }
    newApply(Person, ['John', 'Appleseed']); // returns new object
    

    或者,您可以避免在运行时创建F对象以节省性能和内存消耗

    var newApply = (function(){
      function F(){}
      return function(ctr, array) {
        F.prototype = ctr.prototype;
        var obj = new F();
        ctr.apply(obj, array);
        return obj;
      }
    })();
    

答案 1 :(得分:0)

在我的头脑中,我只能想到一个,如果没有非常大量的测试,我可能不会推荐它。

有人说;而不是单独传递参数,将它们作为数组传递。然后,您可以遍历数组并在调用new时构建一个字符串。获得字符串后,可以调用eval()来运行生成的命令。

这将为您提供处理任何动态数量参数的方法。