Javascript is a prototype-based object-oriented programming language. Unlike the conventional class-based object-oriented programming languages (e.g. C++ or Java), which ask programmers to write a class and then instantiate several similar objects from the class, Javascript adopts a different approach. In the world of Javascript, we have to craft a prototypal object first and then create similar objects from the prototypal object. In this article, we would like to give a gentle introduction to object-oriented programming in Javascript.
Objects and Methods
Let's start to create our first object in Javascript:
var robert = {
  firstName: 'Robert',
  lastName: 'Smith',
};
In the code snippet above, we created a Javascript object holding Robert's first
name and last name.  Objects in Javascript are essentially dictionaries.  We can
access the properties with the dot operator.  For example, the following
code snippet prints Robert's first name and the last name:
console.log(robert.firstName);
// Prints: Robert
console.log(robert.lastName);
// Prints: Smith
Now, we can write a function to get the full name:
function getFullName(employee) {
  return employee.firstName + ' ' + employee.lastName;
}
console.log(getFullName(robert));
// Prints: Robert Smith
It works but it is suboptimal.  Compared with getFullName(robert), it
will be better to define a method for the object which was referred by the
reference robert and invoke robert.getFullName() instead.  To
achieve this, let's move getFullName() into the object literal and
replace employee argument with the implicit this argument:
var robert = {
  firstName: 'Robert',
  lastName: 'Smith',
  getFullName: function () {
    return this.firstName + ' ' + this.lastName;
  }
};
console.log(robert.getFullName());
// Prints: Robert Smith
The implicit this argument will be bound to the object before the
dot operator at the callsite.  In this case, it will be bound to the
object which robert refers to.
Notice that this mechanism will only work if the dot operator and the
function invocation are combined together.  If we write these operators
separately, then we will get a different result.  For example, in the following
code snippet, this will be bounded to the global host object of the
Javascript run-time environment (window object in browsers and
global object in Node.js) instead of the object which is referred by the
reference robert:
var func = robert.getFullName;
console.log(func());
// Prints: undefined undefined
So far, we have learned to craft an object with several properties and methods.
However, in the real world, many objects may share the same method.  For
example, getFullName() is not specific to robert.  Are there
any mechanisms to re-use this method?
Prototypal Inheritance
Javascript supports prototypal inheritance.  With prototypal inheritance, a
programmer can inherit properties and attributes from another prototypal
object (a.k.a. prototype).  To inherit from an object proto, we can
invoke Object.create(proto) which will return a new object resembles to
the object proto.
For example, the following code snippet creates a new object called mike
which inherits from the object robert:
var mike = Object.create(robert);
mike.firstName = 'Mike';
mike.lastName = 'Williams'
console.log(mike.getFullName());
// Prints: Mike Williams
Notice that we can override the properties from the prototypal object in the
derived object.  In the example, we overrode firstName and
lastName.  Methods from the prototypal object (getFullName() in
this example) can get the overridden properties through the this
keyword.
Overriding properties will not change the prototypal object.  For example, the
firstName and lastName properties in robert object
remains unchanged.  The result of calling robert.getFullName() remains
unchanged as well:
console.log(robert.firstName);
// Prints: Robert
console.log(robert.lastName);
// Prints: Smith
console.log(robert.getFullName());
// Prints: Robert Smith
However, changes to the prototypal object will be visible to derived objects if
such property is not overridden by the derived object.  For example, if we
change the getFullName property in the robert object, then the
result of calling mike.getFullName() will be changed:
robert.getFullName = function () {
  return this.lastName + ', ' + this.firstName;
};
console.log(robert.getFullName());
// Prints: Smith, Robert
console.log(mike.getFullName());
// Prints: Williams, Mike
We can get the prototypal object of an object with
Object.getPrototypeOf().  For example:
console.log(Object.getPrototypeOf(mike));
// Prints: { firstName: 'Robert',
// Prints:   lastName: 'Smith',
// Prints:   getFullName: [Function] }
So far, we have discussed how to inherit properties and methods from another
object.  However, Object.create() is a function introduced in ECMAScript
5, which was released in 2011.  How did people use prototypal inheritance before
Object.create() was introduced?
Constructor
Before the introduction of Object.create(), the prototypal inheritance
has to be done indirectly through the new operator and the constructor
function.
New Operator and Constructor
The new operator will create a new object whose prototype is the
object referred by the prototype property of the constructor function
and pass the newly created object through the implicit this argument.
Constructor functions are functions which assume that the implicit
this argument will bind to a newly created object.  These constructors
will initialize the object by setting more properties.  For example:
function Employee(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
Employee.prototype = {
  getFullName: function () {
    return this.firstName + ' ' + this.lastName;
  },
};
var robert = new Employee('Robert', 'Smith');
var mike = new Employee('Mike', 'Williams');
console.log(robert.getFullName());
// Prints: Robert Smith
console.log(mike.getFullName());
// Prints: Mike Williams
The following code show that the prototype object of robert and
mike is the object referred by Employee.prototype:
console.log(Object.is(Object.getPrototypeOf(robert), Employee.prototype));
// Prints: true
console.log(Object.is(Object.getPrototypeOf(mike), Employee.prototype));
// Prints: true
To get more insights, let's add a console.log() in the Employee
constructor function:
function Employee(firstName, lastName) {
  console.log(Object.is(Object.getPrototypeOf(this), Employee.prototype));
  this.firstName = firstName;
  this.lastName = lastName;
}
new Employee('Ada', 'Lovelace');
// Prints: true
The result shows that the object referred by the implicit this argument
is using Employee.prototype as the prototype object.
Constructor Pitfalls
We have learned about the new operator and the construction functions.
However, please be aware that constructor functions are only functions following
a specific convention.  This abstraction will be broken if we don't call these
functions through the new operator.  For example:
var bob = Employee('Bob', 'Taylor');
// Prints: false
console.log(bob);
// Prints: undefined
In this code snippet, the implicit this argument will be bound to the
global host object mentioned earlier, thus the prototype of the object won't be
Employee.prototype.  Besides, the value of bob will be
undefiend becaue the Employee function does not return any
value.
Return values of the constructor functions might cause some confusions as well.
If a constructor does not return any value, then the new expression will
become the newly created object referred by the implicit this argument
as expected. However, if a constructor returns some value, then the new
expression will become the returned value.  For example:
function Employee(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
  if (firstName === 'Bad' && lastName == 'Example') {
    return 'Surprise';
  }
}
console.log(new Employee('Robert', 'Smith'));
// Prints: { firstName: 'Robert',
// Prints:   lastName: 'Smith' }
console.log(new Employee('Bad', 'Example'));
// Prints: Surprise
This mechanism is designed to give the writer of constructor functions an opportunity to change the prototypal inheritance hierarchy. However, I personally don't use this feature in my code becuase it is too confusing.
Idiom for Constructor Functions
Now, let's get back to our first code snippet for Employee:
function Employee(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
Employee.prototype = {
  getFullName: function () {
    return this.firstName + ' ' + this.lastName;
  },
};
In the our first version, we assigned an object to Employee.prototype.
This is not necessary to create a new object if we are not planning to inherit
from other prototypal objects.  Javascript run-time will create an object and
assign it to the prototype property of the function object when the
function is defined.  We can add properties to that object directly.  Thus,
the idiomatic implementation for Employee should be:
function Employee(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
Employee.prototype.getFullName = function () {
    return this.firstName + ' ' + this.lastName;
};
var robert = new Employee('Robert', 'Smith');
console.log(robert.getFullName());
// Prints: Robert Smith
Craft a Polyfill
The new operator and the constructor function sound complex.  How could
I implement an Object.create() in ECMAScript 3?  Here is a simple trick:
function create(proto) {
  function f() {}
  f.prototype = proto;
  return new f();
}
This code snippet defines a fucntion f locally, assign proto to
prototype property, and return the object constructed by the new
operator.
For example, we can copy the code snippet in the Prototypal Inheritance
section and replace Object.create() with our version:
var robert = {
  firstName: 'Robert',
  lastName: 'Smith',
  getFullName: function () {
    return this.firstName + ' ' + this.lastName;
  }
};
var mike = create(robert);
mike.firstName = 'Mike';
mike.lastName = 'Williams';
console.log(mike.getFullName());
// Prints: Mike Williams
It works, but this implementation can be more efficient.  We don't have to
create a new function object f whenever we invoke create().
We can create one instance and reuse it:
var create = (function () {
  function f() {}
  function create(proto) {
    f.prototype = proto;
    return new f();
  }
  return create;
})();
In addition, we can wrap our polyfill with a check, so that we can use the
built-in implementation if the Javascript run-time supports
Object.create():
if (typeof Object.create !== 'function') {
  Object.create = (function () {
    function f() {}
    function create(proto) {
      f.prototype = proto;
      return new f();
    }
    return create;
  })();
}
Of course, our naive implementation is not fully compliant to the one specified
in ECMAScript 5.  Some features specified in ECMAScript 5 cannot be implemented
with ECMAScript 3 unless some run-time specific hacks are applied.  But, this
polyfill helps us to relate Object.create() function with the
constructor function.
Conclusion
In this post, we started from the object literals, properties, and methods. And
then, we introduced the prototypal inheritance with Object.create(),
which creates an object inheriting from another object.  After having a solid
idea on Object.create(), we went further to learn the new
operator and constructor functions in ECMAScript 3.  It was how people write
object-oriented program in ECMAScript in the past.  Finally, we connected two
different methodologies by crafting an Object.create() polyfill.  Hope
you enjoy this post.  See you next time.





