compare code 1 and code 2, which one is correct?
function Rectangle(height, width) {
this.height = height;
this.width = width;
this.calcArea = function() { // why use this here?
return this.height * this.width;
};
}
code 2
I thought it's fine with this :
function Rectangle(height, width) {
this.height = height;
this.width = width;
calcArea = function() {
return this.height * this.width;
};
}
If you don't use this
, calcArea
will not be accessible through the object of Rectangle. When you say,
this.calcArea = function () ...
you create a new variable in the current object (this
) and the function will be assigned to it, so that the object will have access to it.
Try these statements, with and without this
. You ll understand better.
var a = new Rectangle(1, 2);
console.log(a.calcArea());
which one is correct?
This depends on how you view "correct":
- Will either declaration fail to be parse correctly?
- No, both are valid JavaScript.
- Which one will calculate
calcArea
?
- Code 1 will calculate it correctly and Code 2 does not create a member function of the
Rectangle
class but you can make it calculate correctly with a bit of difficulty ad redirection. See below.
- Is either one good practice for creating classes?
- No, neither of them. See at the bottom.
Code 1 - calcArea()
If you create a new instance of the Rectangle
in code 1 then:
function Rectangle(height, width) {
this.height = height;
this.width = width;
this.calcArea = function() { // why use this here?
return this.height * this.width;
};
}
var rect = new Rectangle( 3, 4 );
console.log( rect.calcArea() );
Will correctly output 12
Code 2 - calcArea()
If you create a new instance of the Rectangle
in code 2 then:
function Rectangle(height, width) {
this.height = height;
this.width = width;
calcArea = function() {
return this.height * this.width;
};
}
var rect = new Rectangle( 3, 4 );
console.log( rect.calcArea() );
Will throw an error: TypeError: rect.calcArea is not a function
calcArea
is, instead, attached to the global scope so we can do:
console.log( calcArea() );
Will output NaN
as calcArea
in part of the global scope so has no knowledge of any instance of a Rectangle
class and the global scope does not have a height
or a width
attribute.
If we do:
var rect = new Rectangle( 3, 4 );
width = 7; // Set in the global scope.
height = 10; // Set in the global scope.
console.log( calcArea() );
Then it will return 70
(and not 12
since, within calcArea()
, this
references the global scope and not the rect
object).
If we change what this
refers using .call()
to invoke the function:
var rect = new Rectangle( 3, 4 );
width = 7; // Set in the global scope.
height = 10; // Set in the global scope.
console.log( calcArea.call( rect ) );
Then it will output 12
(since this
now refers to the rect
object and not to the global scope).
You probably don't want to have to do this every time you want to use calcArea()
.
Why Code 1 is not optimal
Code 1 will work but is not the optimal solution because each time you create a new Rectangle
object it will create an calcArea
attribute of that object which is a different function to any calcArea
attributes of any other Rectangle
object.
You can see this if you do:
function Rectangle(height, width) {
this.height = height;
this.width = width;
this.calcArea = function() { // why use this here?
return this.height * this.width;
};
}
var r1 = new Rectangle( 3, 4 ),
r2 = new Rectangle( 6, 7 );
console.log( r1.calcArea.toString() === r2.calcArea.toString() ); // Line 1
console.log( r1.calcArea === r2.calcArea ); // Line 2
Which will output true
when testing the string representation of the functions are identical but false
when testing whether the functions are identical.
What does this mean? If you create 10,000 instances of Rectangle
then you will have 10,000 different instances of the calcArea
attribute as well and each copy will require additional memory (plus time to allocate that memory and to garbage collect it at the end).
What is better practice?
function Rectangle(height, width) {
this.setHeight( height );
this.setWidth( width );
}
Rectangle.prototype.setHeight = function( height ){ this.height = height; }
Rectangle.prototype.setWidth = function( width ){ this.width = width; }
Rectangle.prototype.calcArea = function(){ return this.height * this.width; }
Then if you do:
var r1 = new Rectangle( 3, 4 ),
r2 = new Rectangle( 6, 7 );
console.log( r1.calcArea.toString() === r2.calcArea.toString() ); // Line 1
console.log( r1.calcArea === r2.calcArea ); // Line 2
It will return true
for both - meaning that r1.calcArea
and r2.calcArea
refer to the identical function and regardless of how many instances of Rectangle
there are.
The second version will set the global variable calcArea to do stuff specific to your object whenever an instance of your object is constructed. Use of this is required to set properties of your particular object.
When you preface your methods and properties with 'this' in your constructor they allow any new objects that get created with that constructor to use those properties and methods and have those properties and methods point to the newly created object.
If you create a new object based on your version of the Rectangle constructor that doesn't use 'this' as a preface to calcArea and look at the chrome debugger you get the following error:
Object #<Rectangle> has no method 'calcArea'
In short it simply isn't recognized.
The other aspects of not using "this" is that the method becomes 'global'.
Here is a code example to demonstrate:
function Rectangle(height, width) {
this.height = height;
this.width = width;
calcArea = function() { // Not prefaced with 'this'
return 'hello world';
};
}
var aNewObj = new Rectangle(10,10);
console.log(calcArea()) // Notice this is not aNewObj.calcArea() ...just calcArea() but its still accessible.