代码之家  ›  专栏  ›  技术社区  ›  Karim

在javascript中可以使用哪些技术来定义类,它们的权衡是什么?

  •  674
  • Karim  · 技术社区  · 16 年前

    我更喜欢在大型项目中使用OOP,比如我现在正在研究的项目。我需要用JavaScript创建几个类,但如果我没有弄错的话,至少有两种方法可以做到这一点。语法是什么?为什么要这样做?

    我想避免使用第三方库——至少在一开始是这样。
    在寻找其他答案时,我找到了这篇文章 Object-Oriented Programming with JavaScript, Part I: Inheritance - Doc JavaScript 讨论了JavaScript中的面向对象编程。有更好的继承方式吗?

    19 回复  |  直到 6 年前
        1
  •  730
  •   Kenan Banks    7 年前

    以下是不使用任何外部库的方法:

    // Define a class like this
    function Person(name, gender){
    
       // Add object properties like this
       this.name = name;
       this.gender = gender;
    }
    
    // Add methods like this.  All Person objects will be able to invoke this
    Person.prototype.speak = function(){
        alert("Howdy, my name is" + this.name);
    };
    
    // Instantiate new objects with 'new'
    var person = new Person("Bob", "M");
    
    // Invoke methods like this
    person.speak(); // alerts "Howdy, my name is Bob"
    

    现在真正的答案要比这复杂得多。例如,JavaScript中没有类。javascript使用 prototype -基于继承方案。

    此外,还有许多流行的JavaScript库,它们都有自己的风格来近似JavaScript中类功能。你至少要退房 Prototype jQuery .

    决定哪一个是“最好的”,是一个伟大的方式开始一场圣战堆叠溢出。如果你正在着手一个更大的JavaScript重的项目,那绝对值得学习一个流行的库并按他们的方式来做。我是一个原型,但Stack Overflow似乎倾向于jQuery。

    因为只有“一种方法”,不依赖于外部库,所以我写的方法差不多就是它。

        2
  •  211
  •   Jörg W Mittag    16 年前

    在JavaScript中定义类的最佳方法是不定义类。

    说真的。

    对象方向有几种不同的风格,其中一些是:

    • 基于类的OO(首先由Smalltalk介绍)
    • 基于原型的OO(首先由Self介绍)
    • 基于多方法的OO(我认为首先由CommonLoops引入)
    • 基于谓词的OO(不知道)

    可能还有其他我不认识的人。

    JavaScript实现了基于原型的OO。在基于原型的OO中,通过复制其他对象(而不是从类模板实例化)来创建新对象,方法直接存在于对象中而不是类中。继承是通过委托完成的:如果一个对象没有方法或属性,它将在其原型(即从中克隆的对象)上进行查找,然后在原型的原型上进行查找,依此类推。

    换句话说:没有课程。

    实际上,javascript对该模型有一个很好的调整:构造器。可以说,您不仅可以通过复制现有对象来创建对象,还可以“凭空”构建它们。如果使用 new 关键字,该函数将成为构造函数, this 关键字不会指向当前对象,而是指向新创建的“空”对象。所以,您可以任意配置对象。通过这种方式,JavaScript构造函数可以承担传统的基于类的OO中类的角色之一:充当新对象的模板或蓝图。

    现在,javascript是一种非常强大的语言,因此很容易实现基于类的OO系统。 在javascript中 如果你想的话。但是,如果你真的需要它,而不仅仅是因为Java是这样做的,你就应该这样做。

        3
  •  78
  •   Dale    7 年前

    ES2015课程

    在ES2015规范中,您可以使用类语法,它只是原型系统的一部分。

    class Person {
      constructor(name) {
        this.name = name;
      }
      toString() {
        return `My name is ${ this.name }.`;
      }
    }
    
    class Employee extends Person {
      constructor(name, hours) {
        super(name);
        this.hours = hours;
      }
      toString() {
        return `${ super.toString() } I work ${ this.hours } hours.`;
      }
    }
    

    效益

    主要的好处是静态分析工具发现更容易针对这种语法。对于来自基于类的语言的其他人来说,将该语言用作多语种语言也更容易。

    告诫

    小心它目前的局限性。要想获得私有财产,必须求助于 using Symbols or WeakMaps . 在未来的版本中,类很可能会扩展到包含这些缺失的特性。

    支持

    Browser support 目前不是很好(除了IE,几乎所有人都支持),但是现在您可以使用类似蒸腾器的这些功能 Babel .

    资源

        4
  •  56
  •   Daniel X Moore mcdon    14 年前

    我喜欢用丹尼尔X摩尔的 {SUPER: SYSTEM} .这是一个提供诸如真实实例变量、基于特征的继承、类层次结构和配置选项等好处的规程。下面的例子说明了使用真正的实例变量,我认为这是最大的优势。如果您不需要实例变量,只使用公共或私有变量,那么可能会有更简单的系统。

    function Person(I) {
      I = I || {};
    
      Object.reverseMerge(I, {
        name: "McLovin",
        age: 25,
        homeState: "Hawaii"
      });
    
      return {
        introduce: function() {
          return "Hi I'm " + I.name + " and I'm " + I.age;
        }
      };
    }
    
    var fogel = Person({
      age: "old enough"
    });
    fogel.introduce(); // "Hi I'm McLovin and I'm old enough"
    

    哇,这本身并不是很有用,但是看看添加子类:

    function Ninja(I) {
      I = I || {};
    
      Object.reverseMerge(I, {
        belt: "black"
      });
    
      // Ninja is a subclass of person
      return Object.extend(Person(I), {
        greetChallenger: function() {
          return "In all my " + I.age + " years as a ninja, I've never met a challenger as worthy as you...";
        }
      });
    }
    
    var resig = Ninja({name: "John Resig"});
    
    resig.introduce(); // "Hi I'm John Resig and I'm 25"
    

    另一个优势是拥有模块和基于特征的继承的能力。

    // The Bindable module
    function Bindable() {
    
      var eventCallbacks = {};
    
      return {
        bind: function(event, callback) {
          eventCallbacks[event] = eventCallbacks[event] || [];
    
          eventCallbacks[event].push(callback);
        },
    
        trigger: function(event) {
          var callbacks = eventCallbacks[event];
    
          if(callbacks && callbacks.length) {
            var self = this;
            callbacks.forEach(function(callback) {
              callback(self);
            });
          }
        },
      };
    }
    

    包含Person类的示例包括可绑定模块。

    function Person(I) {
      I = I || {};
    
      Object.reverseMerge(I, {
        name: "McLovin",
        age: 25,
        homeState: "Hawaii"
      });
    
      var self = {
        introduce: function() {
          return "Hi I'm " + I.name + " and I'm " + I.age;
        }
      };
    
      // Including the Bindable module
      Object.extend(self, Bindable());
    
      return self;
    }
    
    var person = Person();
    person.bind("eat", function() {
      alert(person.introduce() + " and I'm eating!");
    });
    
    person.trigger("eat"); // Blasts the alert!
    

    披露:我是丹尼尔·X·摩尔,这是我的 {Super:系统} . 这是用JavaScript定义类的最佳方法。

        5
  •  41
  •   liammclennan    15 年前
    var Animal = function(options) {
        var name = options.name;
        var animal = {};
    
        animal.getName = function() {
            return name;
        };
    
        var somePrivateMethod = function() {
    
        };
    
        return animal;
    };
    
    // usage
    var cat = Animal({name: 'tiger'});
    
        6
  •  29
  •   Amol M Kulkarni    7 年前

    下面是在javascript中创建对象的方法,到目前为止我已经使用了这些方法

    例1:

    obj = new Object();
    obj.name = 'test';
    obj.sayHello = function() {
        console.log('Hello '+ this.name);
    }
    

    例2:

    obj = {};
    obj.name = 'test';
    obj.sayHello = function() {
        console.log('Hello '+ this.name);
    }
    obj.sayHello();
    

    例3:

    var obj = function(nameParam) {
        this.name = nameParam;
    }
    obj.prototype.sayHello = function() {
        console.log('Hello '+ this.name);
    }
    

    例4: Actual benefits of Object.create(). please refer [this link]

    var Obj = {
        init: function(nameParam) {
            this.name = nameParam;
        },
        sayHello: function() {
            console.log('Hello '+ this.name);
        }
    };
    var usrObj = Object.create(Obj);  // <== one level of inheritance
    
    usrObj.init('Bob');
    usrObj.sayHello();
    

    示例5(自定义crockford的object.create):

    Object.build = function(o) {
       var initArgs = Array.prototype.slice.call(arguments,1)
       function F() {
          if((typeof o.init === 'function') && initArgs.length) {
             o.init.apply(this,initArgs)
          }
       }
       F.prototype = o
       return new F()
    }
    MY_GLOBAL = {i: 1, nextId: function(){return this.i++}}  // For example
    
    var userB = {
        init: function(nameParam) {
            this.id = MY_GLOBAL.nextId();
            this.name = nameParam;
        },
        sayHello: function() {
            console.log('Hello '+ this.name);
        }
    };
    var bob = Object.build(userB, 'Bob');  // Different from your code
    bob.sayHello();
    


    用ES6/ES2015更新答案

    类的定义如下:

    class Person {
        constructor(strName, numAge) {
            this.name = strName;
            this.age = numAge;
        }
    
        toString() {
            return '((Class::Person) named ' + this.name + ' & of age ' + this.age + ')';
        }
    }
    
    let objPerson = new Person("Bob",33);
    console.log(objPerson.toString());
    
        7
  •  24
  •   community wiki Jarek    16 年前

    我想你应该读道格拉斯·克罗克福德的 Prototypal Inheritance in JavaScript Classical Inheritance in JavaScript .

    本页示例:

    Function.prototype.method = function (name, func) {
        this.prototype[name] = func;
        return this;
    };
    

    效果?它将允许您以更优雅的方式添加方法:

    function Parenizor(value) {
        this.setValue(value);
    }
    
    Parenizor.method('setValue', function (value) {
        this.value = value;
        return this;
    });
    

    我还推荐他的视频: Advanced JavaScript .

    您可以在他的页面上找到更多视频: http://javascript.crockford.com/ 在JohnReisig的书中,你可以从道格拉斯·克罗克福的网站上找到很多例子。

        8
  •  16
  •   annakata    16 年前

    因为我不接受Yui/Crockford工厂计划,而且因为我喜欢保持事物的独立性和可扩展性,这是我的变体:

    function Person(params)
    {
      this.name = params.name || defaultnamevalue;
      this.role = params.role || defaultrolevalue;
    
      if(typeof(this.speak)=='undefined') //guarantees one time prototyping
      {
        Person.prototype.speak = function() {/* do whatever */};
      }
    }
    
    var Robert = new Person({name:'Bob'});
    

    在理想的情况下,测试类型是基于类似于第一个原型方法的

        9
  •  15
  •   Hearaman    11 年前

    如果你想简单点,你可以完全避免“new”关键字,只使用工厂方法。有时我更喜欢这样,因为我喜欢使用JSON创建对象。

    function getSomeObj(var1, var2){
      var obj = {
         instancevar1: var1,
         instancevar2: var2,
         someMethod: function(param)
         {  
              //stuff; 
         }
      };
      return obj;
    }
    
    var myobj = getSomeObj("var1", "var2");
    myobj.someMethod("bla");
    

    不过,我不确定大型对象的性能影响是什么。

        10
  •  12
  •   Mick    8 年前
    var Student = (function () {
        function Student(firstname, lastname) {
            this.firstname = firstname;
            this.lastname = lastname;
            this.fullname = firstname + " " + lastname;
        }
    
        Student.prototype.sayMyName = function () {
            return this.fullname;
        };
    
        return Student;
    }());
    
    var user = new Student("Jane", "User");
    var user_fullname = user.sayMyName();
    

    这就是typescript用构造函数将类编译为javascript的方式。

        11
  •  10
  •   orip    16 年前

    简单的方法是:

    function Foo(a) {
      var that=this;
    
      function privateMethod() { .. }
    
      // public methods
      that.add = function(b) {
        return a + b;
      };
      that.avg = function(b) {
        return that.add(b) / 2; // calling another public method
      };
    }
    
    var x = new Foo(10);
    alert(x.add(2)); // 12
    alert(x.avg(20)); // 15
    

    原因 that 那是 this 如果将方法作为事件处理程序提供,则可以绑定到其他对象,因此可以在实例化期间保存该值,并在以后使用它。

    编辑:这绝对不是最好的方法,只是一个简单的方法。我也在等待好的答案!

        12
  •  9
  •   intrepidis    11 年前

    您可能希望使用折叠模式创建类型:

        // Here is the constructor section.
        var myType = function () {
            var N = {}, // Enclosed (private) members are here.
                X = this; // Exposed (public) members are here.
    
            (function ENCLOSED_FIELDS() {
                N.toggle = false;
                N.text = '';
            }());
    
            (function EXPOSED_FIELDS() {
                X.count = 0;
                X.numbers = [1, 2, 3];
            }());
    
            // The properties below have access to the enclosed fields.
            // Careful with functions exposed within the closure of the
            // constructor, each new instance will have it's own copy.
            (function EXPOSED_PROPERTIES_WITHIN_CONSTRUCTOR() {
                Object.defineProperty(X, 'toggle', {
                    get: function () {
                        var before = N.toggle;
                        N.toggle = !N.toggle;
                        return before;
                    }
                });
    
                Object.defineProperty(X, 'text', {
                    get: function () {
                        return N.text;
                    },
                    set: function (value) {
                        N.text = value;
                    }
                });
            }());
        };
    
        // Here is the prototype section.
        (function PROTOTYPE() {
            var P = myType.prototype;
    
            (function EXPOSED_PROPERTIES_WITHIN_PROTOTYPE() {
                Object.defineProperty(P, 'numberLength', {
                    get: function () {
                        return this.numbers.length;
                    }
                });
            }());
    
            (function EXPOSED_METHODS() {
                P.incrementNumbersByCount = function () {
                    var i;
                    for (i = 0; i < this.numbers.length; i++) {
                        this.numbers[i] += this.count;
                    }
                };
                P.tweak = function () {
                    if (this.toggle) {
                        this.count++;
                    }
                    this.text = 'tweaked';
                };
            }());
        }());
    

    该代码将为您提供一个名为 MyType . 它将有内部私有字段调用 切换 文本 . 它还将具有这些公开的成员:字段 计数 数字 ;属性 切换 , 文本 数字长度 方法 递增数字ByCount .

    折叠图案在这里详细说明: Javascript Folding Pattern

        13
  •  3
  •   Community rohancragg    7 年前

    @liammclennan's的代码高尔夫 answer .

    var Animal = function (args) {
      return {
        name: args.name,
    
        getName: function () {
          return this.name; // member access
        },
    
        callGetName: function () {
          return this.getName(); // method call
        }
      };
    };
    
    var cat = Animal({ name: 'tiger' });
    console.log(cat.callGetName());
        14
  •  2
  •   Ryan Florence    15 年前

    MooTools (我的面向对象工具)集中在 classes . 甚至可以使用继承来扩展和实现。

    当掌握后,它会产生荒谬的可重用、强大的javascript。

        15
  •  2
  •   Ulad Kasach    8 年前

    具有继承性的基于对象的类

    var baseObject = 
    {
         // Replication / Constructor function
         new : function(){
             return Object.create(this);   
         },
    
        aProperty : null,
        aMethod : function(param){
          alert("Heres your " + param + "!");
        },
    }
    
    
    newObject = baseObject.new();
    newObject.aProperty = "Hello";
    
    anotherObject = Object.create(baseObject); 
    anotherObject.aProperty = "There";
    
    console.log(newObject.aProperty) // "Hello"
    console.log(anotherObject.aProperty) // "There"
    console.log(baseObject.aProperty) // null
    

    简单,甜蜜,完成任务。

        16
  •  1
  •   Mikael Dúi Bolinder    7 年前

    底座

    function Base(kind) {
        this.kind = kind;
    }
    

    一个班

    // Shared var
    var _greeting;
    
    (function _init() {
        Class.prototype = new Base();
        Class.prototype.constructor = Class;
        Class.prototype.log = function() { _log.apply(this, arguments); }
        _greeting = "Good afternoon!";
    })();
    
    function Class(name, kind) {
        Base.call(this, kind);
        this.name = name;
    }
    
    // Shared function
    function _log() {
        console.log(_greeting + " Me name is " + this.name + " and I'm a " + this.kind);
    }
    

    行动

    var c = new Class("Joe", "Object");
    c.log(); // "Good afternoon! Me name is Joe and I'm a Object"
    
        17
  •  1
  •   Roland    7 年前

    基于Triptych的例子,这可能更简单:

        // Define a class and instantiate it
        var ThePerson = new function Person(name, gender) {
            // Add class data members
            this.name = name;
            this.gender = gender;
            // Add class methods
            this.hello = function () { alert('Hello, this is ' + this.name); }
        }("Bob", "M"); // this instantiates the 'new' object
    
        // Use the object
        ThePerson.hello(); // alerts "Hello, this is Bob"
    

    这只创建一个对象实例,但是如果您想在一个类中封装变量和方法的一组名称,它仍然有用。通常,构造函数不会有“bob,m”参数,例如,如果方法将被调用到具有自己数据的系统,例如数据库或网络。

    我对JS还太陌生,不明白为什么它不使用 prototype 事情。

        18
  •  0
  •   Peter Mortensen Pieter Jan Bonestroo    11 年前

    JavaScript是 object-oriented 但是它和其他的完全不同 OOP 像Java、C语言或C++这样的语言。不要那样理解它。抛开旧知识,重新开始。JavaScript需要不同的思考。

    我建议你找一本好的手册或是有关这个问题的东西。 我自己发现 ExtJS Tutorials 对我来说是最好的,尽管我在阅读之前或之后都没有使用这个框架。但它确实很好地解释了什么是JavaScript世界。 抱歉,内容似乎已被删除。这里有一个链接指向 archive.org copy 相反。今天工作。P

        19
  •  0
  •   Avinash Maurya    6 年前

    //new way using this and new
    function Persons(name) {
      this.name = name;
      this.greeting = function() {
        alert('Hi! I\'m ' + this.name + '.');
      };
    }
    
    var gee=new Persons("gee");
    gee.greeting();
    
    var gray=new Persons("gray");
    gray.greeting();
    
    //old way
    function createPerson(name){
     var obj={};
     obj.name=name;
     obj.greeting = function(){
     console.log("hello I am"+obj.name);
     }; 
      return obj;
    }
    
    var gita=createPerson('Gita');
    gita.greeting();