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

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

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


当前回答

我的习惯是使用函数myName()作为属性存储,然后var myName作为“方法”持有者…

不管这是否合法,打我吧!我一直依赖于我的PHP逻辑,事情很简单。: D

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
 (myObj instanceof Function !== false)
 ? Object.create({

     $props: new myObj(),
     fName1: function() { /* code..  */ },
     fName2: function() { /* code ...*/ }
 })
 : console.log('Object creation failed!')
);

if (this !== that) myObj.fName1();其他myObj.fName2 ();

你也可以用“反之亦然”的方式在对象创建之前进行检查,这样更好:

function myObj() {
    this.prop1 = 1;
    this.prop2 = 2;
    this.prop3 = 'string';
}

var myObj = (
    (typeof(myObj) !== "function" || myObj instanceof Function === false)
    ? new Boolean()
    : Object.create({
        $props: new myObj(),
        init: function () { return; },
        fName1: function() { /* code..  */ },
        fName2: function() { /* code ...*/ }
    })
);

if (myObj instanceof Boolean) {
    Object.freeze(myObj);
    console.log('myObj failed!');
    debugger;
}
else
    myObj.init();

JavaScript:使用Object.create()创建对象

其他回答

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 myNamespace = {}
myNamespace._construct = function()
{
    var staticVariable = "This is available to all functions created here"

    function MyClass()
    {
       // Depending on the class, we may build all the classes here
       this.publicMethod = function()
       {
          //Do stuff
       }
    }

    // Alternatively, we may use a prototype.
    MyClass.prototype.altPublicMethod = function()
    {
        //Do stuff
    }

    function privateStuff()
    {
    }

    function publicStuff()
    {
       // Code that may call other public and private functions
    }

    // List of things to place publically
    this.publicStuff = publicStuff
    this.MyClass = MyClass
}
myNamespace._construct()

// The following may or may not be in another file
myNamespace.subName = {}
myNamespace.subName._construct = function()
{
   // Build namespace
}
myNamespace.subName._construct()

外部代码可以是:

var myClass = new myNamespace.MyClass();
var myOtherClass = new myNamepace.subName.SomeOtherClass();
myNamespace.subName.publicOtherStuff(someParameter);

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

在使用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 ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

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

下面是Stoyan Stefanov在他的JavaScript Patterns一书中是如何做到这一点的,我发现这本书非常好(它还展示了他如何做注释,允许自动生成API文档,以及如何向自定义对象的原型添加一个方法):

/**
* My JavaScript application
*
* @module myapp
*/

/** @namespace Namespace for MYAPP classes and functions. */
var MYAPP = MYAPP || {};

/**
* A maths utility
* @namespace MYAPP
* @class math_stuff
*/
MYAPP.math_stuff = {

    /**
    * Sums two numbers
    *
    * @method sum
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} Sum of the inputs
    */
    sum: function (a, b) {
        return a + b;
    },

    /**
    * Multiplies two numbers
    *
    * @method multi
    * @param {Number} a First number
    * @param {Number} b Second number
    * @return {Number} The inputs multiplied
    */
    multi: function (a, b) {
        return a * b;
    }
};

/**
* Constructs Person objects
* @class Person
* @constructor
* @namespace MYAPP
* @param {String} First name
* @param {String} Last name
*/
MYAPP.Person = function (first, last) {

    /**
    * First name of the Person
    * @property first_name
    * @type String
    */
    this.first_name = first;

    /**
    * Last name of the Person
    * @property last_name
    * @type String
    */
    this.last_name = last;
};

/**
* Return Person's full name
*
* @method getName
* @return {String} First name + last name
*/
MYAPP.Person.prototype.getName = function () {
    return this.first_name + ' ' + this.last_name;
};