2

I am creating an object

var testobj=function(x)
{
    var b,c;
    var a=x;
    var init=function(d,e,f)
    {
        a=d;
        b=e;
        c=f;
    };
    return{
        init:init,
        b:function(){
        return b;
        },
        a:a,
        c:c   
    }
};
//here some test of the object
var instobj=new testobj(4);
instobj.init(1,2,3);
alert(instobj.a);  //display 4
alert(instobj.b()); //display 2
alert(instobj.c); //display nothing

I don't understand why I can't get value of 'c' by simply reference to it, instead, I need to use a function to return that value.

tcao
  • 41
  • 5
  • 1
    http://stackoverflow.com/questions/373419/whats-the-difference-between-passing-by-reference-vs-passing-by-value – epascarello May 27 '14 at 17:53
  • 2
    It doesn't display nothing, it displays "undefined" because the value of `c` is undefined. Also note that `instobj.a` isn't working as I suspect you wanted it to because it's value is still `4`. – Matt Burland May 27 '14 at 17:53
  • 4
    JavaScript has assign-by-value. Assigning a new value to `c` later on in the `init` function doesn't magically change the value of the property of the object. – Felix Kling May 27 '14 at 17:59
  • 1
    You don't get an *instance* back from the `new testobj(4)` expression because you're returning an anonymous object, hence defeating the purpose of a constructor function. – haim770 May 27 '14 at 18:05
  • More on constructor functions and prototype here.http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 – HMR May 28 '14 at 01:06

2 Answers2

4

What happened?

I expected you to use the function object when you used new, but you returned an object.

-->return{
    init:init,
    b:function(){
    return b;
    },
    a:a,
    c:c   
}

Usually new isn't used when an object is being returned. When this happens, the context of the function object is lost, as a result, this is what the returned object's init function looks like

function(d,e,f)
{
    a=d;
    b=e;
    c=f;
};

with literally no reference to anything else. At this point you are in a global scope (unless bind was used which is a different issue) causing a,b, and c to now be defined globally (assuming strict is not being used). So what this accomplishes is to create 3 global variables and then assign them to the values.

As a result, this is what ends up happening in your calls at the very end.

You first start by assigning the three global variables a,b, and c to 1,2, and 3

instobj.init(1,2,3);

Next, you alert the value of a, which was still referenced by your returned object, and took a value from the constructor of 4. As a result 4 is alerted

alert(instobj.a);  //display 4 (because that came from the constructor)

In the following line, you reference a function which has no context and as a result is referencing global scope. The variable it returns is the global variable b, set above with init to 2

alert(instobj.b()); //display 2 (this returned b, a global variable)

The last call is to c. However, c is still referencing the original function object's variable c, and that was only declared once but never assigned to. (remember? var b,c;. If this were var a,c=-1; then the following code would alert -1).

alert(instobj.c); //display nothing (undefined from var b,c;)

What could be changed?

Usually a prototypal approach is taken in javascript. This means you set up a constructor, define some prototype functions that each instance will use, and then new up instances of those. For your testobj, that would look like this:

function Test(x){
 this.a = x;
 this.b = undefined;
 this.c = undefined;
}
Test.prototype.init = function(d,e,f){
 this.a = d;
 this.b = e;
 this.c = f;
};

When new is used, this refers to the instance of the function object. Later, when referencing the variable the instantiation was assigned to, the . property name may be used to reference this. property values.

Also when new is used, functions attached to the prototype are included in the instantiated object. Their scope is the same this as described above, and the property references will be the same as well.

As a result, you may access your test object like this:

var testobj = new Test(4);
testobj.init(1,2,3);
alert(testobj.a);//1
alert(testobj.b);//2
alert(testobj.c);//3

jsFiddle Demo

Link to top of answer

Community
  • 1
  • 1
Travis J
  • 81,153
  • 41
  • 202
  • 273
1

When you are generating properties for objects you should be using this, for example:

function TestObj () {
    this.c = 3;
    this.b = function (number) {
        alert(number);
    };

    return this;
}

var instance = new TestObj();
alert(instance.c);
instance.b(4);

Read more about OOP in JS

Ramy Nasr
  • 2,367
  • 20
  • 24