Angular shitstorm: What's your opinion on the controversial plans for Angular 2.0?
Objectify

Behaviour Driven Development for JavaScript: Part Two

MarcoEmrich
BDD

In the concluding part of a two part series, Marco Emrich shows you how you can use JavaScript as a vehicle to carry you into the wonderful world of BDD.

Marco Emrich is an author, trainer, software developer and project manager with an academic degree in computer science. He speaks at various IT-conferences (OOP, Seacon, Webtech, SoCraTes), writes articles (ObjektSpektrum, RailsWay, PHP & Web Magazine) and facilitates code retreats. This extract is taken from his eBook Behaviour Driven Development with JavaScript, which was released by our sister publishing arm, Developer.Press. 

This article is the second in a two part series – to get up to speed, you can find Part One here. 

JavaScript is object oriented

JS is an object oriented language; this differs from being class oriented. If you have a background in industrial languages like C++ or Java, you might think JS is missing something. I thought so for a long time too.

Actually popular languages like C++ or Java have less powerful implementations of OOP-concepts, which they inherited from the mighty Smalltalk. The development of OOP did not even stop with Smalltalk. There are many languages that took the concepts from Smalltalk even further. These concepts are mostly used only in academia.

One improvement was to get rid of classes. Languages like Cecil, Self or more recently IO experimented with a prototypical approach, which also found its way into JS.

You might still argue that JS does not have the best implementation of prototyping and an ugly syntax compared to languages like Self. The prototypical inheritance concept itself is, however, a great improvement. 

The old way

The old way of creating objects was to supply a constructor function. This was targeted at Java and C++ developers to provide them with a coding style that they were used to. But the constructor function also had its downsides. Therefore ECMAScript5 introduced a better way for creating objects.

So how did it look before ECMAScript5 stepped in? Let’s assume you have built a shop and now you need to start selling various products online. In the old days, 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(); 

Constructor function (javascript_refresher/product_in_ecma3.js)

Actually, this isn’t that 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 JS language. The value of name can be changed at 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 concerns, many developers have created libraries, frameworks and tools that provide all types of object creation and instantiation logic. Many of them introduced classes (for example Prototype5 or Coffeescript6).

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 JS 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 (MDN). A polyfill is a technique to make modern features available to older browsers. You can find other polyfills on the Modernizr-Wiki.

The Object.create-polyfill from MDN allows you to use the new way of creating objects in older browsers, such as IE8. This is important when you consider that IE7/IE8 still has a combined market share of about 7% (January 2013). It is not an issue if you only target IE9+, 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 listing 1.6 to ensure that you have the 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 either Kris Kowal’s or David de Rosier’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: 99.50,
name: 'Grindle 3'
};

However, this isn’t the best option. You can manipulate the properties of the objects easily from the outside but there isn’t a way to check or transform the assigned values. Take a look at this assignment: 

MyProduct.price = -20;

A mistake like this one would mean 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 taught 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 [Stefanov 2010]). 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, for example _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 (for example. in [Stefanov 2010]). My opinion is that they aren’t worth the effort, most of the time.

Provide a setter method that starts with “set”, for example 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, for example 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 might be for internal use only, or you want it to be ready-only. In this case just leave out the appropriate methods. So listing 1.8 shows a better implementation.

var myProduct = {
_price: 99.50,
_name: 'Grindle 3',
price: function() {return this._price;},
name: function() {return this._name;},
setPrice: function(p) {this._price = p;},
}; 

Solo object with getters and setters (javascript_refresher/product_with_getters_and_setters.js). If you want to check the price before setting it, you can now easily do this:

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

setPrice-method with check (javascript_refresher/product_with_price_check.js).

The real ECMAScript5 implementation is even nicer (listing 1.10). It has the added benefit of calling the getters and setters implicitly, for example myProduct.price = 85.99. Finally, JS supports the uniform access principle! In this tutorial however, we will stick to the first workaround mentioned since you can’t backport this language feature.

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 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);
var product2 = Object.create(Product);
product2.setName('yPhone 7');
product2.setPrice(599.99);

