What techniques can be used to define a class in JavaScript, and what are their trade-offs?

后端 未结 19 1454
庸人自扰
庸人自扰 2020-11-22 07:26

I prefer to use OOP in large scale projects like the one I\'m working on right now. I need to create several classes in JavaScript but, if I\'m not mistaken, there are at le

相关标签:
19条回答
  • 2020-11-22 08:03

    I prefer to use Daniel X. Moore's {SUPER: SYSTEM}. This is a discipline that provides benefits such as true instance variables, trait based inheritance, class hierarchies and configuration options. The example below illustrates the use of true instance variables, which I believe is the biggest advantage. If you don't need instance variables and are happy with only public or private variables then there are probably simpler systems.

    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"
    

    Wow, that's not really very useful on it's own, but take a look at adding a subclass:

    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"
    

    Another advantage is the ability to have modules and trait based inheritance.

    // 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);
            });
          }
        },
      };
    }
    

    An example of having the person class include the bindable module.

    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!
    

    Disclosure: I am Daniel X. Moore and this is my {SUPER: SYSTEM}. It is the best way to define a class in JavaScript.

    0 讨论(0)
  • 2020-11-22 08:03

    Following are the ways to create objects in javascript, which I've used so far

    Example 1:

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

    Example 2:

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

    Example 3:

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

    Example 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();
    

    Example 5 (customised Crockford's 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();
    


    To keep answer updated with ES6/ ES2015

    A class is defined like this:

    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());
    
    0 讨论(0)
  • 2020-11-22 08:04

    If you're going for simple, you can avoid the "new" keyword entirely and just use factory methods. I prefer this, sometimes, because I like using JSON to create objects.

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

    I'm not sure what the performance hit is for large objects, though.

    0 讨论(0)
  • 2020-11-22 08:05

    Here's the way to do it without using any external libraries:

    // 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"
    

    Now the real answer is a whole lot more complex than that. For instance, there is no such thing as classes in JavaScript. JavaScript uses a prototype-based inheritance scheme.

    In addition, there are numerous popular JavaScript libraries that have their own style of approximating class-like functionality in JavaScript. You'll want to check out at least Prototype and jQuery.

    Deciding which of these is the "best" is a great way to start a holy war on Stack Overflow. If you're embarking on a larger JavaScript-heavy project, it's definitely worth learning a popular library and doing it their way. I'm a Prototype guy, but Stack Overflow seems to lean towards jQuery.

    As far as there being only "one way to do it", without any dependencies on external libraries, the way I wrote is pretty much it.

    0 讨论(0)
  • ES2015 Classes

    In the ES2015 specification, you can use the class syntax which is just sugar over the prototype system.

    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.`;
      }
    }
    

    Benefits

    The main benefit is that static analysis tools find it easier to target this syntax. It is also easier for others coming from class-based languages to use the language as a polyglot.

    Caveats

    Be wary of its current limitations. To achieve private properties, one must resort to using Symbols or WeakMaps. In future releases, classes will most likely be expanded to include these missing features.

    Support

    Browser support isn't very good at the moment (supported by nearly everyone except IE), but you can use these features now with a transpiler like Babel.

    Resources

    • Classes in ECMAScript 6 (final semantics)
    • What? Wait. Really? Oh no! (a post about ES6 classes and privacy)
    • Compatibility Table – Classes
    • Babel – Classes
    0 讨论(0)
  • 2020-11-22 08:07

    Code golf for @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());

    0 讨论(0)
提交回复
热议问题