2011-08-21 64 views
17

我知道,这经常被讨论。但是经过像19世纪以外的人一样寻找,我需要一些建议。通过声明一个“命名空间”我没有问题,但是当谈到prototype.foo函数时,我卡住了。我找到了一种方法,但我不喜欢它:使用函数原型的Javascript名称空间声明

Namespace = {} 
Namespace.obj = function() { 
    this.foo="bar"; 
} 
Namespace.obj.prototype.start = function() { 
    this.foo="fubar"; 
} 

blah = new Namespace.obj(); 
blah.start(); 

现在,因为我在脚本的情况下有点神经质,我想有这样的事情:

Namespace = { 
    obj: function() { 
     this.foo="bar"; 
    }, 
    obj.prototype.start: function(tabinst) { 
     this.foo="fubar"; 
    } 
} 
... 

但随后会抛出一个错误: “未捕获的SyntaxError:意外的令牌。”

我知道,这是整容,但我认为必须有更好的方法来声明包含类和原型函数的“命名空间”。

回答

29

我会这样做的方式是使用"Module pattern"
你基本上把你所有的“模块”逻辑封装在一个自动执行的函数中,它会返回一个包含你的类,函数,变量等的对象......想想你的模块API的返回值。

Namespace = (function() { 
    /** Class obj **/ 
    var obj = function() { 
     this.foo = 'bar'; 
    }; 
    obj.prototype = { 
     start: function() { 
      this.foo = 'fubar'; 
     } 
    }; 

    /** Class obj2 **/ 
    var obj2 = function() { 
     this.bar = 'foo' 
    }; 
    obj2.prototype = { 
     start: function() { 
      this.bar = 'barfoo'; 
     }, 
     end: function() { 
      this.bar = ''; 
     } 
    }; 
    return { 
     obj : obj, 
     obj2: obj2 
    }; 
})(); 

var o = new Namespace.obj() 
o.start() 

为了进一步封装“目标文件”类的方法和构造函数,我们可以做到以下几点:

/** Class obj **/ 
var obj = (function() { 
    /** class Constructor **/ 
    var obj = function() { 
     this.foo = 'bar'; 
    }; 
    /** class methods **/ 
    obj.prototype = { 
     start: function() { 
      this.foo = 'fubar'; 
     } 
    }; 
    return obj; 
})(); 

还有,采用这种模式来为自由的一个重要特征,这是“私人变量“,请考虑以下内容:

/** Class Foo **/ 
var Foo = (function() { 
    // Private variables 
    var private_number = 200 
    /** class Constructor **/ 
    var Foo = function() { 
     this.bar = 0; 
    }; 
    /** class methods **/ 
    Foo.prototype = { 
     add: function() { 
      this.bar += private_number; 
     } 
    }; 
    return Foo; 
})(); 

foo = new Foo(); 
alert(foo.bar); // 0 
foo.add(); 
alert(foo.bar);// 200 
alert(foo.private_number) //undefined 
+0

好方法,+1。 – Jiri

+0

Thans Amjad,这太好了。但是现在我又绊倒了。我看到它是正确的:使用这种方法不可能创建一个Namespace.blah()函数,它是从obj分离的? –

+2

@Johnny如果我理解你的问题,只需在返回对象中添加一个'blah'函数: '。 。 012hreturn { obj:obj, obj2:obj2, blah:function(){/ * do something * /} };' –

2

是因为,你不能使用这种类型的对象声明

obj.prototype或这里obj.something链接的,因为语言看到OBJ作为非对象值。你可以伪造这样一个像这样

Namespace = {}; 

Namespace.obj =function() { 
     this.foo="bar"; 
}; 

Namespace.obj.prototype.start = function(tabinst) { 
     this.foo="fubar"; 
}; 

console.log(Namespace.obj.prototype); 

效果(见本小提琴http://jsfiddle.net/WewnF/

编辑:哇,我刚刚注意到,我说的已经是问题之内。我很抱歉没有注意到这一点......那么你描述自己的方式是实现这一目标的正确方法。

否则,您可以像这样重新编写代码 - 但不完全符合您的要求,并且工作原理也不一样(因为obj本身不是函数,您必须调用它的主函数这样obj.main();)

Namespace = { 
    obj: { 
      main : function() { 
       this.foo="bar"; 
      }, 
      prototype : { 
      start: function(tabinst) { 
      this.foo="fubar"; 
      } 
      } 
    } 
} 

编辑2:见本拨弄http://jsfiddle.net/NmA3v/1/

Namespace = { 
    obj: function() { 
     this.foo="bar"; 
    }, 
    prototype: { 
     obj : { 
      start : function(hi) { 
       alert(hi); 
      } 
     } 

    }, 

    initProto : function(){ 
     for(var key in Namespace) 
     { 
      if(key !== "prototype") 
      { 
       for(var jey in Namespace.prototype[ key ]) 
        Namespace[ key ].prototype[ jey ] = Namespace.prototype[ key ][ jey ]; 
      } 
     } 
    } 
} 

Namespace.initProto(); 

console.log(Namespace.obj); 

var test = new Namespace.obj(); 

test.start("Hello World"); 

这将有相同的效果。 说明:我们声明我们的对象是正常的属性函数,然后使用一个主对象,它具有与上面相同的名称的对象,例如对于每个Namespace.obj,还有一个Namespace.prototype.obj,它包含我们想要在原型链中添加的功能。然后使用namespace.protoInit(),我们遍历所有属性 - 并从Namespace.prototype [key]中提取函数,并将它们添加到Namespace [key] .prototype - 成功扩展原型对象!有点非正统,但工作!

+0

编辑中的第一个片段不会按照您的想法工作。 'obj.main'和'obj.prototype'是两个不同的独立函数。是的,如果你不用'new'来调用它,而只是因为它指向'window',那么'this'将引用同一个对象。所以你会让'foo'全球化。 –

+0

你的第二个例子将'Namespace'限制为只包含一个“类”,它以某种方式破坏了命名空间的用途。 –

+0

对于第一个例子,你是正确的,我感到很蠢,因为没有注意到这一点,但我不同意第二个例子。为什么它只限于一个“班级”?如果您使用更多的对象,它将遍历它们并为它们分配正确的原型值。 – Pantelis

3

只是为了踢和扩大上面的答案。基于嵌套名称空间的更多对象符号定位

var NS = {}; 

// Class List 
NS.Classes = { 
    Shape: (function(){ 
    // Private 
    var whateveryouwishboss = false; 

    // Public 
    var Shape = function(x,y,w,h){ 
     this.x = x; 
     this.y = y; 
     this.w = w; 
     this.h = h; 
    }; 
    Shape.prototype = { 
     draw: function(){ 
     //... Im on prototype Shape 
     } 
    } 
    return Shape; 
    })(), 
    Person: (function(){ 
    //.... 
    })() 
} 

/////// Let the games begin 

var rect = new NS.Class.Shape(0,0,10,10); 
rect.draw()