There is also the convention to start such a prototype with an upper letter, like classes in more traditional languages (i.e. var Product instead of var product).

Initializers

In order to set all attributes of a new product object to their correct values, you would need to call all its settermethods – a major inconvenience. You should build an initializer-method instead. Such a method could be compared to a constructor- method in other languages.

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

Even better, overwrite the create-Method of Product to encapsulate creation and initialization.

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

A great advantage of the prototypical approach is the unification of instantiation and inheritance. You don’t need anything special you can 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 – author and numPages – with getters and setters.

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 (listing 1.15). Again, you wouldn’t need this in real ES5. The real Object.create allows for a second argument containing the extensions.

tip: Alternative implementations of Object.extend are available in jQuery (http://api.jquery.com/jQuery. extend) and Underscore.js (http://underscorejs.org/#extend). If you already have one of these libs in your project, it makes sense to use them instead.

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

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

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 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();}
});
console.log(Product);
console.log(Book);

(javascript_refresher/product_inheritance_with_extend.js).

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 prototypes 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 that allow for runtime modification of classes – Ruby or Smalltalk would fit the bill. But JavaScript’s objects-only-approach makes this much simpler.This is a big gain if you like to do any metaprogramming.

The best advantage of the “objects only” approach is its simplicity. Let’s take a look at the inner workings: First of all, you don’t need any special handling for methods. Methods are just object properties that happen to contain functions. So it doesn’t matter if you look up a simple number or call a method. Now take a look at how JS decides which method to call. Listing 1.17 demonstrates the principle.

var Product = {
init: function(name) {
this._name = name;
return this;
},
_name: '',
name: function() { return this._name; },
setName: function(n) { this._name = n; }
};
var Book = Object.create(Product).extend({
init: function(name, author) {
Product.init(name);
this._author = author;
return this;
},
author: null,
setAuthor: function(author) { this._author = author; },
author: function() { return this.author(); }
});
var myBook = Object.create(Book).init('Lords of the Rings', 'J.R.R. Tolkien');
myBook.mostImportantHobbit = "Frodo"; 

If you try to get the value of myBook.mostImortantHobbit, the JavaScript-engine just takes a look at the myBookobject and returns the value.

A look-up for myBook.name() requires more steps. The JS-engine didn’t find the name-property on the myBookobject, so it needs to look it up in its prototype Book. It isn’t there either, so it follows up the prototype-chain till it finds it. The property name actually is available in Book’s prototype Product. So JS interprets the parenthesis and calls the function contained in name. The function gets executed in the context of the myBook. Therefore this._name refers to the value “Lord of the Rings”. Even if JS does need to execute several steps, they are easy to understand. Always follow the prototype-chain.

Instantiation and inheritance do not need to be handled differently.

Other things to consider

In a real project there are many other things to consider. You usually want to keep your code in namespaces. To manage your namespaces and file dependencies you might want to use a tool that provides AMD (Asynchronous Module Definition). RequireJS or curl are popular ones.

You might even like to use one of the bigger base frameworks like Ember, Backbone or AngularJS. I won’t delve into these things here, since they are not necessary to understand behaviour driven development. I urge you to really take a look into better ways to structure you code bases. It can make a big difference.

Alternate styles

JS is a very flexible language, supporting various programming paradigms (at least functional and object oriented). This allows for many different approaches to software design and development. You might like a pure functional approach, or do OOP with prototypes, or use a library for class-based object-orientation instead. You might consider using mixings/traits or other advanced constructs. Perhaps you will consider using a preprocessor/transpiler like Harmonizr for writing ECMAScript6/Harmony Code, or even trying out CoffeeScript. For the purpose of this tutorial, it doesn’t matter. The behaviour driven approach should work using any of these traits or mixings.

So take my JS-style here with a pinch of salt; I kept it a little bit simpler than in real projects. Consider it a vehicle to carry you into the wonderful world of behaviour driven development.

 

 



 



 

 

 








 



 

 




 

 



 


Author
Comments
comments powered by Disqus