Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attaching methods to prototype from within constructor function

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?

like image 301
Matthew Taylor Avatar asked Mar 11 '10 22:03

Matthew Taylor


3 Answers

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.

like image 101
Matthew Taylor Avatar answered Oct 09 '22 22:10

Matthew Taylor


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

like image 34
Heath Borders Avatar answered Oct 10 '22 00:10

Heath Borders


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.

like image 45
squelart Avatar answered Oct 09 '22 22:10

squelart