Javascript OOP最佳实践?

Javascript OOP best practices?

我讨厌看到用JavaScript进行面向对象编程的许多不同方法。有人能告诉我,考虑到我想在一个大型项目上工作,我想让我的代码成为未来的证据,我应该使用哪种技术吗?


这些只是我提出的一些快速指导原则,如果其他人有任何有意义的内容需要添加,我将这个答案设置为社区wiki,这样您就可以轻松地进行编辑。

  • 为对象命名以确保它们不会与第三方JavaScript库冲突。
    1
    2
    3
    4
    window['Andrew']['JS'] = {
        addEvent: function(el,evName) {/*Stuff*/},
        Rectangle: function(width,height) {/*Stuff*/}
    };

    因此,您可以使用以下方法创建一个矩形对象:

    1
    var myRect = new Andrew.JS.Rectangle(14,11);

    然后,您的代码将永远不会干扰或被其他任何人的Rectangle干扰。

  • 使用一致的命名策略,特别是:
    • 对象名称应大写,其他所有内容(变量、函数)应以小写字符开头,即
      1
      2
      var myRect = new Andrew.JS.Rectangle(14,11);
      document.write(myRect.getArea());
    • 确保每件事都有意义,即方法的动词,参数的名词+形容词。
  • 确保所有方法和参数都与它们所属的对象相关。例如在本例中,可以使用inSquareFeet()方法将矩形的面积转换为平方英尺。
    1
    myRect.getAreaObject().inSquareFeet();

    确保insquarefeet是由getAreaObject()返回的对象的方法,而不是Andrew.JS.Rectangle的方法。

  • 使用构造函数,或者更具体地说,尽量确保对象在构造后不需要任何进一步的初始化,因此,不要:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    var Person = function()
    {
        this.name ="";
        this.sayHello = function ()
        {
            alert(this.name +" says 'Hello!'");
            return this;
        }
    }

    var bob = new Person();
    bob.name ="Bob Poulton";
    bob.sayHello();

    尝试:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var Person = function(name)
    {
        this.name = name;
        this.sayHello = function ()
        {
            alert(this.name +" says 'Hello!'");
            return this;
        }
    }

    var bob = new Person("Bob Poulton");
    bob.sayHello();

  • 我总是使用John Resig的:

    http://ejohn.org/blog/simple-javascript-inheritance/

    它很简单,不需要任何框架来运行。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    //Create and define Global NameSpace Object
    ( function(GlobalObject, $, undefined)
    {
        GlobalObject.Method = function()
        {
            ///<summary></summary>
        }

        GlobalObject.Functionality = {};

    }) (GlobalObject = GlobalObject || {}, jQuery);

    //New object for specific functionality
    ( function(Events, $, undefined)
    {
        //Member Variables
        var Variable; // (Used for) , (type)

        // Initialize
        Events.Init = function()
        {
            ///<summary></summary>
        }

        // public method
        Events.PublicMethod = function(oParam)
        {
            ///<summary></summary>
            ///<param type=""></param>
        }

        // protected method (typically define in global object, but can be made available from here)
        GlobalObject.Functionality.ProtectedMethod = function()
        {
            ///<summary></summary>
        }

        // internal method (typically define in global object, but can be made available from here)
        GlobalObject.InternalMethod = function()
        {
            ///<summary></summary>
        }

        // private method
        var privateMethod = function()
        {
            ///<summary></summary>
        }
    }) (GlobalObject.Funcitonality.Events = GlobalObject.Funcitonality.Events || {}, jQuery )

    // Reusable"class" object
    var oMultiInstanceClass = function()
    {
        // Memeber Variables again
        var oMember = null; //

        // Public method
        this.Init = function(oParam)
        {
            oMember = oParam;

            for ( n = 1; i < oMemeber.length; i += 1 )
            {
               new this.SubClass.Init(oMember[i]); // you get the point, yeah?
            }
        }

        this.Subclass = function()
        {
            this.Init = function() { }
        }
    }

    其优点是它自动初始化全局对象,允许您维护代码的完整性,并根据定义将每一项功能组织成特定的分组。

    这个结构是可靠的,它提供了所有基本的语法方面的东西,您可以在没有关键字的情况下从OOP中得到这些东西。

    甚至还有一些巧妙的方法来设置接口。如果你选择这么做,一个简单的搜索会给你一些好的教程和提示。

    甚至可以使用JavaScript和Visual Studio设置IntelliSense,然后定义每一个部分并引用它们,这样就可以使编写JavaScript更清晰、更易于管理。

    根据您的情况使用这三种方法有助于保持全局名称空间的整洁,保持代码的有序性,并保持每个对象的关注点分离。如果使用正确。记住,如果不利用对象背后的逻辑,那么面向对象的设计是没有用的!


    仅供参考,我认为Yui提供了一些关于这个主题的很好的教程


    我对OOP的理想对象就像使用一个带有原型的实例方法:

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    var Users = function()
    {
        var _instance;
        this.prototype.getUsername = function(){/*...*/}
        this.prototype.getFirstname = function(){/*...*/}
        this.prototype.getSecurityHash = function(){/*...*/}
        /*...*/

        /*Static Methods as such*/
        return { /*Return a small Object*/
            GetInstance : function()
            {
                if(_instance == null)
                {
                    _instnance = new Users(arguments);
                }
                return _instnance; //Return the object
            },
            New: function()
            {
                _instnance = null; //unset It
                return this.GetInstnace(arguments);
            }
        }
    }

    然后我总是使用如下:

    1
    2
    3
    4
    5
    6
    Firstname = Users.GetInstance('Robert','Pitt').getFirstname();
    Username  = Users.GetInstance().getUsername(); //Returns the above object.

    Me  = Users.New('Robert',null); //Deletes the above object and creates a new instance.
    Father = Users.New('Peter','Piper'); //New Object
    Me.AddFather(Father); //Me Object.

    这就是我在构建一个JavascriptOO风格架构时所走的路。


    因为您正在进行一个大型项目,我建议您使用一个类似mooolts的javascript框架http://mooolts.net/。

    它具有良好的类和继承结构。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    function foo() {
      var bar = function() { console.log("i'm a private method"); return 1; };
      var iAmAPrivateVariable = 1;

      return {
        publicMethod: function() { alert(iAmAPrivateVariable); },
        publicVariable: bar()
      }
    }

    //usage
    var thing = foo()

    这是一种功能性的方法,它比您将看到的任何其他方法(如封装)都更适合它。

    一般来说,你不应该在JavaScript中使用OO,因为很多原因,它不是一种很好的语言。想想这个带有歪斜括号和分号的方案,你将开始像专业人士那样编写语言。也就是说,有时候OO更适合。在这些情况下,上述通常是最佳选择

    把遗产混为一谈

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function parent() {
      return { parentVariable: 2 };
    }

    function foo() {
      var bar = function() { console.log("i'm a private method"); return 1; };
      var iAmAPrivateVariable = 1;

      me = parent();
      me.publicMethod = function() { alert(iAmAPrivateVariable); };
      me.publicVariable = bar();

      return me;
    }

    这使得事情稍微复杂一点,但实现了预期的最终结果,同时仍然采用面向对象概念的功能性方法(在本例中,使用decorator函数而不是真正的继承)。我喜欢整个方法的一点是,我们仍然以这种语言来处理对象——一个可以随意附加东西的属性包。

    另一个需要注意的是,这与你将要从事的大多数工作中的大部分时间都会看到的截然不同,而且通常很难解释a)发生了什么,b)为什么对同事来说这是个好主意。


    我使用这种模式,并建议您也使用它:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    function Person(firstname, lastname, age)
    {
      var self = this;
      var _ = {};

      // Private members.  
      var firstname  = firstname;
      var lastname = lastname;
      var age = age || 'unknown';

      // Private methods.
      function first_letter_to_uppercase(str) {
        return str.charAt(0).toUpperCase() + str.substr(1);
      }

      // Public members and methods.
      _.get_age = function()
      {
        return age;
      }

      _.get_name = function()
      {
        return first_letter_to_uppercase(firstname) + ' ' +
          first_letter_to_uppercase(lastname);
      }
      return _;
    }

    var p = new Person('vasya', 'pupkin', 23);
    alert("It's"  + p.get_name() + ', he is ' + p.get_age() + ' years old.')


    您可以尝试使用一个简单、有用和快速的对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var foo = {
        foo1: null,
        foo2: true,
        foo3: 24,
        foo4: new Array(),

        nameOfFunction1: function(){
          alert("foo1");
        },

        nameOfFunction2: function(){
          alert("foo2");
        },
    }

    要使用这个,必须创建这个对象的实例,并像Java中的对象一样使用:

    1
    foo.nameOfFunction2();

    您还可以查看其他解决方案的链接:http://www.javascriptkit.com/javautors/oopjs.shtml

    我希望能回答你的问题。