如何在JavaScript中创建命名空间,使我的对象和函数不被其他同名对象和函数覆盖?我使用了以下方法:

if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();}

有没有更优雅或更简洁的方式来做到这一点?


当前回答

我通常在闭包中构建它:

var MYNS = MYNS || {};

MYNS.subns = (function() {

    function privateMethod() {
        // Do private stuff, or build internal.
        return "Message";
    }

    return {
        someProperty: 'prop value',
        publicMethod: function() {
            return privateMethod() + " stuff";
        }
    };
})();

自从写了这篇文章以来,我的风格在这些年里有了微妙的变化,现在我发现自己是这样写闭包的:

var MYNS = MYNS || {};

MYNS.subns = (function() {
    var internalState = "Message";

    var privateMethod = function() {
        // Do private stuff, or build internal.
        return internalState;
    };
    var publicMethod = function() {
        return privateMethod() + " stuff";
    };

    return {
        someProperty: 'prop value',
        publicMethod: publicMethod
    };
})();

通过这种方式,我发现公共API和实现更容易理解。将return语句视为实现的公共接口。

其他回答

另一种方式是,我认为它比对象文字形式限制少一点,是这样的:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

上面的模式非常类似于模块模式,不管你喜欢与否,它允许你将所有函数公开为public,同时避免了对象字面量的刚性结构。

我创建的命名空间是受到Erlang模块的启发。这是一种非常实用的方法,但这就是我最近编写JavaScript代码的方式。

它为闭包提供全局名称空间,并在闭包中公开已定义的集合函数。

(function(){

  namespace("images", previous, next);
  // ^^ This creates or finds a root object, images, and binds the two functions to it.
  // It works even though those functions are not yet defined.

  function previous(){ ... }

  function next(){ ... }

  function find(){ ... } // A private function

})();

如果你需要私有作用域:

var yourNamespace = (function() {

  //Private property
  var publicScope = {};

  //Private property
  var privateProperty = "aaa"; 

  //Public property
  publicScope.publicProperty = "bbb";

  //Public method
  publicScope.publicMethod = function() {
    this.privateMethod();
  };

  //Private method
  function privateMethod() {
    console.log(this.privateProperty);
  }

  //Return only the public parts
  return publicScope;
}());

yourNamespace.publicMethod();

否则,如果你永远不会使用私有作用域:

var yourNamespace = {};

yourNamespace.publicMethod = function() {
    // Do something...
};

yourNamespace.publicMethod2 = function() {
    // Do something...
};

yourNamespace.publicMethod();

模块模式最初被定义为在传统软件工程中为类提供私有和公共封装的一种方式。

在使用Module模式时,我们可能会发现定义一个简单的模板来开始使用它是很有用的。下面是一个涵盖了名称间距、公共变量和私有变量的例子。

在JavaScript中,Module模式被用来进一步模拟类的概念,这样我们就可以在单个对象中同时包含公共/私有方法和变量,从而将特定部分从全局作用域中屏蔽掉。这样做的结果是降低了我们的函数名与页面上其他脚本中定义的其他函数冲突的可能性。

var myNamespace = (function () {

  var myPrivateVar, myPrivateMethod;

  // A private counter variable
  myPrivateVar = 0;

  // A private function which logs any arguments
  myPrivateMethod = function( foo ) {
      console.log( foo );
  };

  return {

    // A public variable
    myPublicVar: "foo",

    // A public function utilizing privates
    myPublicFunction: function( bar ) {

      // Increment our private counter
      myPrivateVar++;

      // Call our private method using bar
      myPrivateMethod( bar );

    }
  };

})();

优势

为什么模块模式是一个很好的选择?首先,对于来自面向对象背景的开发人员来说,它比真正的封装思想要简洁得多,至少从JavaScript的角度来看是这样。

其次,它支持私有数据——因此,在模块模式中,代码的公共部分能够触及私有部分,而外部世界无法触及类的私有部分。

缺点

Module模式的缺点是,由于我们以不同的方式访问公共成员和私有成员,当我们希望更改可见性时,实际上必须对使用成员的每个位置进行更改。

我们也不能访问稍后添加到对象的方法中的私有成员。也就是说,在许多情况下,模块模式仍然是非常有用的,如果使用正确,肯定有潜力改善我们的应用程序的结构。

揭示模块模式

现在我们对模块模式有了一些熟悉,让我们来看看稍微改进的版本——Christian Heilmann的揭示模块模式。

当我们想要从一个公共方法调用另一个公共方法或访问公共变量时,他不得不重复主对象的名称,这使Heilmann感到沮丧,因此出现了揭示模块模式。他也不喜欢模块模式的要求,因为他希望公开的东西必须切换到对象文字符号。

他努力的结果是一种更新的模式,在这种模式中,我们只需在私有作用域中定义所有函数和变量,并返回一个匿名对象,该对象带有指向我们希望显示为公共的私有功能的指针。

下面是如何使用reveal Module模式的示例

var myRevealingModule = (function () {

        var privateVar = "Ben Cherry",
            publicVar = "Hey there!";

        function privateFunction() {
            console.log( "Name:" + privateVar );
        }

        function publicSetName( strName ) {
            privateVar = strName;
        }

        function publicGetName() {
            privateFunction();
        }


        // Reveal public pointers to
        // private functions and properties

        return {
            setName: publicSetName,
            greeting: publicVar,
            getName: publicGetName
        };

    })();

myRevealingModule.setName( "Paul Kinlan" );

优势

这种模式使得脚本的语法更加一致。在模块的末尾,它还使哪些函数和变量可以被公开访问更加清楚,从而降低了可读性。

缺点

这种模式的一个缺点是,如果一个私有函数引用了一个公共函数,那么如果需要一个补丁,这个公共函数就不能被覆盖。这是因为私有函数将继续引用私有实现,并且该模式不应用于公共成员,只应用于函数。

引用私有变量的公共对象成员也遵循上面的无补丁规则。

我最近最喜欢的图案是这样的:

Var命名空间=(函数(){ //暴露给公众 返回{ internalA, c: internalC } //所有私有 /** *完整的JSDoc * / 函数internalA() { / /…… } /** *完整的JSDoc * / 函数internalB() { / /…… } /** *完整的JSDoc * / 函数internalC() { / /…… } /** *完整的JSDoc * / 函数internalD() { / /…… } }) ();

当然,return可以在后面,但是如果后面只有函数声明,那么就更容易看到名称空间是关于什么的,以及暴露了什么API。

在这种情况下使用函数表达式的模式导致不遍历整个代码就无法知道公开了哪些方法。