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

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

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


当前回答

这是user106826到Namespace.js的链接的后续。该项目似乎转移到了GitHub上。现在是smith/namespacedotjs。

我一直在我的小项目中使用这个简单的JavaScript助手,到目前为止,它似乎很轻,但足够通用,可以处理命名空间和加载模块/类。如果它允许我将包导入到我选择的名称空间中,而不仅仅是全局名称空间,那就太好了……唉,但这不是重点。

它允许你声明命名空间,然后在该命名空间中定义对象/模块:

Namespace('my.awesome.package');
my.awesome.package.WildClass = {};

另一种选择是立即声明名称空间及其内容:

Namespace('my.awesome.package', {
    SuperDuperClass: {
        saveTheDay: function() {
            alert('You are welcome.');
        }
    }
});

要了解更多用法示例,请查看源代码中的example.js文件。

其他回答

我创建的命名空间是受到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

})();

JavaScript默认情况下不支持命名空间。因此,如果您创建任何元素(函数、方法、对象、变量),那么它将变成全局的,并污染全局命名空间。让我们以定义两个没有任何名称空间的函数为例,

function func1() {
    console.log("This is a first definition");

}
function func1() {
    console.log("This is a second definition");
}
func1(); // This is a second definition

它总是调用第二个函数定义。在这种情况下,命名空间将解决名称冲突问题。

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

是的。例如:

var your_namespace = your_namespace || {};

然后你就可以

var your_namespace = your_namespace || {};
your_namespace.Foo = {toAlert:'test'};
your_namespace.Bar = function(arg) 
{
    alert(arg);
};
with(your_namespace)
{
   Bar(Foo.toAlert);
}

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

在使用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" );

优势

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

缺点

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

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

JavaScript还没有命名空间的原生表示,但TypeScript有。

例如,您可以使用以下TS代码(playground)

namespace Stack {
    export const hello = () => console.log('hi')
}

Stack.hello()

如果你不能将你的代码更新到TS,你至少可以使用TS在为命名空间生成JS输出时所采用的模式,它看起来像这样:

var Stack;
(function (Stack) {
    Stack.hello = () => console.log('hi');
})(Stack || (Stack = {}));
Stack.hello();

进一步阅读:

TS -命名空间 命名空间和模块