I'm trying to get my head around this black art called JavaScript - and, I must admit, pretty excited about it. I've been looking at code examples, mainly from "easeljs" since that is what I will be using mainly. And I'm a bit confused..
I (think I) understand the difference between using the prototype
for functions or properties that are class
variables and using this.someProp
for 'instance' variables (Yes, I understand that there are no classes in JavaScript.)
The code I have looked at, and am using as templates for my own code, declare
prototype
variables and then refers to them with this i.e.
In the constructor:
this.name = name;
Then a declaration:
Object.prototype.name;
And later,
this.name = "Freddy";
This is within functions called with 'new' so in this case, as I understand it, this
refers to the current object. What puzzles me is what the prototype declaration is doing and why do we use it for instance variables?
Clarification: In the following code, I don't see what the prototype declaration of radius is achieving:
(function(){
// constructor
function MyCircle(radius){
this.radius = radius;
}
MyCircle.prototype.radius;
this.area = function(){
return 3.14*this.radius*this.radius;
};
window.MyCircle = MyCircle;
}());
The value on a prototype has a key behaviour that is different from a property set directly on the instance. Try this:
// Create a constructor
function A() {}
// Add a prototype property
A.prototype.name = "Freddy";
// Create two object instances from
// the constructor
var a = new A();
var b = new A();
// Both instances have the property
// that we created on the prototype
console.log(a.name); // Freddy
console.log(b.name); // Freddy
// Now change the property on the
// prototype
A.prototype.name = "George";
// Both instances inherit the change.
// Really they are just reading the
// same property from the prototype
// rather than their own property
console.log(a.name); // George
console.log(b.name); // George
This would not possible without prototypical inheritance.
You can test whether the property is the instances property or the prototype property using the hasOwnProperty
method.
console.log(a.hasOwnProperty("name")); // false
An instance can override the prototype
value.
b.name = "Chris";
console.log(b.hasOwnProperty("name")); // true
console.log(a.name); // George
console.log(b.name); // Chris
And return to the prototype
value.
delete b.name;
console.log(b.hasOwnProperty("name")); // false
console.log(b.name); // George
This is a powerful part of prototypical inheritance.
In the other pattern:
function A() {
this.name = "George";
}
The this.name
variable is declared again with every new instance.
It makes some sense to have methods as functions declared on the prototype. Rather than the function definition being re-declared on every instance, all instances can share a single function.
In terms of variables, rather than functions, the prototype can possibly be used for default values in the case that an instance does not set its own value.
The code in a fiddle
A value stored on the prototype provides a default value for that property.
If you subsequently write a value to that property, the instance will acquire that new value, hiding the value that's on the prototype, which will be left intact.
In the context of the code you've now added to the question:
MyCircle.prototype.radius;
does absolutely nothing. It's a no-op - it attempts to read that property and then discards the result.
Yeah, I agree the prototype can be used for default values of properties (variables). The constructor function doesn't need to declare a property; it may be done conditionally.
function Person( name, age ) {
this.name = name;
if ( age ) {
this.age = age;
}
}
Person.prototype.sayHello = function() {
console.log( 'My name is ' + this.name + '.' );
};
Person.prototype.sayAge = function() {
if ( this.age ) {
console.log( 'I am ' + this.age + ' yrs old!' );
} else {
console.log( 'I do not know my age!' );
}
};
Person.prototype.age = 0.7;
//-----------
var person = new Person( 'Lucy' );
console.log( 'person.name', person.name ); // Lucy
console.log( 'person.age', person.age ); // 0.7
person.sayAge(); // I am 0.7 yrs old!
See how Lucy's age
is conditionally declared and initialized.
Other answers have already explained the difference between prototype vs instance properties.
But just to add to the answer, let's break down your code snippet:
(function(){ // <------- 1
// constructor
function MyCircle(radius){ // <------- 2
this.radius = radius; // <------- 2.1
}
MyCircle.prototype.radius; // <------- 3
this.area = function(){ // <------- 4
return 3.14*this.radius*this.radius;
};
window.MyCircle = MyCircle; // <------- 5
}());
- Creating an
IIFE
which acts as a scope container for the inner code
- Declaring a function called
MyCircle
using a constructor pattern (but observe that it never gets "constructed" so should probably get rid of the capital letter since it's misleading)
- when invoked creates a
radius
instance property on the invoked object
- Attempting to access a
radius
property on MyCircle
function's prototype
which doesn't exist so evaluates to undefined
- Creating an
area
instance property on the global window object and assigning it a function expression
- Creating a
MyCircle
instance property on a window
object and assigning to it the MyCircle
function
Summary: It seems like it's creating an area
and MyCircle
properties on the global window
object, and when MyCircle
is invoked it creates an additional radius
property.
Usage: MyCircle should be invoked before area since area relies on MyCircle initialising the radius:
window.MyCircle(10);
window.area(); // evaluates to 314