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 exp开发者_运维问答ect 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?
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.
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 asob.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 thatthis
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.
精彩评论