8

I have this example code:

var foo = {

    self: this,

    init: function(){

        self.doStuff();
    },

    doStuff: function(){
        alert('doing stuff');   
    }

}

foo.init();

Why the refence "self" doesn't work?

Thanks!

mauriblint
  • 1,802
  • 2
  • 29
  • 46
  • Think about this: When creating the object `foo`, what scope are you in and what would `this` be (given that the smallest scope in JavaScript is a `function`)? – J. Holmes Dec 01 '11 at 20:25
  • 1
    possible duplicate of [Self-references in object literal declarations](http://stackoverflow.com/questions/4616202/self-references-in-object-literal-declarations) – Bergi Jan 10 '14 at 16:11
  • possible duplicate of [How can a Javascript object refer to values in itself?](http://stackoverflow.com/questions/2787245/how-can-a-javascript-object-refer-to-values-in-itself) – givanse Mar 10 '14 at 03:53

4 Answers4

9

Because at the time you declare the object literal this is not a reference to the object, but to whatever the calling context is.

Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
8

The value of this is determined by how the current function was called. It does not refer to the current object.

This will work:

var foo = {
    init: function(){
        this.doStuff();
    },
    doStuff: function(){
        alert('doing stuff');   
    }
};

foo.init();

Since when you call foo.init(), this becomes foo.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 1
    Thanks! I ve a problem, because if I write this inside an object function, like this..: init: function(){ $('#element').click(function(){ this.doStuff(); //"this" is refence to jQuery object }) } but.. why this code doesn't work? init: function(){ self = this; $('#element').click(function(){ self.doStuff(); //"this" is refence to jQuery object }) } – mauriblint Dec 01 '11 at 21:00
  • `self` is a global, you are likely overwriting it somewhere else. – Quentin Dec 01 '11 at 21:11
3

Following up on Qeuntin's response you would use the following to achieve what you're looking for

var foo = {

    self: false,

    init: function(){
        self = this
        self.doStuff();
    },

    doStuff: function(){
        alert('doing stuff');   
    },
}

EDIT: Since it's been pointed out that whilst this solves OP's problem (i.e it works) it isn't exactly how you should go about it. So, here's a scoping reference.

function A()
{
    //Semi-private / hidden var
    var pVar = "I'm a private, err hidden, variable",
        //fn (technically a var)
        pFn = function(){},
        //empty var, placholder for hidden fn
        privatePlaceholderFn;

    //Instance-time... public fn
   this.instancePublicFn = function()
    {
        console.log("--- instace public ---");
        //Print hidden var to cosole
        console.log(pVar);
        //Call hidden fn
        instancePrivateFn();
        console.log("--->Setting  private from instance public")
        //Set the hidden fn
        setPrivate();
        console.log("--- / instance public ---");
    }
    //Pass fn to private method.
    this.setPrivFromOutside = function(fn)
    {
        setPrivateFromPrivateYetOutside(fn);
    }

    //Set the hidden fn
    this.iPFnPlaceholderSetter = function(fn)
    {
        privatePlaceholderFn = fn;
    }

    //Call the semi-private / hidden fn
    this.callPrivate = function()
    {
       privatePlaceholderFn();
    }
    //A misnomer, proves the scope exists. See "function setPrivate()"
    this.setPrivateFromInstance = function()
    {
        //Prove scope exists
        console.log(privatePlaceholderFn);
        console.log("Private From instance - gets inside scope");

    }
    //Set hidden fn from private method
    function setPrivate()
    {
        privatePlaceholderFn = function()
        {
            //Show scope exists
            console.log(pVar);
        }
    }
    //Set the hidden fn from hidden method
    function setPrivateFromPrivateYetOutside(fn)
    {
        //fn's scope won't resolve to inside
        privatePlaceholderFn = fn;
    }
    //Private / hidden messager
    function instancePrivateFn()
    {
        //Just loggin' something
        console.log("Instance Private method");
    }
}
//Add an object method to the prototype
A.prototype.protoPuFn = function(){
    console.log("---> Private var from object literal method");
    //console.log(pVar)
}

//...
a = new A();

//Add object literal fn
a.objFn = function()
{
    console.log("Object literal defined public fn - Gets outside scope");
    //console.log(pVar);
}
//Set private / hidden placeholder fn
a.iPFnPlaceholderSetter(function()
{
    console.log("Hidden fn, passed through instance public - gets outside scope");
    //console.log(pVar);
});
//Attempt to read hidden var
console.log(a.pVar);
//Call object literal defined fn
a.objFn();
//Call the hidden fn
a.callPrivate();
//Call prototype added fn
a.protoPuFn();
//Call instance added public fn
a.instancePublicFn();
//Call private / hidden method (set
a.callPrivate();
//Same as iPFnPlaceholderSetter except the param is passed to a hidden method, before seting.
a.setPrivFromOutside(function()
{
    console.log("-->Passed from outside, through public then private setters");
    //console.log(pVar)
})
//Call the hidden method
a.callPrivate();
//Set hidden fn from instance public
a.setPrivateFromInstance();
//Call the hidden method.
a.callPrivate();
//Use evi(a)l fn to steal scope.
a.evil("this.meth = function(){console.log(pVar)}");
//Call fn with stolen scope
a.meth();
CBusBus
  • 2,321
  • 1
  • 18
  • 26
  • 3
    This answer is not correct. It's still referring to the global (or relatively global) variable "self", not the "self" property of the object. – Pointy Feb 19 '14 at 19:49
  • Forgive my ignorance, but can the second example not be written in object literal form? – Protomancer Mar 02 '18 at 05:00
2

ES6 provides getters for the object properties, so you can use them to reference the object itself and to use its other members:

var foo = {
     prop1: 1,

     get prop2() { return this.prop1 + 1; }
}

// foo.prop2 = 2;
P.Petkov
  • 1,569
  • 1
  • 12
  • 20