DEV Community

Martin Himmel
Martin Himmel

Posted on

JavaScript (ES5) Objects

This was originally posted on my site at https://martyhimmel.me on January 31, 2017. Like a number of others on dev.to, I've decided to move my technical blog posts to this site.

While discussing data types, we compared an object to a dictionary - a set of terms (properties or keys) and their definitions (values). Another way to think of an object is as a container for something. Here's an example:

var person = {
  firstName: 'John',
  lastName: 'Smith',
  age: 24,
  isMarried: true,
  children: ['Jack', 'Judy'],
  pet: {
    type: 'dog',
    name: 'Fido'
  }
};
Enter fullscreen mode Exit fullscreen mode

In this example, the person object contains several different data types, including another object. Accessing each of those properties is done with dot notation.

console.log(person.firstName); // John
console.log(person.age); // 24
console.log(person.children[0]); // Jack
console.log(person.pet.name); // Fido
Enter fullscreen mode Exit fullscreen mode

Objects can also contain functions. Here's an example, continuing with the person object:

var person = {
  firstName: 'John',
  lastName: 'Smith',
  ... // The other properties
  getFullName: function() {
    return person.firstName + ' ' + person.lastName;
  }
};
console.log(person.getFullName()); // John Smith
Enter fullscreen mode Exit fullscreen mode

Just like the other properties of an object, you declare the property name and give it a value. In this case, the value is a function.

this

The getFullName function could be written in a slightly different way, using the this keyword.

var person = {
  ...
  getFullName: function() {
    return this.firstName + ' ' + this.lastName;
  }
}
console.log(person.getFullName()); // John Smith
Enter fullscreen mode Exit fullscreen mode

The result is the same. this is a reference to the current object. I'll save the details of how this works for a separate tutorial, as it can be a bit confusing until you understand all the rules for it. For this tutorial, we'll keep it simple though, and this will always refer to the current/containing object.

Creating Objects with Constructor Functions

Up to this point, we've been manually creating every object. That's fine, but it makes for a lot of repetitious code. If we wanted 10 person objects, we'd have to create 10 separate objects.

var person1 = {
  firstName: 'John',
  lastName: 'Smith'
};
var person2 = {
  firstName: 'Jane',
  lastName: 'Doe'
};
// and so on
Enter fullscreen mode Exit fullscreen mode

There's a principle in programming called "DRY" - Don't Repeat Yourself. If you can avoid duplicating code (sometimes, you can't or don't necessarily want to), it makes the code easier to maintain. So, how do we use that principle here?

You'll notice each of our person objects have the same properties - firstName and lastName. They could have all the properties from the first example, if we wanted, but we'll keep it simple here. In any case, the same code is repeated in creating every object.

This is where constructor functions come in handy. A constructor function is a function that produces objects. A common naming convention is to capitalize the first letter of a constructor function. This sets it apart from other functions. Otherwise, a constructor function is created in exactly the same way as any other function. Now, let's convert the above example.

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
var person1 = new Person('John', 'Smith');
var person2 = new Person('Jane', 'Doe');
// and so on
console.log(person1.firstName + ' ' + person1.lastName); // John Smith
console.log(person2.firstName + ' ' + person2.lastName); // Jane Doe
Enter fullscreen mode Exit fullscreen mode

Using the Person constructor gets rid of the repititious code of assigning the first and last name to each object. Imagine if each person had 10 or 15 properties. That would be a lot of repeated code. Now imagine you had 500 people, then found a bug or needed to change some property or method of the object. You'd have to make 500 changes. Using a constructor function, you only have to change it in one place. This is why the DRY principle is important.

The Person constructor takes two arguments - firstName and lastName. The use of this inside the function is used to assign the values passed to the given property name. In other words, the firstName argument passed to the constructor is assigned to the firstName (this.firstName) property of the created object.

Calling new is what creates the actual object. If we look at the person1 object that was created, it looks like this:

console.log(person1); // {firstName: 'John', lastName: 'Doe'}
Enter fullscreen mode Exit fullscreen mode

Object Prototypes

In this section, we'll continue using the above Person constructor as our base. It would be convenient to have a getFullName method - like in the first section. That's where prototypes come in.

JavaScript is a prototype based language. You can read about it in depth in Mozilla's developer docs.

Every object in JavaScript has a prototype. Logging an object to the console gives more info than just the object itself. So, a real view of console.log(person1); would give us this (using Chrome):

Person
  firstName: "John"
  lastName: "Smith"
  __proto__: Object
    constructor: Person(firstName, lastName)
      // a handful of other properties
      prototype: Object
        // more properties
    __proto__: Object
      // a bunch of properties inherited from the prototype chain
Enter fullscreen mode Exit fullscreen mode

As you can see, there's a whole lot more going on than just the two properties we created in the constructor. That's all part of the prototype structure of JavaScript objects. The prototype allows objects to inherit properties from other objects. This also means we can retrofit objects with new methods by tapping into the prototype property of the constructor.

The format for adding methods via the prototype chain is:

[constructor name].prototype.[method name] = function() {
    // do something
};
Enter fullscreen mode Exit fullscreen mode

Let's add the getFullName method.

Person.prototype.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
};
console.log(person1.getFullName()); // John Smith
Enter fullscreen mode Exit fullscreen mode

Now that the Person constructor has a getFullName method, that method is available to every instance - person1, person2, and any other objects that may have been created with the constructor. And because of the way prototypes work, even if a Person object was created before the prototype method was added to the constructor (as is the case in this example - write the constructor, create two objects, add the prototype method), that method is still available to all objects created with the given constructor.

Top comments (0)