A Comparison of Class-Based and Prototype-Based Languages

Edward Byne

Friday 24 April 2015

Outline

  1. Historical and object-oriented context
  2. Overview of classes
  3. Overview of prototypes
  4. Key differences

1. Context

Timeline

  • Until 1950s: assembly language
  • 1950s and 1960s: emergence of high-level languages e.g. FORTRAN, ALGOL (procedural languages)
  • 1967: Simula - considered the first object-oriented programming language

Object-oriented languages

"Object-oriented design requires that you shift from thinking of the world as a collection of predefined procedures to modeling the world as a series of messages that pass between objects."

Sandi Metz, Practical Object-Oriented Design in Ruby (2013)

 

  • Data (state) and operations (behaviour) combined into a single thing
  • Objects invoke one another's behaviour by sending each other messages
  • Encapsulation - distinction between internal complexity and external interface

Object-oriented languages

"In a world of objects, new arrangements of behaviour emerge naturally. You don't have to explicitly write code for the spouse_steps_on_cat procedure, all you need is a spouse object that takes steps and a cat that does not like being stepped on. Put these objects into a room together and unanticipated combinations of behaviour will appear."

Sandi Metz, Practical Object-Oriented Design in Ruby (2013)

 

  • Well designed objects can be reused in different and changing contexts
  • OO languages have unlimited object types
  • Objects can be combined using inheritance and composition techniques

Object-oriented languages

  • Need a mechanism for creating objects
  • That's where classes and prototypes come in

Timeline

  • Until 1950s: assembly language
  • 1950s and 1960s: emergence of high-level languages e.g. FORTRAN, ALGOL (procedural languages)
  • 1967: Simula - considered the first object-oriented programming language
  • Simula introduced the concept of classes
  • Classes used in many subsequent OO languages e.g. Smalltalk (1980), Java (1995), Ruby (1995)
  • 1987: Self - first prototype-based language
  • 1995: JavaScript - the only widely-used prototypal language

Java

JavaScript

Self

Scheme

Origins of JavaScript

Syntax

Functions

Prototypes

Largely an amalgamation of three different languages:

2. Classes

Classes

  • A class provides a blueprint for the construction of similar objects
  • Defines methods (behaviour) and attributes (data)
  • Every object must be an instance of a class
  • Built in classes e.g. String
greeting = String.new("Hello, World!")
greeting = "Hello, World!"

OR:

greeting.length
# - > 13
greeting.upcase
# - > "HELLO, WORLD!"

Creating Classes

We can create our own classes:

class Dog

  attr_reader :name

  def initialize(name)
    @name = name
    @tired = false
  end

  def bark
    "Woof!"
  end

  def walk
    @tired = true
  end

  def sleep
    @tired = false
  end

  def tired?
    @tired
  end

end
dog = Dog.new("Henry")

dog.bark
# - > "Woof!"

dog.tired?
# - > false

dog.walk
dog.tired?
# - > true

Classes - Inheritance

A class can be derived from another class:

class Beagle < Dog

  def howl
    "Awoooooo!"
  end

end
beagle = Beagle.new("Henry")

beagle.bark
# - > "Woof!"

beagle.howl
# - > "Awoooooo!"

Beagle.superclass
# - > "Dog"

Classes - Composition

A class can contain other classes:

class Tail

  def wag
    "Wag wag!"
  end

end

class Dog

  attr_reader :name

  def initialize(name)
    @name = name
    @tired = false
    @tail = Tail.new
  end

  def wag_tail
    @tail.wag
  end

end
dog = Dog.new("Henry")

dog.wag_tail
# - > "Wag wag!"

3. Prototypes

  • JavaScript is classless - an object can be easily created using an object literal ({})
  • No need to specify a class or blueprint
var dog = {
  name: "Henry",
  isTired: false,
  walk: function() {
    this.isTired = true;
  },
  sleep: function() {
    this.isTired = false;
  }
};

JavaScript - creating objects

dog.name;
// - > "Henry"

dog.isTired;
// - > false

dog.walk();
dog.isTired;
// - > true

Prototypal inheritance

  • We can create an empty object...
var empty = {};
empty.toString;
// - > function toString() { ... }
Object.getPrototypeOf(empty);
// -> Object {}
  • But it has hidden properties...
  • Supplied by its prototype...

"The problem with object-oriented languages is they've all got this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle."

Joe Armstrong, interviewed in Coders at Work

 

  • Instead of being instances of classes, objects inherit directly from other objects
  • Every object has a hidden link to an object (prototype) from which it can inherit properties

Prototypal inheritance

Prototypes - constructors

  • JavaScript provides the constructor invocation pattern
  • First we create a function:
var Dog = function(name) {
  this.name = name;
};
dog = new Dog("Henry");
// - > { name: "Henry" }
  • And when a function is invoked with the new prefix a new object is created using this function...
  • Functions automatically get a property named prototype, which by default holds an empty object deriving from Object.prototype. This becomes the prototype of the new object:
