0

I would like to set a design standard for class- as well as class-instance-creation. Combining lots of intel from multiple websites (e.g. from stackoverflow), there finally is a way to have a relative maximum of flexibility. My goal is exactly that in terms of having code-structures that behave similar to more defined classes of Java and the like.

Here is working codesnippet of what I have so far (including explanations):

var MyClass = function (prop1)
{
    var _class = MyClass;
    var _proto = _class.prototype;

    // public member of constructed class-instance
    this.prop1 = prop1;

    // private static property as well as private member of class-instances
    // (see getter below!)
    var prop2 = this.prop1 * 10;
    // need to use this. instead of _proto because the property prop2 of
    // the class itself would be returned otherwise
    this.getProp2 = function ()
    {
        return prop2;
    }

    // 1 function for all instances of the class
    // reached by a fallback to the prototype
    _proto.protoAlert = function ()
    {
        // must call this.getProp2() instead of using prop2 directly
        // because it would use the private static member of the class
        // itself instead the one of the class-instance
        alert(this.prop1 + " " + this.getProp2());
    }
};

var c1 = new MyClass(1);
c1.protoAlert();
var c2 = new MyClass(2);
c2.protoAlert();
c1.protoAlert();

This works well so far. However, there are some hurdles to take to not provoke errors and uncatched misbehavior of the script. The private property prop2 exists in both class and class-instances. It's a likely unintended double identity. Furthermore, the private property of class-instances are only properly reachable through setter- and getter-function. This is not the worst thing as it enforces a common way to access private variables. The downside is: Setters and getters have to be called with this. to actually reference to the prop2 of the class-instance then returning it. As for class inheritance - I didn't look into this topic with my current standard yet. Hopefully, it will work out as well.

Is there a more elegant solution or at least one that is less prone to possible errors?

Thank you in advance!

Florian R. Klein
  • 1,375
  • 2
  • 15
  • 32
  • 6
    The first thing you should immediately do is to [stop assigning prototype methods inside the constructor](http://stackoverflow.com/questions/28255957/assigning-prototype-methods-inside-the-constructor-function-why-not)! – Bergi Dec 18 '15 at 13:53
  • Personal opinion: a more elegant solution would be to not use classes at all. Composition over inheritance is big in javascript. Also, see bergis comment if you want elegance, don't declare prototypes inside the class, it's asking for trouble. – Shilly Dec 18 '15 at 13:53
  • 1
    Why aren't you using the established `MyClass.prototype.protoAlert = function ..` approach (outside the constructor!) and are instead badly reinventing the wheel...?! – deceze Dec 18 '15 at 13:57
  • 1
    [here you go](http://stackoverflow.com/a/9321429/1048572), complete with modularisation, encapsulation and inheritance. Or just use ES6 `class` syntax these days. – Bergi Dec 18 '15 at 13:58
  • *"The private property `prop2` exists in both class and class-instances"* – what? Unclear what that means or how you've reached this conclusion. – deceze Dec 18 '15 at 14:00
  • *"The downside is: Setters and getters have to be called with `this.`"* – as opposed to what...!? The syntax is different from Java, this is simply something you need to get used to, there's no point in bringing it up or perhaps even trying to work around it. – deceze Dec 18 '15 at 14:01
  • @deceze If I reference to ``prop2`` simply as that, I would access the property of that name within ``MyClass``. Using ``this.getProp2()`` accesses a different property, the ``prop2`` of the object created by calling ``new MyClass(...)``. And yes, I am often trying to work around certain language concepts to imitate those of more structured/strict ones IF this gives me more control and maybe reduces code-repetition. – Florian R. Klein Dec 18 '15 at 14:09
  • About composition over inheriance: As far as I understoof from one of the articles you gave me, composition requires code repitition as you keep writing the whole namespace-chain (yes, I am using some objects as namespaces which contain e.g. the "classes"). ``MyModule.MySubModule.MyClass.myMethod = function () {...};`` – Florian R. Klein Dec 18 '15 at 14:13
  • First and foremost: learn how to write Javascript, don't try to force Javascript to be something it is not. If you want something like type checking and such, use something like TypeScript instead. – What you're talking about with `prop2`... I assume this is all about using `prop2` *inside of `protoAlert`*!? It's not that you're accessing the "class" version, it's that you're accessing the *latest* version of the last instantiated object, since you're overwriting the function definition of `protoAlert` with each new object. – deceze Dec 18 '15 at 14:23
  • It is useful to get used to the idea that there is no such thing as a "class" in Javascript. It is further useful to realise that "private" properties are overvalued in OOP (as Tomáš very rightly explains) and that perfectly good programs can be written without them. – As Python would put it: we're all consenting adults, "privacy" can be perfectly achieved by mere naming conventions without the need of the language to supervise you. – deceze Dec 18 '15 at 14:28
  • Not sure if I get what you mean, but you would extend the module with the functions you need, not rewrite or nest the functions inside layers of namespace. So most object methods will still be directly on the object, not a namespace chain, unless you chose to have a long namespace chain. Anyway, it's mostly preference. There's pro's and cons to each side. – Shilly Dec 18 '15 at 14:32

1 Answers1

2

JavaScript does not really provide practical pattern for private properties. The pattern you're using does work only as long as you define all methods in constructor. You should keep in mind that this means every time you create the class, you create all the methods.

If you think about it, private variables are not serving any function in the program, they serve the programmer to keep in mind, that he should and what he should not be changing. As such, you can simply use some naming pattern. I've seen this a lot along other people's code:

function MyClass() {
    // Private property
    this._helloWord = "Hello word.";
}
// From outside, accessed as `helloWord`, without underscore
Object.defineProperty(MyClass.prototype, "helloWord", {
    get: function() {console.log("helloWord getter");return this._helloWord;},
    set: function(value) {console.log("helloWord setter");return this._helloWord = value;},
};
MyClass.prototype.alertProp = function() {
    alert(this._helloWord);
}
// Accessing from the outside:
var instance = new MyClass();
alert(instance.helloWord); // will activate the getter function

Most people will instantly understand there is something special about _underscored variables. You can also make variable constant this way:

Object.defineProperty(MyClass.prototype, "helloWord", {
    value: "Hello world",
    writable: false // <----
};

Learn more about Object.defineProperty. You should also get to understand that the structure of Javascript is a little bit different than in OOP languages. If you try to push other languages' patterns on it, it will cause performance and structural problems.

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • You cannot use `get` and `writable` together in a property descriptor – Bergi Dec 18 '15 at 14:22
  • I hoped to successfully work around ``Object.defineProperty`` because it clutters the code quite a bit. However, besides newer standards which include the class-concept natively, this seems to be the cleanest way to have private as well as public parameters. For inheritance, I will have to use composition as proposed in the questions comment section. – Florian R. Klein Dec 18 '15 at 14:22
  • @FlorianR.Klein I tend to write local wrappers for `Object.defineProperty` - functions that slightly automate the process, especially when there's a repetitive pattern. – Tomáš Zato Dec 18 '15 at 14:30
  • @TomášZato That minimizes the effect a bit. Thanks for the hint. – Florian R. Klein Dec 18 '15 at 14:34