【问题标题】:Javascript namespace declaration with function-prototype带有函数原型的 Javascript 命名空间声明
【发布时间】:2011-10-31 13:24:54
【问题描述】:

我知道,这经常被讨论。但在像 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 。"

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

【问题讨论】:

    标签: javascript class namespaces function-prototypes


    【解决方案1】:

    我的做法是使用"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()
    

    为了进一步封装“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
    

    【讨论】:

    • 比阿姆贾德,这太棒了。但现在我又跌跌撞撞了。我看对了吗:使用这种方法,不可能创建一个与 obj 分离的 Namespace.blah() 函数?
    • @Johnny 如果我理解你的问题,只需在返回对象中添加一个blah 函数:. . return { 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 本身不是一个函数,你必须调用它的 main 函数这个 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 - 成功扩展原型对象!有点不正统,但有效!

    【讨论】:

    • 编辑中的第一个 sn-p 不会像您想象的那样工作。 obj.mainobj.prototype 是两个不同的独立函数。是的,如果您在没有new 的情况下调用它们,this 将引用同一个对象,但这仅仅是因为它引用了window。所以你将使foo 成为全球性的。
    • 您的第二个示例将Namespace 限制为仅包含一个“类”,这在某种程度上违背了命名空间的目的。
    • 你对第一个例子是正确的,我因为没有早点注意到这一点而感到愚蠢,但我不同意第二个例子。为什么将其限制为只有一个“类”?如果您使用更多对象,它将遍历它们并为它们分配正确的原型值。
    • 啊,没错,obj 里面有 prototype.... 我错过了。对此感到抱歉。我仍然会说这不是一个简单的方法。
    【解决方案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()
    

    【讨论】:

      猜你喜欢
      • 2011-01-31
      • 1970-01-01
      • 2011-03-30
      • 2011-07-22
      • 2012-12-18
      • 1970-01-01
      • 2015-09-01
      • 1970-01-01
      • 2017-05-31
      相关资源
      最近更新 更多