JavaScript Classes

前端 未结 8 462
心在旅途
心在旅途 2020-12-07 09:21

I understand basic JavaScript pseudo-classes:

function Foo(bar) {
    this._bar = bar;
}

Foo.prototype.getBar = function() {
    return this._bar;
};

var f         


        
相关标签:
8条回答
  • 2020-12-07 09:51

    Closures are your friend!

    Simply add the following tiny function to your top-level namespace and you're ready to OOP, complete with

    • encapsulation, with static and instance, private and public variables and methods
    • inheritance
    • class-level injection (eg. for singleton services)
    • no constraints, no framework, just plain old Javascript

    function clazz(_class, _super) {
        var _prototype = Object.create((_super || function() {}).prototype);
        var _deps = Array.isArray(_class) ? _class : [_class]; _class = _deps.pop();
        _deps.push(_super);
        _prototype.constructor = _class.apply(_prototype, _deps) || _prototype.constructor;
        _prototype.constructor.prototype = _prototype;
        return _prototype.constructor;
    }
    

    The above function simply wires up the given class' prototype and eventual parent constructor, and returns the resulting constructor, ready for instantiation.

    Now you can most naturally declare your base classes (ie. that extend {}) in a few lines of code, complete with static, instance, public and private properties and methods:

    MyBaseClass = clazz(function(_super) { // class closure, 'this' is the prototype
        // local variables and functions declared here are private static variables and methods
        // properties of 'this' declared here are public static variables and methods
        return function MyBaseClass(arg1, ...) { // or: this.constructor = function(arg1, ...) {
            // local variables and functions declared here are private instance variables and methods
            // properties of 'this' declared here are public instance variables and methods
        };
    });
    

    Extending a class? All the more natural as well:

    MySubClass = clazz(function(_super) { // class closure, 'this' is the prototype
        // local variables and functions are private static variables and methods
        // properties of this are public static variables and methods
        return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
            // local variables and functions are private instance variables and methods
            _super.apply(this, arguments); // or _super.call(this, arg1, ...)
            // properties of 'this' are public instance variables and methods
        };
    }, MyBaseClass); // extend MyBaseClass
    

    In other words, pass the parent class constructor to the clazz function, and add _super.call(this, arg1, ...) to the child class' constructor, which calls the parent class' constructor with the required arguments. As with any standard inheritance scheme, the parent constructor call must come first in the child constructor.

    Note that you're free to either explicitly name the contructor with this.constructor = function(arg1, ...) {...}, or this.constructor = function MyBaseClass(arg1, ...) {...} if you need simple access to the constructor from the code inside the constructor, or even simply return the constructor with return function MyBaseClass(arg1, ...) {...} as in the above code. Whichever you feel most comfortable with.

    Simply instantiate objects from such classes as you normally would from a constructor: myObj = new MyBaseClass();

    Notice how closures nicely encapsulate all of a class' functionality, including its prototype and constructor, providing a natural namespace for static and instance, private and public properties and methods. The code within a class closure is completely free of constraints. No framework, no constraints, just plain old Javascript. Closures rule!

    Oh, and if you want to inject singleton dependencies (eg. services) into your class (ie. prototype), clazz will do this for you à la AngularJS:

    DependentClass = clazz([aService, function(_service, _super) { // class closure, 'this' is the prototype
        // the injected _service dependency is available anywhere in this class
        return function MySubClass(arg1, ...) // or: this.constructor = function(arg1, ...) {
            _super.apply(this, arguments); // or _super.call(this, arg1, ...)
            // the injected _service dependency is also available in the constructor
        };
    }], MyBaseClass); // extend MyBaseClass
    

    As the above code attempts to illustrate, to inject singletons into a class simply place the class closure as the last entry into an array with all its dependencies. Also add the corresponding parameters to the class closure in front of the _super parameter and in the same order as in the array. clazz will inject the dependencies from the array as arguments into the class closure. The dependencies are then available anywhere within the class closure, including the constructor.

    In fact, since the dependencies are injected into the prototype, they are available to static methods even before any object is instantiated from the class. This is very powerful for wiring up your apps or unit and end-to-end tests. It also removes the need to inject singletons into constructors, which otherwise unnecessarily clobbers the constructor's code.

    Check this fiddle: http://jsfiddle.net/5uzmyvdq/1/

    Feedback and suggestions most welcome!

    0 讨论(0)
  • 2020-12-07 09:52

    JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.

    You can see more in this link Mozilla Community

    Github

    0 讨论(0)
  • 2020-12-07 10:07

    This closure allows instantiation and encapsulation but no inheritance.

    function Foo(){
        var _bar = "foo";
    
        return {
            getBar: function() {
                return _bar;
            },
            setBar: function(bar) {
                _bar = bar;
            }
        };
    };
    
    a = Foo();
    b = Foo();
    
    a.setBar("bar");
    alert(a.getBar()); // "bar"
    alert(b.getBar()); // "foo"
    
    0 讨论(0)
  • 2020-12-07 10:08

    what about this :

    var Foo = (function() {
        // "private" variables 
        var _bar;
    
        // constructor
        function Foo() {};
    
        // add the methods to the prototype so that all of the 
        // Foo instances can access the private static
        Foo.prototype.getBar = function() {
            return _bar;
        };
        Foo.prototype.setBar = function(bar) {
            _bar = bar;
        };
    
        return Foo;
    })();
    

    And now we have instantiation, encapsulation and inheritance.
    But, there still is a problem. The private variable is static because it's shared across all instances of Foo. Quick demo :

    var a = new Foo();
    var b = new Foo();
    a.setBar('a');
    b.setBar('b');
    alert(a.getBar()); // alerts 'b' :(    
    

    A better approach might be using conventions for the private variables : any private variable should start with an underscore. This convention is well known and widely used, so when another programmer uses or alters your code and sees a variable starting with underscore, he'll know that it's private, for internal use only and he won't modify it.
    Here's the rewrite using this convention :

    var Foo = (function() {
        // constructor
        function Foo() {
            this._bar = "some value";
        };
    
        // add the methods to the prototype so that all of the 
        // Foo instances can access the private static
        Foo.prototype.getBar = function() {
            return this._bar;
        };
        Foo.prototype.setBar = function(bar) {
            this._bar = bar;
        };
    
        return Foo;
    })();
    

    Now we have instantiation, inheritance, but we've lost our encapsulation in favor of conventions :

    var a = new Foo();
    var b = new Foo();
    a.setBar('a');
    b.setBar('b');
    alert(a.getBar()); // alerts 'a' :) 
    alert(b.getBar()); // alerts 'b' :) 
    

    but the private vars are accessible :

    delete a._bar;
    b._bar = null;
    alert(a.getBar()); // alerts undefined :(
    alert(b.getBar()); // alerts null :(
    
    0 讨论(0)
  • 2020-12-07 10:13

    Javascript is certainly OOP. You always have polymorphism, however you have to sacrifice either encapsulation or instantiation which is the problem you ran into.

    Try this to just brush up on your options. http://www.webmonkey.com/2010/02/make_oop_classes_in_javascript/ Also an old question that I had bookmarked: Is JavaScript object-oriented?

    0 讨论(0)
  • 2020-12-07 10:13

    One problem with a lot of JS classes out there is that they do not secure their fields and methods which means that anyone using it may accidentally replace a method. For example the code:

    function Class(){
        var name="Luis";
        var lName="Potter";
    }
    
    Class.prototype.changeName=function(){
        this.name="BOSS";
        console.log(this.name);
    };
    
    var test= new Class();
    console.log(test.name);
    test.name="ugly";
    console.log(test.name);
    test.changeName();
    test.changeName=function(){
        console.log("replaced");
    };
    test.changeName();
    test.changeName();
    

    will output:

    ugly
    BOSS
    replaced 
    replaced 
    

    As you can see the changeName function gets overriden. The following code would secure the class methods and fields and the getters and setters would be used to access them making this more of a "regular" class found in other languages.

    function Class(){
        var name="Luis";
        var lName="Potter";
    
        function getName(){
             console.log("called getter"); 
             return name;
        };
    
        function setName(val){
             console.log("called setter"); 
             name = val
        };
    
        function getLName(){
             return lName
        };
    
        function setLName(val){
            lName = val;
        };
    
        Object.defineProperties(this,{
            name:{
                get:getName, 
                set:setName, 
                enumerable:true, 
                configurable:false
            },
            lastName:{
                get:getLName, 
                set:setLName, 
                enumerable:true, 
                configurable:false
            }
        });
    }
    
    Class.prototype.changeName=function(){
        this.name="BOSS";
    };   
    
    Object.defineProperty(Class.prototype, "changeName", {
        writable:false, 
        configurable:false
    });
    
    var test= new Class();
    console.log(test.name);
    test.name="ugly";
    console.log(test.name);
    test.changeName();
    test.changeName=function(){
        console.log("replaced")
    };
    test.changeName();
    test.changeName();
    

    This outputs:

    called getter
    Luis
    called setter 
    called getter 
    ugly 
    called setter 
    called setter 
    called setter 
    

    Now your class methods cannot be replaced by random values or functions and the code in the getters and setters always run when attempting to read or write to field.

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