Question

Here is the textbook standard way of describing a 'class' or constructor function in JavaScript, straight from the Definitive Guide to JavaScript:

function Rectangle(w,h) {
    this.width = w;
    this.height = h;
}
Rectangle.prototype.area = function() { 
    return this.width * this.height;
};

I don't like the dangling prototype manipulation here, so I was trying to think of a way to encapsulate the function definition for area inside the constructor. I came up with this, which I did not expect to work:

function Rectangle(w,h) {
    this.width = w;
    this.height = h;
    this.constructor.prototype.area = function() { 
        return this.width * this.height;
    };
}

I didn't expect this to work because the this reference inside the area function should be pointing to the area function itself, so I wouldn't have access to width and height from this. But it turns out I do!

var rect = new Rectangle(2,3);
var area = rect.area(); // great scott! it is 6

Some further testing confirmed that the this reference inside the area function actually was a reference to the object under construction, not the area function itself.

function Rectangle(w,h) {
    this.width = w;
    this.height = h;
    var me = this;
    this.constructor.prototype.whatever = function() { 
        if (this === me) { alert ('this is not what you think');}
    };
}

Turns out the alert pops up, and this is exactly the object under construction. So what is going on here? Why is this not the this I expect it to be?

Was it helpful?

Solution

I think the right answer is that you should not do this because as kennebec said in the comment:

If you have a hundred rectangles, you are going to redeclare that prototype method a hundred times.

OTHER TIPS

I thought that 'this' always referred to the object against which the function was called.

The meaning of this depends how the function was called:

  • this usually refers to the object the function is called from at runtime, e.g. when called as ob.foo(), this in foo will refer to ob.
  • If a function is called in a no-object-provided way, e.g. just foo(), this refers to the global variable (the top object that contains all other global variables in your js program).
  • And in .call() and .apply(), the object that this refers to is supplied.

Now, if you wanted a way to point to the object for the function that your function was created in (i.e., the 2nd level this at the time of creation), then you would have to rename the deeper this to make it shine through the currently-visible one. If that's clear as mud, this should help clarify a bit:

function outside() {
    // Here, 'this' refers to the object outside() is called with.
    // Let's "rename" this, to make it visible to inside functions.
    var that = this,
        private = 42;

    return {
        inside: function {
            // here, 'this' refers to the object inside() is called with,
            //  it is hiding outside's this.
            // Also, 'that' refers to the object outside() was called with,
            //  *at the time* outside was called and inside was created.
            // Notice that you can see 'private'
            // (but nobody outside of 'outside()) can!
            return private;
        }
    }
}

This pattern above is useful to create object with public methods that can access private members. See Crockford for probably better explanations.

What 'this' points to, is determined when the code is run.

Here's an easy way to figure out what 'this' should be.

If you use '.' to reference a function, 'this' will be a reference to whatever is on the left side of the '.'

(This doesn't account for .call and .apply)

var myFn = function () {
return this.name + ' called me';
};

var me = {
name : 'oatkiller',
shoutOut : myFn
};

var you = {
name : 'Matthew',
shoutOut : myFn
};

// im on the left of the dot, so this points to me
me.shoutOut(); // 'oatkiller called me'

// youre on the left of the dot, so this points to you
you.shoutOut(); // 'Matthew called me'

I think you're really close with the final example. How about using a privileged method:

function Rectangle(w,h) {
  this.width = w;
  this.height = h;
  var that = this;
  this.area = function() { 
    return that.width * that.height;
  }
}

var rect = new Rectangle(2,3);
console.log(rect.area());

See Crockford's "Private Members in JavaScript" for more info.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top