Reputation: 4115
The code below was copied and pasted from the MDN page on OOP with JavaScript. I have researched general questions on OOP with JavaScript. However, I'm a beginner and have the following questions about this code snippet:
What is the purpose of the line Person.prototype.gender = '';
? If I take it out and run the code, I get the same results.
Why exactly does calling genderTeller()
cause an 'undefined' alert? The MDN explanation seems a bit thin from a beginner's perspective. Is this a scope issue?
function Person(gender) {
this.gender = gender;
}
Person.prototype.gender = '';
Person.prototype.sayGender = function () {
alert(this.gender);
};
var person1 = new Person('Male');
var genderTeller = person1.sayGender;
person1.sayGender(); // alerts 'Male'
genderTeller(); // alerts undefined
alert(genderTeller === person1.sayGender); // alerts true
alert(genderTeller === Person.prototype.sayGender); // alerts true
Upvotes: 2
Views: 115
Reputation: 707696
For question 1, the line
Person.prototype.gender = '';
gives the gender property a default value so that even if it is not assigned some other value when a new Person
object is created, then it has a default value that is not undefined
. There is a difference between ''
and undefined
that is important sometimes and not important other times, but there is a difference.
For question 2, when you do this:
var person1 = new Person('Male');
var genderTeller = person1.sayGender;
genderTeller(); // alerts undefined
Your variable genderTeller
contains a function pointer and only a function pointer. It has NO association with your person1
object. When you call it as just a function, the this
pointer inside the function will be set to either the global object (window
in a browser) or undefined
(if running in strict mode) and thus because this
is not a Person
object, this.gender
will not contain an appropriate value and will likely be undefined
.
There is a very important lesson in this error. In javascript, the this
pointer is set according to how a method is called. When you call genderTeller()
, you're just calling a function and thus there is no association whatsoever with any particular Person
object. The this
pointer will NOT point to a Person
object and thus any references to this.xxx
that assume it's a Person
object will not work.
When you call person1.sayGender()
, this is a very different way of calling the same function. Because you're calling it via an object reference, javascript will set the this
pointer to the person1
object and it will work.
The difference between these two scenarios is subtle, but very important in javascript so I'll try to explain a bit more.
After you create your person1
object, it's an object that contains a property called sayGender
. That property contains a value that points to the function for the sayGender operation. sayGender
is just a function that's contained in a property (technically in the prototype chain on the person1
object, but you can just think of it as a property on that object.
When you do this:
var genderTeller = person1.sayGender;
You're creating a new variable called genderTeller
that now ALSO holds a pointer to that same function. But, just like when it's in the sayGender
property, it is just a function. It has no innate binding to any object. It only gets a binding to an object if you call it via an object reference (or if you use .call()
or .apply()
to force an object reference but that's beyond what you're doing here). When you just call
genderTeller()
you are just calling a function and it will have no object reference associated with it and thus this
will not point to a Person
object when the function runs.
As I mentioned above, it is possible to force an object reference. For example, you could do all of these:
var genderTeller = person1.sayGender;
genderTeller.call(person1);
// .apply() only differs from .call() in how
// arguments are passed to the function
// since you have no arguments to sayGender() it looks the same as .call()
var genderTeller = person1.sayGender;
genderTeller.apply(person1);
var genderTeller = person1.sayGender.bind(person1);
genderTeller();
And, it would again work because you were forcing an association with the person1
object when the function is called.
See MDN's reference for .call()
and .apply()
and .bind()
if you want more info on this, but you should generally not need to use those just to call methods on an object because just calling it with the form obj.method()
creates the object association and causes the this
pointer to be set appropriately for you.
Upvotes: 2
Reputation: 1106
Here is the correct answer.
1) The prototype definition is not for a default value for Person. In fact, if you create another new Person with no gender, you will see it remains undefined.
var person2 = new Person();
person2.sayGender; // alerts undefined
The point here is to show that the constructor definition overrides a prototype definition.
2) Calling genderTeller() causes undefined because genderTeller is a global function which happens to have same function definition that it copied from person1.sayGender method. genderTeller is same as window.genderTeller.
Therefore when you execute genderTeller(), 'this' = window. Since window does not have a 'gender' property, you get an undefined. You can see it by this code
genderTeller(); // returns undefined
gender = 'hi there';
genderTeller(); // returns 'hi there'
Hope that helps. Here is a Plunker. http://plnkr.co/edit/wwc2vYIvH9QdFYStesVW
Upvotes: 0
Reputation: 123513
What is the purpose of the line
Person.prototype.gender = '';
? If I take it out and run the code, I get the same results.
This seems to establish a default value for the gender
property. With it, the property is still set even when an instance is created without calling the constructor:
var person2 = Object.create(Person.prototype);
console.log(person2.gender); // ""
Which could be useful when creating a child type:
function Employee() {}
Employee.prototype = Object.create(Person.prototype);
console.log(new Employee().gender); // ""
Why exactly does calling
genderTeller()
cause an 'undefined' alert?
MDN's document on this
should explain it further, especially the section on "Function context." But, the value of this
is determined when a function
is called rather than by when or where it's defined.
By assigning person1.sayGender
to genderTeller
, it's being disassociated from person1
. So, it's no longer a "method" of a particular object.
Instead, it's called as a regular function with the value of this
being defaulted to the global object, which is window
in browsers.
window.gender = 'n/a';
var genderTeller = person1.sayGender;
genderTeller(); // logs: 'n/a'
Upvotes: 3
Reputation: 2785
I looked over the article and was struck by how confusing this would indeed be to somebody not already intimately familiar with javascript.
Regarding #1, I believe this is a hint to the JIT compiler that "gender" is a string. Javascript JIT compilers like it when object properties stay the same type (in this case, String). It's silly that the article does not describe this, or that this line is there at all. Maybe it's there to demonstrate that you can override prototype properties in a "new Person" instance
Regarding #2, when you call object.method() in automatically calls method where the stack fills in "object" as "this". but if you do something like var detachedmethod = curobject.method and then call detachedmethod(), it does not call with "this" bound as curobject (instead, in the method body, this === undefined. Or maybe "window" I'm not sure :-))
All in all, it's a bunch of nitpicking, and not very important for day to day javascript usage, and can be picked up as you go along.
Upvotes: 0