Dilemma : What is the best way to create an object in JavaScript ?

JavaScript is a powerful but weird programming language. Syntactically it carries a lot of baggage from class based Java and ultimately C++,  even though it is not a class based language. It is an object based language. Everything is an object (like Ruby). The most commonly used syntax for defining and creating objects goes like

 var Person = function(firstName, surname){
  this.firstName = firstName
  this.surname = surname
  this.fullName = function(){
    return firstName + surname
  }
}
var person = new Person("John", "Doe")
console.log(person.firstName)
console.log(person.surname)
console.log(person.fullName())

For a person from a Java/C++ background, the above code is perfectly idiomatic because that is how you would do it in those languages.
However there are subtle issues with it. For instance if a novice JavaScript developer just forgets to add a ‘new’ and says

var person = Person("John", "Doe")

Then you have an issue. The code would execute (maybe with a console error saying that it can’t find a property called firstName for undefined). The bigger issue is when you say

 this.firstName = firstName

The ‘this’ refers to the calling object, in this case the window(assuming you are on a browser). The window object now has a firstName and a lastName. You have effectively polluted the global object. There are many ways of getting around the problem and this Stackoverflow post gives a number of them. One of the solutions which we used on a project was

 var Person = function(firstName, surname){
  var person = {}
  person.firstName = firstName
  person.surname = surname
  person.fullName = function(){
    return firstName + surname
  }
  return person
}
var person = Person("John", "Doe")
console.log(person.firstName)
console.log(person.surname)
console.log(person.fullName())

As can be seen, there is no ‘new’ keyword. Instead we use a JavaScript object literal and a closure to achieve the same effect. Also there is no ‘this’ keyword. Instead we use a locally scoped person variable in the closure to hold the reference to the object literal. We have made creating an object ‘safer’ than before. The Java and C++ guys will now say that it is not idiomatic. To a certain extent it is not; Person can be a function or a constructor function. Without looking at the definition of Person, it is impossible to know. One might also argue if we use the ‘new’ keyword, catch an unsafe call early via the safety net of unit testing. Advantage ‘new’ keyword.

Let me add a new requirement. I want to add a ‘static’ property on Person. I want to say

 console.log(Person.species)

Both the above approaches will fail to accomodate a ‘static’ property or function. A way to solve all the problems could be

var Person = {
 species: "Homo Sapiens",

New: function (firstName, surname) {
 var person = {}
 person.firstName = firstName
 person.surname = surname
 person.fullName = function () {
 return firstName + surname
 }
 return person
 }

}
var person = Person.New("John", "Doe")
console.log(person.firstName)
console.log(person.surname)
console.log(person.fullName())
console.log(Person.species)

The above code is very idiomatic to Ruby developers. Yes there is an uppercase N for the new method (because new is a keyword in JavaScript), but other than it looks fine. The Java and C++ developers’ concerns will be pacified by the explict usage of ‘New’. The Person is now a JavaScript module of sorts, but not quite. The species ‘static property’ can not be used from with the New function. To do this, we can go modify the definition to follow the module pattern.

var Person = (function () {
var species = "Homo Sapiens"

var New = function (firstName, surname) {
var person = {};
person.firstName = firstName
person.surname = surname
person.fullName = function () {
return firstName + surname
}
person.toString = function () {
return ["Name:", firstName,
"Surname:", surname,
"Species:",species]
.join(' ')
}
return person
}

return {
New: New,
species: species
}

}())
var person = Person.New("John", "Doe")
console.log(person.firstName)
console.log(person.surname)
console.log(person.fullName())
console.log(person.toString())

Now the variable species can be used from anywhere in the closure including the toString of the person object.

There are other ways of creating JavaScript objects (the internet is full of them). You can choose whichever way you like, but make sure that there is one way of doing it per codebase. It will make navigating through the codebase easier.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s