Dog.prototype;
// - > {}

Prototypes - constructors

  • We can add properties to the constructor's prototype, so that those properties are available to any instances of that constructor:
var Dog = function(name) {
  this.name = name;
  this.isTired = false;
};

Dog.prototype.bark = function() {
  return "Woof!";
};

Dog.prototype.walk = function() {
  this.isTired = true;
};

Dog.prototype.sleep = function() {
  this.isTired = false;
};
dog = new Dog("Henry");

dog.name;
// - > "Henry"

dog.bark();
// - > "Woof!"

Prototypes - inheritance

Prototypes can inherit from other prototypes:

var Beagle = function(name) {
  this.name = name;
};

Beagle.prototype = new Dog();

beagle = new Beagle("Henry");
beagle.bark();
// - > "Woof!"
var Tail = function() {};

Tail.prototype.wag = function() {
  return "Wag wag!"
};

var Dog = function(name) {
  this.name = name;
  this.tail = new Tail();
};

Dog.prototype.wagTail = function() {
  return this.tail.wag();
};

dog = new Dog("Henry");
dog.wagTail();
// - > "Wag wag!"

And composition is also possible:

Prototype chain

  • Differential inheritance - JavaScript will look from the bottom of the prototype chain upwards until it finds the referenced property
  • If not found then undefined is returned

Object.prototype

Function.prototype

Array.prototype

Dog.prototype

Beagle.prototype

4. Key differences

Key Differences

1. Inheritance is optional in prototypal languages

  • Classical - every object must be an instance of a class
  • Prototypal - an object can inherit from other objects, but it doesn't have to:
var dog = Object.create(null);

Object.getPrototypeOf(dog);
// - > null

Key Differences

2. Prototypes may help to avoid complex taxonomies

  • Classical - a class can only inherit from its parent
  • This may lead to complex hierarchies/taxonomies
  • Prototypal - an object can inherit from any other object
  • Prototypal languages aren't interested in where an object comes from, just what it can do

Key Differences

2. Prototypes may help to avoid complex taxonomies (continued)

  • Douglas Crockford advises against using the constructor invocation pattern

 

"JavaScript is conflicted about its prototypal nature."

 

"The pseudoclassical form can provide comfort to programmers who are unfamiliar with JavaScript, but it also hides the true nature of the language. The classically inspired notation can induce programmers to compose hierarchies that are unnecessarily deep and complicated."

Douglas Crockford, JavaScript: The Good Parts, 2008

Key Differences

3. Prototypal objects inherit state as well as behaviour

  • Classical - each instance of a class has its own state (instance variables)
  • Prototypal - an object inherits state from its prototype which can then be modified

Key Differences

4. Prototype changes have retroactive heredity

  • Classical - an object is limited to the methods and instance variables prescribed by its class (and any parent classes) at the time it is instantiated
  • Prototypal - changes to an object's prototype will also affect the state/behaviour of the object, even after creation
dog = new Dog("Henry");

dog.bark();
// - > "Woof!"

Dog.prototype.bark = function() {
  return "Wau!";
};

dog.bark();
// - > "Wau!"

Key Differences

5. Prototypal objects are dynamic

  • Prototypal - the object itself can also be modified at any time
dog = new Dog("Henry");

dog.bark();
// - > "Woof!"

dog.bark = function() {
  return "Wau!";
};

dog.bark();
// - > "Wau!"

dog.isHungry = true;

dog.isHungry
// - > true

Key Differences

6. Prototypal objects do not generally have private data

  • Classical - an object has control over its data and can have private methods
  • Prototypal - object properties are generally public
  • This is one of the main reasons that Douglas Crockford advocates a functional programming style over the "pseudoclassical style"

Functional vs pseudoclassical

  • A review of functional programming goes beyond the scope of this talk but here is an example:
  • The function's variables are not accessible outside the function but will always be accessible to inner functions
var dog = function(name) {

  var name = name;

  return {
    bark: function() {
     return "Woof!";
    },
    readNameTag: function() {
     return name;
    }
  };

}
henry = dog("Henry")

henry.name;
// - > undefined

henry.readNameTag();
// - > "Henry"

Functional vs pseudoclassical

  • But with ECMAScript 6 it seems that constructors are here to stay:
class Dog {
  constructor(name) {
    this.name = name;
    this.isTired = false;
  }

  bark() {
    return "Woof!";
  }

  walk() {
    this.isTired = true;
  }

  sleep() {
    this.isTired = false;
  }

}

  
var Dog = function(name) {
  this.name = name;
  this.isTired = false;
};

Dog.prototype.bark = function() {
  return "Woof!";
};

Dog.prototype.walk = function() {
  this.isTired = true;
};

Dog.prototype.sleep = function() {
  this.isTired = false;
};

Questions?

Classical and Prototypal Languages

By Edward Byne

Classical and Prototypal Languages

  • 255