Question about constructors - an alternative to this?

Hello all

I am looking to write a simple library for use with the canvas element, but I am getting a little confused about constructors.

Suppose I have the following code:

animal={};  											
animal.details=function() {
this.name="rabbit";
this.location="field";
}

pet1 = new animal.details();
alert(pet1.name);

This makes an “animal” object, containing a details object correct? This code seems to work, but I would be interested in other ways of writing it.

I tried:

function animal()
{
function details() {
this.name="rabbit";
this.location="field";
}
}

pet1 = new animal();
alert(pet1.details.name);

I thought perhaps creating an animal object would automatically create other objects defined inside. Apparently not, this nested function idea doesn’t seem to work.

I would be interested to know why the above code does not work, and any alternative ways of rewriting the first piece of code please.

Thank you

Matt

This will create an object, surely enough, but generally speaking the name of the class is used as a constructor, not a method that is a property of the object.

This won’t work for several reasons.

The function details inside of the animal function isn’t being executed. When you call pet1.details.name you’re in fact trying to get the name property from the details object inside of the pet1 object. However, in this case, details isn’t an object, but a method.

Let’s take a look at an instantiable object, it’s basic. It’s not the only way of doing it, but it’s one of the easier to grasp methods.


//basic instantiable object    
Animal = function(name, location){


    // at some point we'll need to deal with scope issues
    var that = this;


    //if you want private variables, declare them with var in the main object
    var someLocalVar = "Hello";


    //if you want public variables, make them properties of the object
    this.name = name;
    this.location = location;
    
    
    //example method for returning an object of variables
    this.getDetails = function() {
        return {
            "name":this.name,
            "location": this.location
        }
    }
    
    //example of setting a public property
    this.setDetail = function(detailName, detailContents) {
        this[detailName] = detailContents;
    }
    
    //getter for a private property
    this.getSomeLocalVar = function() {
        return someLocalVar;
    }


    //setter for a private property
    this.setSomeLocalVar = function(newValue) {
        someLocalVar = newValue;
    }
    
}


pet1 = new Animal("Rabbit", "Field");


console.log(pet1.name, pet1.location); //Rabbit, Field


pet1.setDetail("name", "Kitty");
console.log( pet1.name ); // Kitty


console.log( pet1.someLocalVar); // undefined
console.log( pet1.getSomeLocalVar() ); // Hello


pet1.setSomeLocalVar("World"); 
console.log( pet1.getSomeLocalVar() ); // World


pet2 = new Animal("Dog", "Yard");


console.log(pet2.name); // Dog


console.log(pet1.name); //still Kitty

The code with the comments is reasonable self-explanatory, but feel free to ask any questions if you have any.

Further reading:

