1

I am generating a lot of "classes" (actually functions trying to simulate classes as in c# or other object oriented languages), and are looking for the best way to do this. As you might notice, I also have jQuery available.

This is how all classes are generated at this point:

MyClass = (function() {
  function innerClass() {

    var self = this;
    var myField;

    // This function works as the constructor
    this.init = function(opts) {
      // Arguments to the constructor
      var defaultOpts = {
        myInitArgument: null
      }
      opts = $.extend(defaultOpts, opts);
      self = this;

      // Any custom constructor code is generated here...
    }

    // A function
    this.myFunction = function() {
      myField = "Hello World!";
    }

    // Returns an object with all selected fields and function that should work as "public". Those not mentioned here, will not be visible outside this class.
    return {
      init: this.init,
      myFunction: this.myFunction,
      myField: myField,
    }
  }
  return innerClass;
})();

Then I create instances of the class like this:

var myObject = new MyClass();
myObject.init({myInitArgument: 'test'});

My main problem here is that inside the myFunction, "myField" will be set to "Hello World!" if I break in the debugger (i.e. Chrome Developer Tools), but using "myObject.myField" returns undefined.

I made a fiddle if you would like to play around with this sample.

What is the best way to accomplish this problem, and are there perhaps other things you feel of warning me about?

3
  • There are no "fields" in javascript. One is a property, to other a variable. You have to decide which one you want to use. Commented Aug 19, 2014 at 12:54
  • What's that IIFE around the innerClass good for? It can be omitted safely in your case. Also, you shouldn't use an init method, just use the constructor that you have there. Commented Aug 19, 2014 at 12:55
  • @bergi - yes his innerClass is of no use. I have provided him with an answer without that Commented Aug 19, 2014 at 12:57

4 Answers 4

0

JavaScript is a bit weird when it comes to making classes and objects. IMO, this is the most reliable and readable method of doing it: start with a function that becomes your primitive object (Fruit).

Edit: thanks to @Bergi for pointing out that previous version had vestigial variables, needed to be moved to init().

function Fruit(opts) {
    this.init(opts);
}

Now, expand the function, giving it more functions, like init, etc:

Fruit.prototype.init = function(opts) {
    // init values go here
    this.cost = 0;
    this.count = 0;

    var that = this;  // in the iteration below, we want to refer to our parent
    for( k in opts )(function(k, v) {
        that[k] = v;
    })(k, opts[k]);
}

// now, here's a specialized set of functions that sets properties (price/quant)
// note that they do "return this" - this is so you can conveniently chain
// commands. ex: apple.setPrice(10).setQuantity(5);
Fruit.prototype.setPrice = function(how_much) {
    this.cost = how_much;
    return(this);
}

Fruit.prototype.setQuantity = function(how_many) {
    this.count = how_many;
    return(this);
}

Simple function to return a computed value. At this point, once instantiated, the object becomes 'self aware'. Helper functions like this become more readable.

Fruit.prototype.getEarnings = function() {
    return( this.cost * this.count );
}

So far we've only setup the abstract structure. To use this, create a new object:

var apple = new Fruit({ genus: 'Malus' });
var orange = new Fruit({ genus: 'Citrus' });

apple.setPrice(1.50).setQuantity(20);
orange.setPrice(1.25).setQuantity(40);

console.info( apple.genus + " will earn you $" + apple.getEarnings() );   // $30
console.info( orange.genus + " will earn you $" + orange.getEarnings() ); // $50
Sign up to request clarification or add additional context in comments.

10 Comments

Nice approach. How does it work when working with events? Like if I add a jQuery click delegate like this in the init-function: $('.button').click(function() { var x = that.getEarnings(); }); Would 'that' still be available as the same object?
You always need to tie a variable (apple/orange) to an event somehow. For example: var apple = new Fruit(); first, then refer to it by its actual instance afterward: $(".button-more").click(function() { apple.setQuantity(apple.count+1); });
What is the benefit of for( k in opts )(function(k, v) {that[k] = v;})(k, opts[k]); over $.extend(this, opts); or a simple for(var k in opts) this[k] = opts[k];. And why no var k, k is a global here, right ?
@Volune: my example is generic enough to where it doesn't require jQuery.
@pp19dd What about for(var k in opts) this[k] = opts[k]; and the missing var ?
|
0

I don't understand what you do that much complicated things to have classes.

var myField and <returned object>.myField are two different variables, modifying one won't change the other.

You can try this (encapsulation):

return {
    init: this.init,
    myFunction: this.myFunction,
    getMyField: function() {return myField;},
}
// ...
$('.console').append('<br />Outside myFunction: ' + myObject.getMyField());

or this (get operator):

return {
    init: this.init,
    myFunction: this.myFunction,
    get myField() {return myField;},
}
// ...
$('.console').append('<br />Outside myFunction: ' + myObject.myField);

2 Comments

Please use the term property, not variable for object properties.
True, but I don't think using the proper terms will make my point about changing one doesn't change the other more understandable.
0

This worked fine for me

 $('.console').append('Class simulation test:<br />');

// My class
function MyClass() {    

        var self = this, myField;

        // This function works as the constructor
        this.init = function(opts) {
            // Arguments to the constructor
            $('.console').append('<br />Inside myFunction: ' + JSON.stringify(opts));
            var defaultOpts = {
                myInitArgument: null
            }
            opts = $.extend(defaultOpts, opts);
            //self = this; // no need of this

            // Any custom constructor code is generated here...
            this.myFunction('Hello from the constructor!');
        }

        // A function
        this.myFunction = function(value) {

         this.myField = value;        //if you dont use var it will either refer to parent my field or window 
            $('.console').append('<br />Inside myFunction: ' + this.myField);
        };
        console.log(JSON.stringify(arguments[0]));
        this.init(arguments[0]);
        // Returns an object with all selected fields and function that should work as "public". Those not mentioned here, will not be visible outside this class.
        return {

            myFunction: this.myFunction,
            myField: myField,
        }

}

// instanciate
var myObject = new MyClass({myInitArgument: 'test'});


// test
myObject.myFunction('Hello from the outside!');
$('.console').append('<br />Outside myFunction: ' + myObject.myField);

Comments

0

I have recently been researching this. I have succeeded, here. The ticker object at that link is a real psuedo class.

var foo = function(args){
    this.args = args;
    this.whatever = "whatever";
}
foo.prototype.addBar = function(bar){
    this.args += bar;
}
foo.prototype.getArgs = function(){
    return this.args;
}

var baz = new foo("Hello");
baz.addBar(" Super Man");
var helloStr = baz.getArgs();
//helloStr holds "Hello Super Man"
var what = baz.whatever;
//what holds "whatever"

Simple, no need for inner function, new foo() is the constructor.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.