intro.js

A comparison between the object-orientated features of Perl and JavaScript.

Define the Shape class. The initial uppercase letter is just a convention.

package Shape;
sub new {
    my $caller = shift;
    my $self = bless {}, ref($caller)? ref($caller) : $caller;
    $self->{sides} = 0;
    $self->{method} = sub { warn 'One way to define a method' };
    return $self;
}
function Shape() {
  this.sides = 0;
  this.method = function(){ console.log('One way to define a method') }
}
 

Add methods to the 'class' - any function added as a property of a prototye receives the instance of the object in 'this', just as Perl receives the instance of the 'thingy' in $_[0].

sub describe {
    warn 'I am a Shape';
    warn 'My properties:';
    warn "    $i : $self->{$i}" foreach keys %$self;
}
Shape.prototype.describe = function(){
  console.log('I am a Shape: this is my .describe() method');
  console.log('My properties:');
  for (var i in this){
    if (this.hasOwnProperty(i)           // Avoid listing methods:
    && typeof this[i] != 'function'){ // Avoid listing methods
      console.log('    this.'+ i + ' : '+this[i]);
    }
  }
};
sub moreInfo {
    warn "I am a Shape with $self->{sides} sides";
}
Shape.prototype.moreInfo = function(){
  console.log('I am a shape with '+this.sides+' sides');
};

Define the Square class:

package Square;
use base 'Shape';
sub new {
    my $caller = shift;
    $caller->SUPER::new(@_);
    $self->{sides} = 4;
    $self->{colour} = 'red';
}
function Square() {
  Shape.call(this);    // Optionally, call the parent class' constructor:
  this.sides = 4;      // Optionally, set properties to over-ride those of the parent:
  this.colour = 'red'; // Optionally, set a property not in the parent:
}
 

Inherit from Shape, as Perl's use base..., but JS also / includes inhertiance of Shape's constructor

Square.prototype = new Shape();

JavaScript must explicitly set the constructor:

Square.prototype.constructor = Square;

Over-ride the moreInfo method

sub moreInfo {
    warn "I am a Square, and ust have four sides. My sides==$self->{sides}";
    warn "I have sides as my own property: ".( exists $self->{sides}? 'true':'false');
    warn "I have colour as my own property: ".( exists $self->{colour}? 'true':'false');
}
Square.prototype.moreInfo = function(){
  console.log('I am a Square, so must have four sides. My sides=='+this.sides);
  console.log('I have sides as my own property: '+this.hasOwnProperty('sides'));
  console.log('I have colour as my own property: '+this.hasOwnProperty('colour'));
}
 

Add a method the parent does not have:

sub moreInfo {
    warn "Square goes plonk"
}
Square.prototype.plonk = function(){
  console.log('Square goes Plonk');
}

Instantiate:

my $aShape = new Shape();
$aShape->{method}->();
$aShape->describe;
var aShape = new Shape();
aShape.method();
aShape.describe();

Instantiate:

my $aSquare = new Shape();
$aSquare->{method}->();
$aSquare->describe;
$aSquare->moreInfo;
$aSquare->plonk;
warn "Square colour: $aSquare->{colour}";
var aSquare = new Square();
aSquare.method();
aSquare.describe();
aSquare.moreInfo();
aSquare.plonk();
console.log('Square colour: '+ aSquare.colour);
warn 'Square inhertiance:';
warn $aSquare->isa('Shape')? 'true':'false'; # Will fail
warn $aSquare->isa('Square')?'true':'false';
console.log('Square inheritance'); 
console.log(aSquare instanceof Object); 
console.log(aSquare instanceof Shape); 
console.log(aSquare instanceof Square); 

Call the parent method - frequently said this can't be done in JS. However, built-in methods such as 'propertyIsEnumerable' will only consider the first-level prototype, and not the base-class.

$aSquare->SUPER::describe
Shape.prototype.describe.call(aSquare);

Property-shadowing can't be done in Perl:

console.log('aSquare has four sides: '+aSquare.sides);
console.log('aSquare also has a prototype with 0 sides: '+aSquare.__proto__.sides);
console.log( aSquare );

Property-shadowing can be avoided by creating objects via Object.create - though that is ECMA1.5, which is implemented to varying degrees in various places, so may not be reliable:

var d = Object.create(null); // null could be an instance, to retain the prototype

Give this instance a new method, and call it - easy to do in JS, useful for the Observer/Subscriber pattern.

# Cannot easily be done in Perl without reverting to a code refernce?
aShape.newMethod = function(){ console.log('This is from newMethod') }
aShape.newMethod();

This new method does not effect the 'class':

var anotherShape = new Shape();
try {
  anotherShape.newMethod()
} catch (e){
  console.error( e );
}

Other things that can't be done in Perl without bringing in libraries: