使用函数原型声明Javascript命名空间

18

我知道这个问题经常被讨论。但是在像19世纪的一个人一样搜索了一番后,我需要一些建议。我没有问题地声明了一个“命名空间”,但当涉及到原型函数prototype.foo时,我卡住了。我找到了一种解决方法,但我不喜欢它:

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";
    }
}
...

但是它会抛出一个错误:"Uncaught SyntaxError: Unexpected token ."

我知道这只是一些表面上的问题,但我认为应该有一种更好的方法来声明包含类和原型函数的“名称空间”。

3个回答

34

我会使用"模块模式"来实现。


基本上,你需要将所有的“模块”逻辑封装在一个自执行函数中,并返回一个包含类、函数、变量等的对象。可以将返回值视为公开你的模块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()
为了进一步封装“obj”类的方法和构造函数,我们可以执行以下操作:
/** 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

谢谢Amjad,这很棒。但是现在我又遇到了问题。我理解得对吗:使用这种方法无法创建一个与obj分离的Namespace.blah()函数? - CYB
2
@Johnny 如果我理解你的问题正确,只需将 blah 函数添加到返回对象中: . . return { obj : obj, obj2: obj2, blah: function () {/* 做一些事情 */} }; - Amjad Masad
Amjad,非常感谢。这正是我想要的!现在我明白了。 - CYB

3

是的,因为你不能在对象声明中使用这种类型的链式调用

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:查看此fiddle 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-成功扩展了原型对象!这有点不正规,但可行!


你编辑的第一个片段不会像你想象的那样工作。obj.mainobj.prototype是两个不同的独立函数。是的,如果你在没有使用new的情况下调用它们,this将引用相同的对象,但这仅因为它引用了window。所以你会使foo成为全局变量。 - Felix Kling
你的第二个例子将 Namespace 限制为仅包含一个“class”,这在某种程度上违背了命名空间的目的。 - Felix Kling
你对第一个例子是正确的,我感到很愚蠢没有早点注意到这一点,但我不同意第二个例子。为什么要将其限制为仅一个“类”?如果使用更多对象,它将遍历它们并将正确的原型值分配给它们。 - Pantelis
啊,没错,你在prototype里面有obj……我漏看了。对此我很抱歉。但我仍然认为这不是一个直接的方法。 - Felix Kling

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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接