Time to embrace some new features

A modern approach to object creation in JavaScript

MarcoEmrich
peacock

Start using Douglas Crockford’s Object.create method, now supported by all modern browsers.

After stalling for several years, we’ve recently seen JavaScript
language development making a fair amount of progress. The
ECMAScript5-standard
is now almost 3 years old and all major
browser vendors support it in their current versions. So this may
be the right time to really embrace new language features and adopt
a modern style of development.

If you use JS for more than just jQuery-one-liners, you will
need a structured approach. There are many ways to structure your
code in JS. One great and also very common way to do it is to use
objects and prototypical inheritance.

The old way

Let’s assume you build a shop and you need to express products.
In the old days – before ECMAScript5 (ES5), you would have written
something like this:

var Product = function(name) {
    this.name = name;
};
Product.prototype.showName = function() {alert(this.name);};

var myProduct = new Product("Super Fancy TV");
myProduct.showName();

Actually, this isn’t bad. You have a constructor to create your
objects and attach methods to the prototype. All individual
products have these methods, but there are several weak points to
consider:

  • There are no private attributes in the
    JavaScript language. The value of name can be changed any time from
    the outside.
  • The methods are scattered. Even if you put
    them all in one place, there is no single structure to define your
    object concepts – like a class in many other object oriented
    languages.
  • You can easily forget to use the “new”
    keyword
    . This won’t throw an error, it just leads to
    completely different behaviour and probably to some really nasty
    bugs that are hard to find.
  • Inheritance will provide even more problems.
    There isn’t an agreed way in the JS-community of how to do it
    properly.

Because of these points, many developers have created libraries,
frameworks and tools that provide all types of object creation and
instantiation logic. Many of them introduced classes (e.g. Prototype or Coffescript).

You can take this path if you want, but I wouldn’t recommend it.
It may be hard to believe, but prototypical inheritance is actually
much easier than class-based – and even provides additional
benefits. Just take a look at other prototypical languages like IO
or Self. It is the old JavaScript syntax that made prototypes hard
to use.

A
better approach
was pioneered by Douglas Crockford. He wrote a
short
Object.create
method that has made its way into the ES5
standard – in a slightly extended form.

Object.create

The good news is that you don’t need a modern browser with an
ES5 implementation. There is
a polyfill from the Mozilla Developer Network
. It allows you to
use the new way of creating objects, even in older browsers like
IE8 and below. This is important, considering that IE7/IE8 still has
a combined market share of about 9%
. It is not an issue if you
only target Firefox, Chrome, Safari or Opera users. If you are in
doubt, take a look at the compatibility
chart
.