[LIST]
[][URL=“http://ejohn.org/blog/simple-class-instantiation/”]Simple “Class” Instantiation
[
][URL=“http://www.phpied.com/3-ways-to-define-a-javascript-class/”]3 ways to define a JavaScript class

[*]instantiable+javascript+class Google search
[/LIST]PS: I’ve used console.log() everywhere instead of alert() - make sure you have Firebug or Chrome/Safari dev tools open when you run the code :wink:

Hello John

Thanks very much for your very thorough reply. Your explanation of why my second piece of code was, in particular, very useful.

I think with your help I may have figured out a misconception I had. Looking at my first piece of code I was believing that the “details” object was being created inside of the “animal” object. However, I can now see that they are not true (correct?). Instead, they are completely separate objects, but it just happens that the function for creating the details object is inside of the animal object. So one objects contains the method for making the other.

One of the things I was asking in my question was for an alternative way of writing my code (such as without the {} ). I have now tried the code below, which works.

function animal(){

this.details=function() {
this.name=“rabbit”;
this.location=“field”;
}
}
pet1 = new animal();
pet2= new pet1.details()
alert(pet2.name);

If instead I try: pet1=new animal.details(); alert(pet1.name);

… then this does not work. From the help you have given me, can I rightly conclude that this fails because it firstly looks for an animal object (which doesn’t exist, only a function) and then the details constructor. On the other hand, the code which does run works because it creates the object before trying to call the method within. Is there a way to streamline the code which does work, so that it doesn’t have to be stepwise like this? I guess the details function cannot be a public method if it is nested inside another function. Or perhaps I need to use one of these “getters” you refer to, to access the details function?

Many thanks again for your help

Matt

Hey Matt,

You’re getting there :slight_smile:

Some things to note about your example
To recap:

function animal(){
     this.details = function() {
        this.name = "rabbit";
        this.location = "field";
    }
}

pet1 = new animal();
pet2 = new pet1.details()

alert(pet2.name);

animal()” is only truly an object when the function is called with the “new” keyword (like you have done).

animal.details()” in your example is not an object that can be instantiated because it is not a constructor. (in fact, calling it with the “new” keyword as you have done should actually throw a JavaScript error (animal.details() is not a constructor)

If you want to streamline this, you can set the details for an object either through the constructor or through a setter method. And then get them with a getter method if you like :wink:

“Setter” and “getter” methods are usually used to either “set” or “get” a specific property (e.g. this.name). While you can simply use pet.name = “dog” (or to get it usepet.name) because they are public properties.

A private property like “someLocalVar” in my initial example should only be accessible via its getter method and only changeable via its setter method, you can not modify it by trying pet.someLocalVar because you would then be creating a new public property on the pet object.

Now, let’s look at setting properties through the Constructor and through Setters.

Through the constructor:


//basic instantiable object    
var Animal = function(name, location){
    this.name = name;
    this.location = location;
}    
var pet1 = new Animal("Dog", "Garden");
//you now have instant access to the name and location because they were passed in through the constructor
console.log(pet1.name);
console.log(pet1.location);

Through setter methods:


var Animal = function(){

     //specific setter method for the name
    this.setName = function(name) {
        this.name = name;
    }

    //specific setter method for the location
    this.setLocation = function(location) {
        this.location = location;
    }

    //generic setter method
    this.setDetail = function(detailName, detailContents) {
        this[detailName] = detailContents;
    }
}    
var pet1 = new Animal();
//you now can set the name, location, whatever
pet1.setName("Duck");
pet1.setLocation("Pond");
pet1.setDetail("type", "Bird");

//now that properties have been set, you can access them.
console.log(pet1.name);
console.log(pet1.location);
console.log(pet1.type);

Hope this made things a little clearer. (And that I haven’t overloaded your brain too much).

Hello again John

Once again, this is very helpful, and the examples are great.

These are the last things I shall ask.:

  1. Not really a question, but strange though that calling pet2 = new pet1.details() in my example does not create a problem in any of my browsers.

  2. Just to clarify… you say that in your setter example you cannot change a property using a line like pet1.name = …
    Is this really because the name property is not directly inside the Pet1 object, it is actually inside the setName method of pet1? It is a little confusing what “this” refers to in this.setName and this.name.

Thanks again, I am going to save the contents of this thread as a reference for myself for the future !

Matt

Hi again,[LIST=1]
[]Take a look in the Firebug console in Firefox and see what happens.
[
]Iif you are setting properties inside of the object with “this” then they are public and visible.
If you you set them with var then they are private and not accessible (You can still create a public property by setting one directly on the object, e.g. pet1.someProperty = “some value”)
[/LIST]“this” is indeed a very confusing thing in JavaScript. This should, in our example, refer to the parent method, e.g the class "Animal". That’s because “this” will point to the function/object that the method is a part of.

There are certainly a few things to read up on about it, someone asked a question on StackOverflow that received a pretty well written answer: http://stackoverflow.com/questions/133973/how-does-this-keyword-work-within-a-javascript-object-literal[URL=“http://www.quirksmode.org/js/this.html”]

OK John, thank you. It makes sense.

I am going to read up on things a lot more. I don’t know how to mark this thread as “answered” but I shall pester you no more !

Thanks again

Matt