如何将原型模式引入JavaScript命名空间

4
在我开始之前,我想坦白,我是一个JavaScript新手,对于JavaScript模式和术语的理解和知识很少,因此请随意向我解释基本概念,就像我只有5岁一样!
我以前在我的工作中成功地使用了JavaScript原型模式。
以下是我以前使用原型模式的示例。
var SomeNameSpace = SomeNameSpace || {};
SomeNameSpace.SomeClass = function(oSomeParameter){
     this.SomeProperty = oSomeParameter
     ...
}

SomeNameSpace.SomeClass.prototype = {
     SomeClassMethod: function (oSomeOtherParameter) {//code here}
}

var someClassInstance = new SomeNameSpace.SomeClass("some string");
var result = someClassInstance.SomeClassMethod("some other string");

这段代码是我一直以来使用 JavaScript 的示例。

现在我负责支持一些新的 JavaScript 代码。我想向这个新库引入相同的原型模式,但命名空间的写法对我来说很陌生,我不知道如何修改它以适应我的需求。

一个例子

if (typeof SomeNamespace == "undefined") {
    SomeNamespace = { __namespace: true };
}



 SomeNamespace.SomeOtherNamespace = {
     SomeClass: function(oSomeParameter){
          this.SomeProperty = oSomeParameter
          ...
     }
 }

我不知道如何将原型函数添加到这段代码中...
(如果我对细节含糊不清,那么很抱歉,我甚至不确定为什么在我的第二个示例中要声明该命名空间,如果有人能向我解释一下,那就太好了!)
* 编辑 更正了第二个示例中的语法错误
* 编辑 我的示例中漏掉了 "new" 关键字

先尝试验证您的语法,它看起来不正确 SomeClass = function...。在这里检查 http://www.jshint.com/ - elclanrs
你在第一个例子中忘记了 new - Bergi
1个回答

8

定义方法

这段代码在语法上是不正确的:

SomeNamespace.SomeOtherNamespace = {
     SomeClass = function(oSomeParameter){  // you probably have : instead of =
          this.SomeProperty = oSomeParameter
          ...
     }
 }

在第二个示例中添加实例方法,您只需在SomeClass的定义之后执行以下操作:

SomeNamespace.SomeotherNamespace.SomeClass.prototype.SomeClassMethod = function() {
};

在你提到的第一种和第二种方式中,你的代码想要表明这些函数(第一个例子中的实例方法,第二个例子中的类)都属于同一个对象(第一个例子中的原型,第二个例子中的命名空间)。对于一些属性来说,这是很好的,但是当你处理具有许多方法甚至更糟糕的命名空间的类时,我发现这会更加妨碍。
我建议您使用不同的文件分隔您的代码,并将它们混合在一起。一个文件夹代表一个命名空间,一个文件代表一个类。按照第一个示例中的模式进行操作,但是不要说“这是具有这些方法的原型对象”,而是使用上面的示例行逐个添加它们。
声明命名空间
首先,我们需要理解一点。在JavaScript中,命名空间只是一个对象(其中包含您感兴趣的任何属性,构造函数,静态函数-ex工厂方法,其他命名空间等)。
第一个示例a = a || {}确保命名空间a被定义,但是如果它在其他地方被定义,则确保不覆盖它。对于大多数用例,它已经足够了,并且具有非常简洁和清晰的优点,适合大多数人阅读您的代码。
第二个示例执行与第一个示例类似的操作,但有两个不同之处:
1.在定义a之前特别检查a是否未定义(ex1仅检查了通常足够的falsyness) 2.向a添加_namespace属性
关于undefined的检查,我怀疑您需要它。如果您的代码与将‘a’用作对象以外的其他内容发生冲突,那么无论使用哪种方法,都很有可能会导致某些内容出错。
我认为_namespace属性是纯粹惯用于该代码的东西。它可以帮助各种工具(可能在调试期间或用于自动文档生成等),但这就是我所能想到的。显然,您更有可能看到它是否实际用于某些内容,所以如果您遇到有趣的用法,也许可以留下评论。
总之,我更喜欢第一种变体,因为它更加简洁和更加频繁(因此易于被阅读代码的人识别)。
完整示例:
// class definition
a = a || {}; // global namespace, all good
a.b = a.b || {}; // both lines are needed

a.b.Class = function() {
  this.myProp = 'hello';
};

a.b.Class.prototype.myMethod = function() {
};


// usage
var myInstance = new a.b.Class();
instance.myMethod();
var x = instance.myProp;

谢谢!我会记住的!最后,我应该像示例1还是示例2那样声明我的命名空间呢? - user2727893
很难在评论中解释清楚,所以我会很快更新我的答案。 - Tibos
非常感谢您抽出时间向我详细解释这个问题!回答您的问题,没有理由使用_namespace(我强烈怀疑编写它的人不知道为什么要包含它,因为代码中存在许多不一致之处)。最后一个问题,使用a = a || {};而不是var a = a || {};更好的做法是正确的吗? - user2727893
var a语句在本地作用域中声明变量,而第一个语句在全局作用域中声明变量。如果您希望命名空间仅在特定范围内定义(-在特定函数内部),则需要使用var a。通常情况下不需要这样做,但有一个例外:IIFE(请谷歌它)。由于您已经将所有内容包装在命名空间中,因此不会污染作用域,因此不需要IIFE。 PS:您可能无论如何都在全局作用域中定义命名空间,因此使用/不使用var都是一样的。 - Tibos
再次感谢,但我如何在命名空间中声明属性? 我需要写 SomeNamespace.SomeProperty = "Some Value" 吗? - user2727893
没错。你可以以同样的方式定义命名空间方法(它们只是恰好是函数的属性)。 - Tibos

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