if (!Object.create) {
    Object.create = function (o) {
        if (arguments.length > 1) {
            throw new Error('Object.create implementation only accepts the first parameter.');
        }
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Just include the polyfill from above you will have an

Object.create
function. The first line of the snippet checks
if
Object.create
is already there. This ensures that the
polyfill won’t override the native implementation if the current
browser provides one. If you need additional polyfills for other
ES5-features, you can use Kris Kowal’s es5-shim.

Solo Objects

Let’s take another look at the shop project. If you would like
to create only one product, there is no need for
Object.create
. Just create the object directly.

var myProduct = {
      price: 399.50,
      name: 'Super Fancy TV'
};

However, this isn’t the best option. You can manipulate the
properties of the objects easily from the outside and there is no
way to check or transform the assigned values. Take in to
consideration an assignment like
MyProduct.price = -20
.

A mistake like this one would ensure that your company sells a
lot of products fast and has really happy customers – customers
that receive 20 bucks with every purchase. Your company wouldn’t be
able to do that for long!

Years of experience in object oriented programming and design
have told us to separate the inner state of an
object from its outer interface. You usually want
to make attributes private and provide some getter- and
setter-methods.

Getters and Setters

ES5 provides a great concept for accessing properties with
getters and setters. The sad news is that there is no way to get
them to run in older browsers – you can’t just use a polyfill. So
for the next two years or so, most of us don’t have the luxury to
use it. In the meantime, there are many approaches you could use
(see JavaScript
Patterns
by Stoyan Stefanov
). All of them have their own
distinct set of advantages and disadvantages. There is no single
solution, so it comes down to a matter of taste. Here is what I
usually do:

  • Prefix attributes with an underscore, e.g.

    _price
    . This is just a convention to mark the attribute as
    private. You should never call them from outside the object;
    violations to this rule are usually easy to spot. There are also
    more sophisticated approaches using closure-based patterns to
    archive real privacy (e.g. in Stefanov’s book). My opinion is that
    they aren’t worth the effort, most of the time.
  • Provide a setter method that starts with
    “set”
    , e.g.
    setPrice(value)
    . There is no way in older browsers to
    overwrite the assignment operator =. So this is the next best
    thing. Java and C++ programmers are used to it.
  • Provide a getter method with the name of the original
    attribute
    , e.g.
    price()
    . Many programmers prefer prefixing the method with
    “get”. I think this isn’t necessary in JS and just makes the code
    less readable – your mileage may vary.
  • Sometimes an attribute may be for internal use only – or you
    want it to be ready-only. In this case, just leave out the
    appropriate methods. So a better implementation may be this.

    var myProduct = { _price: 99.50, _name: ‘Grindle 3′,

    price:    function()  {return this._price;},
    name:     function()  {return this._name;},
    setPrice: function(p) {this._price = p;},
    

    };

If you want to check the price before setting it, you can now do
this easily.

setPrice: function(p) {
    if (p <= 0) {
        throw new Error("Price must be positive");
    }
    this._price = p;
}

The real ECMAScript5 implementation is even nicer. It has the
added benefit of calling the getters and setters implicitly, e.g.

myProduct.price = 85.99
. Finally, JavaScript supports the
uniform
access principle
!

var myProduct = {
...
        get price() {return this._price;},
        set price(p) {
            if (p <= 0) { throw new Error("Price must be positive"); }
            this._price = p;
        },

...

Prototypes

A real shop will, of course, have many products. And you don’t
want to create them all from scratch. You will need a place, where
you can put common structure and behaviour. The usual pattern in JS
is to create a parent object for this – a
prototype of a product, where all other products
are derived from. With
Object.create
you build new products from it.

var Product = {
    _price: 0,
    _name: '',

    price:    function()  {return this._price;},
    name:     function()  {return this._name;},
    setPrice: function(p) {this._price = p;},
    setName:  function(n) {this._name  = n;}
};

var product1 = Object.create(Product);
product1.setName('Grindle 3');
product1.setPrice(99.50);

Initializes

It may be a bit inconvenient to call all necessary setters for
all new instances of Product. To circumvent that, provide an
initialize-Method, similar to a constructor from other
languages.

var Product = {
    ...
    init: function(name, price) {
        this._name = name;
        this._price = price;
    },
    ...
};
var product1 = Object.create(Product).init('Grindle 3', 99.50);

Inheritance

A great advantage of the prototypical approach is the
unification of instantiation and inheritance. You don’t need
anything special – just use
Object.create
for inheritance as well.

var Product = {
...
};

var Book = Object.create(Product);
Book._author = null;
Book._numPages = null;
Book.setAuthor   = function(author) {this._author = author;};
Book.setNumPages = function(num_pages) {this._numPages = num_pages;};
Book.author      = function() {return this.author();};
Book.numPages    = function() {return this.numPages();};


Book
is a new object that is derived from
Product
. Instead of setting specific values, like

name
and
price
, it gets additional structure and behaviour. The new
attributes
author
and
numPages
make up the structure, while their simple getters
and setters serve as placeholder examples for some real
behaviour.

The downside is that calling
Book
several times is redundant and the whole syntax is
quite different from defining the base object. Therefore, you
usually build a small
extend
function that makes inheritance a little more
convenient.

Object.prototype.extend = function(newProperties) {
    for (var propertyName in newProperties) {
        this[propertyName] = newProperties[propertyName];
    }
    return this;
};

Again, you wouldn’t need this in real ES5. The real
Object.create
allows for a second argument containing the
extensions.

You can now factor your code using the new extend method.

var Product = {
...
};

var Book = Object.create(Product).extend({
    _author: null,
    _numPages: null,
    setAuthor:   function(author) {this._author = author;},
    setNumPages: function(num_pages) {this._numPages = num_pages;},
    author:      function() {return this.author();},
    numPages:    function() {return this.numPages();}
});

Inside Prototypical Inheritance

The objects- and prototype-concept has many advantages over
static class-based approaches, like:

  • Unification of inheritance and instantiation.
    You can use the same mechanism (Object.create)
    to inherit from a prototype or build instances from it (similar to
    class usage). Actually – it is the same thing.
  • Inheritance of values. You can inherit values
    from prototypes; there is no need to set defaults in a
    constructor.
  • Runtime modification of prototypes. There is
    no difference between run-time and compile-time in JS. You can
    modify prototypes during program execution while more static
    languages only allow changes to classes before the compiler runs.
    This isn’t a direct advantage of prototypical inheritance; it’s the
    dynamic nature of JS. There are even class-based languages
    available, which allow for runtime modification of classes – Ruby
    or Smalltalk would fit the bill. But JavaScript’s
    objects-only-approach makes this much simpler – a real gain if you
    like to do any metaprogramming.

Conclusion


Object.create
provides a much more attractive way to use
prototypes, but there are still some quirks left. You’ll have to
implement your own getters/setters,
init
- and
extend
-Methods to get it working on older browsers. These
disadvantages will eventually fade away when you drop the shim and
thrive on real ECMAScript5 – which you can already do if IE8 and
below are no concern for you. Until then you can start do adopt the
new approach using
Object.create
. It may become the standard approach sooner
than you think.

Marco Emrich started professional software development in
1993 and has used many different languages and technologies. He
holds an academic degree in computer science (German Dipl. Inform.)
and did some research for the Fraunhofer IESE Institute in the area
of generative programming. Currently, he is employed by “webmasters
akademie”, a German IT training centre. There, he works as author,
trainer, software developer and project manager. He also likes
coding for fun, like for his spare time project fantasy-cards.net.

This article was originally published in Web & PHP
Magazine. Photo by Madison
Berndt
.

Author
Comments
comments powered by Disqus