When you use new
, the value of this
inside the constructor points to the newly created object (for more information on how new
works, take a look at this answer and this answer). So your new instance i
, has a hello
function. When you try to access the property of an object, it walks up the prototype chain until it finds it. Since hello
exists on the instance of the object, there is no need to walk up the prototype chain to access the version of hello
that returns hhhhhhhh
. In a sense, you have overridden the default implementation in your instance.
You can see this behavior if you don't assign hello
to this
inside your constructor:
var O = function(someValue) {
}
O.prototype.hello = function(){
return "hhhhhhh";
}
var i = new O("chris");
console.log(i.hello()); //this prints out hhhhhhh
What you're doing is kind of backwards. The prototype basically provides the "default" form of something, which you can override on a per-instance basis. The default form is used only if the property you're looking for cannot be found on the object. That is, JavaScript will start walking up the prototype chain to see if it can find a property that matches what you're looking for. It it finds it, it will use that. Otherwise, it will return undefined
.
What you basically have in the first case is as follows:
Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
|
+----i.hello (returns "hello, chris")
So when you do i.hello
, JavaScript sees that there is a hello
property on i
and uses that. Now if you didn't explicitly define a hello
property, you basically have the following:
Object.prototype.hello (not defined; returns "undefined")
|
+----O.prototype.hello (returns "hhhhhhhh")
|
+----i.hello (is "undefined", so JavaScript will walk up the chain until
it sees O.prototype.hello, which does have a defined value
it can use.)
What this means is that you can provide a default implementation in the prototype, and then override it (in a sense it's like sub-classing). What you can also do is modify the behavior on a per-instance basis by directly modifying the instance. The version of hello
that you have on the prototype is kind of a fail-safe and a fall-back.
EDIT: Answers to your questions:
Overriding on a per-instance basis means you attach a property or a function to a particular instance. For example, you could do:
i.goodbye = function() {
return "Goodbye, cruel world!";
};
Which means that this behavior is specific to that particular instance (i.e., only to i
and not to any other instances that you may have created).
If you take out this
, then you have basically have:
hello = function() {
return "hello, " + someValue;
}
Which is equivalent to doing:
window.hello = function() {
return "hello, " + someValue;
}
So in this case, hello
is a global reference to that function. What this means is that hello
isn't attached to any of your objects.
hello
can be undefined if you don't have this.hello = function() { .... };
inside your constructor. I was also talking about the general process that JavaScript uses to try to resolve properties on objects. As I mentioned before, it involves walking up the prototype chain.