O'Reilly logo

JavaScript for PHP Developers by Stoyan Stefanov

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Object-Oriented Programming

This chapter discusses the object-oriented features of JavaScript, including objects, constructor functions, and prototypes. It also talks about code reuse and inheritance.

Constructors and Classes

In PHP, if you have a Dog class, you create a $fido instance of this class using:

// PHP
$fido = new Dog();

JavaScript has a similar syntax:

// JavaScript
var fido = new Dog();

One important difference is that Dog is not a class in JavaScript because there are no classes in the language. Dog is just a function. But functions that are meant to create objects are called constructor functions.

Syntactically, there is no difference between a regular function and a constructor function. The difference is in the intent. Thus, for readability purposes, it’s a common convention to capitalize the first letter in constructor function names.

When you call a function with the new operator, it always returns an object. The object is known as this inside the body of the function. That’s what happens even if you don’t do anything special in the function. Remember that otherwise (when called without new) every function without an explicit return returns undefined:

function Dog() {
  this.name = "Fido";
  this.sayName = function () {
    return "Woof! " + this.name;
  };
}

var fido = new Dog();
fido.sayName(); // "Woof! Fido"

Note

In JavaScript, just as in PHP, parentheses are optional when you’re not passing arguments to the constructor, so this is also valid: var fido = new Dog;

If you type fido in the console, you’ll see that it has two properties: name and sayName. Some consoles also show you a special property called __proto__, but you can ignore it for now.

Take a look at the sayName property. It points to a function. Since functions are objects in JavaScript, they can be assigned to properties, in which case you can also call them methods. There’s really no difference between properties and methods in JavaScript. Methods are just callable properties.

Returning Objects

When you call any function with new, the following happens:

  1. An “empty” object referenced via this is created automatically behind the scenes:

    var this = {}; // Pseudocode, would be a syntax error if you use it
  2. The programmer adds properties to this at will:

    this.name = "Fido";
  3. this is implicitly returned at the end of the function:

    return this; // Not an error, but you don't need to use it

The programmer can interfere with step 3 by returning a different object:

function Dog() {
  var notthis = {
    noname: "Anonymous"
  };
  this.name = "Fido";
  return notthis;
}

var fido = new Dog();
fido.name;   // undefined
fido.noname; // "Anonymous"

In this example, whatever you add to this is simply destroyed when the function returns. You might as well remove it, in which case you don’t really need the magic provided by new and you can call the function as a regular function and achieve the same effect:

function Dog() {
  return {
    noname: "Anonymous"
  };
}

var fido = Dog(); // No `new` but `()` is needed this time
fido.name;        // undefined
fido.noname;      // "Anonymous"

Note, however, that returning something other than this causes the instanceof operator and the constructor property to not work as expected:

function Dog() {
  return {
    noname: "Anonymous"
  };
}

var fido = new Dog();
fido instanceof Dog;         // false
fido.constructor === Object; // true

When you use new you can return a custom object (not this), but it has to be an object. Trying to return a nonobject (a scalar value) results in your return value being ignored, and you still get this at the end:

function Dog() {
  this.name = "Fido";
  return 1;
}

var fido = new Dog();
typeof fido; // "object"
fido.name;   // "Fido"

More on This

As you know now, there’s no difference between constructor functions and regular functions other than their usage intent. So what happens if you add properties to this in a nonconstructor function? In other words, what happens when you call a constructor function (which adds to this) and forget to call it with new?

function Dog() {
  this.thisIsTheName = "Fido";
  return 1;
}

var fido = new Dog();
var one = Dog();

// fido is a regular object:
typeof fido;        // "object"
fido.thisIsTheName; // "Fido"

// one is 1, as returned from a nonconstructor function
typeof one;        // "number"
one.thisIsTheName; // undefined

// What?!
thisIsTheName;     // "Fido"

The surprise here is that by calling Dog() without new, the global variable thisIsTheName gets created. That’s because skipping new means this is a regular function invocation and now this refers to the global object. And properties added to the global object can be used as global variables. (There’s more about the global object later.)

This is dangerous behavior because you don’t want to pollute the global namespace. That’s why it’s important to use new when it’s meant to be used. It’s also important to follow the convention of naming constructors with a capital letter so you send a hint about their intended purpose to the reader of the code:

function Request() {} // Ahaa!, it's a constructor
function request() {} // Just a function

Note

This dangerous behavior is fixed in ECMAScript 5’s strict mode.

Enforcing Constructors

If you are extra paranoid, you can make sure programmatically that your constructors always behave as constructors even when the callers forget new. You can use the instanceof operator, which takes an object and a constructor function reference and returns true or false:

fido instanceof Dog; // true

Here’s one way to implement the self-enforcing constructor:

function Dog() {

  // Check if the caller forgot `new`
  if (!(this instanceof Dog)) {
    return new Dog(); // Re-call properly
  }

  // Real work starts...
  this.thisIsTheName = "Fido";
  // real work ends

  // Implicitly at the end ...
  // return this;
}

var newfido = new Dog();
var fido = Dog();

newfido.thisIsTheName; // "Fido"
fido.thisIsTheName;    // "Fido"

The line this instanceof Dog checks if the newly created this object was created by Dog. The line var fido = Dog(); didn’t use new, so this points to the global object. The global object was definitely not created by Dog. After all, it was around even before Dog, so the check fails and the line return new Dog(); is reached.

Note

You don’t really know exactly which constructor was used to create the global object because it’s an internal implementation dependent on the environment.

Another way to introspect and ask “Who created you?” is to use the constructor property that all objects have. It’s also a writable property, so it’s not really reliable, as the following example shows:

function Dog() {}
var fido = new Dog();

fido.constructor === Dog; // true, as expected
fido.constructor = "I like potatoes";
fido.constructor === Dog; // false, ... wait, what?!
fido.constructor;         // "I like potatoes"

Prototypes

The concept of prototypes doesn’t exist in PHP but is an important concept in JavaScript.

Let’s take a look at an example:

function Dog(name) { // Constructor
  this.name = name;
}

// Add a member to the `prototype` property
Dog.prototype.sayName = function () {
  return this.name;
};

var fido = new Dog("Fluffy");
fido.sayName(); // "Fluffy"

What happened here?

  1. There’s a normal function Dog, obviously created with the intent to be a constructor function because it starts with a capital D and refers to this inside its body.
  2. Behind the scenes, the Dog() function, like any other function, automatically gets a property called prototype. (You know that functions are objects, so they can have properties.) The prototype property is always created for each function, constructor or otherwise.
  3. You add a new property to the prototype property, called sayName. This property happens to be a function, so you can say it’s a method.
  4. sayName() has access to this.
  5. Creating a fido object with new Dog() gives fido access to all the properties added to the prototype property. Otherwise, if you call Dog() without new, the prototype and everything in it is ignored.
  6. fido.sayName() works fine even though sayName is not a property of the fido object.

An object can access properties and methods that don’t belong to it but instead to the object referred to as prototype of the constructor function that created the object.

Let this sink in a bit; there’s more on prototypes coming up.

Object Literals

You’ve already seen the use of object literals at several occasions in this book (e.g., when talking about representing PHP’s associative arrays in JavaScript).

Object literals are simply key-value pairs, delimited with commas and wrapped in curly braces:

var obj = {
  name: "Fluffy",
  legs: 4,
  tail: 1
};

Note

It’s not OK to leave the trailing comma after the last property because some environments (earlier Internet Explorer versions) cannot handle it and raise an error.

You can also start with some properties (or no properties at all) and add more later:

var obj = {};
obj.name = "Fluffy";
obj.legs = 4;
obj.tail = 1;

Accessing Properties

Having created an object using the object literal notation, you can access the properties using the dot notation:

var desc = obj.name + " has " + obj.legs + " legs and " + obj.tail + " tail(s)";

desc; // "Fluffy has 4 legs and 1 tail(s)"

Alternatively, you can use the square bracket notation to access properties:

var desc = obj["name"] + " has " + obj["legs"] + " legs and " +
           obj["tail"] + " tail(s)";

desc; // "Fluffy has 4 legs and 1 tail(s)"

This is not too common, because it’s longer and a little clumsy to be passing property names as strings. However, it is useful when you don’t know the property name in advance and need a variable. For example, when iterating over all properties:

var all = [];
for (var property in obj) {
  all.push(property + ": " + obj[property]);
}
var desc = all.join(', ');

desc; // "name: Fluffy, legs: 4, tail: 1"

Or, as another example, when the property name is evaluated at runtime:

var obj = {
  foo: "Foo",
  bar: "Bar",
  foobar: "Foo + Bar = BFF"
};

var fprop = "foo", bprop = "bar";

obj[fprop];         // "Foo"
obj[bprop];         // "Bar"
obj[fprop + bprop]; // "Foo + Bar = BFF"

Square bracket notation is required when the property is not a valid identifier (same for quotes around property names in an object literal):

var fido = {};
fido.number-of-paws = 4;    // ReferenceError
fido['number-of-paws'] = 4; // This is OK

Confusing Dots

Dots in JavaScript are used to access properties, but in PHP, they concatenate strings. When you’re mentally in PHP mode but you’re writing in JavaScript, you can often confuse the purpose of the dot out of habit:

// JavaScript
var world = "wrrrld";
var result = "hello " . world;

Funny enough, this is not a syntax error in JavaScript. The result contains the value undefined. This is because the space around the dot is optional, so it works like this:

"hello".world; // undefined

In other words, you’re accessing the property world of the string object "hello". String literals are converted to objects behind the scenes, as if you did new String("hello") (more about this coming soon). Since this object doesn’t have such a property, the result is undefined.

Methods in Object Literals

Can you add methods to an object using object literal notation? Absolutely—methods are just properties, which happen to point to function objects:

var obj = {
  name: "Fluffy",
  legs: 4,
  tail: 1,
  getDescription: function () {
    return obj.name + " has " + obj.legs + " legs and " + obj.tail + " tail(s)";
  }
};

obj.getDescription(); // "Fluffy has 4 legs and 1 tail(s)"

Note

In getDescription(), you can substitute obj with this.

You can add methods to an existing object at a later time:

obj.getAllProps = function () {
  var all = [];
  for (var property in obj) {
    if (typeof obj[property] !== "function") {
      all.push(property + ": " + obj[property]);
    }
  }
  return all.join(', ');
};

obj.getAllProps(); // "name: Fluffy, legs: 4, tail: 1"

Did you notice some properties were filtered out using the following?

typeof obj[property] !== "function"

Most likely, you don’t want functions showing up in the list of properties, but because functions are just like all other properties, they will show up if you don’t filter them out. You can try removing this filter to see what happens.

Note

Spoiler alert: accessing a function object in a string concatenation context converts the function object to a string. This happens by calling the toString() method, which all objects that inherit Object respond to. Function objects implement toString() by returning the source code of the function, although this is not standard and implementations vary among engines in terms of new lines and spacing.

Fancy Arrays

Overall, the obj now looks like a fancy array, or like a PHP associative array that has some of its properties acting as functions. In fact, early PHP versions didn’t have the concept of objects at all. When objects were added later, they were dubbed fancy arrays. To this day, it’s easy to go back and forth between an object and an associative array in PHP:

// PHP
$mutt = array(
  'name' => "Fluffy",
  'legs' => 4,
  'tail' => 1,
);

echo $mutt['name'];    // "Fluffy"
var_dump($mutt->name); // NULL, this is not an object

Here $mutt is an array, so accessing name as a property doesn’t work. However, you can convert $mutt to an object:

// PHP
$mutt = (object)$mutt; // $mutt is now an object
echo $mutt['name'];    // Fatal error, this is not an array anymore
echo $mutt->name;      // "Fluffy"

As you can see, associative arrays and objects are so close that JavaScript decided to go with using only objects to express both concepts.

To continue the PHP/JavaScript analogy, you can imagine JavaScript’s object literals as being associative arrays converted to objects behind the scenes. Just as if you do the following in PHP:

// PHP
$mutt = (object)array(
  "name" => "Fluffy"
);

echo $mutt->name; // "Fluffy"

The same in JavaScript:

// JavaScript
var mutt = {
  name: "Fluffy"
};

mutt.name; // "Fluffy"

Own Properties

In JavaScript, there is a distinction between properties owned by an object and properties that are inherited from a prototype object. The syntax for accessing either of those is the same, but sometimes you need to know if a property belongs to your object or if it came from someplace else.

Own properties are those added to an object using the object literal notation or via an assignment:

var literal = {
  mine: "I pwn you"
};

literal.mine; // "I pwn you"

var assigned = {};
assigned.mine = "I pwn you";

Own properties are also those added to this and returned by constructor functions:

function Builder(what) {
  this.mine = what;
}

var constructed = new Builder("pwned");
constructed.mine; // "pwned"

Notice, however, that these two objects have access to a toString() method that neither of them defined:

literal.toString();     // "[object Object]"
constructed.toString(); // "[object Object]"

The method toString() is not an own method for either of the two objects. It’s something that came from a prototype.

If you want to tell the difference between own properties and prototype properties, you can use another method called hasOwnProperty(), which takes a name of a property/method as a string:

literal.hasOwnProperty('mine');           // true
constructed.hasOwnProperty('mine');       // true
literal.hasOwnProperty('toString');       // false
constructed.hasOwnProperty('toString');   // false

literal.hasOwnProperty('hasOwnProperty'); // false

Let’s do some more introspection. Where did that toString() come from? How can you find out which is the prototype?

__proto__

Objects have prototypes, but they don’t have a prototype property—only functions do. However, many environments offer a special __proto__ property for each object. __proto__ is not available everywhere, so it’s useful only for debugging and learning.

__proto__ is a property that exposes the secret link between the object and the prototype property of the constructor that created the object:

constructed.prototype; // undefined, objects don't have this property
constructed.constructor === Builder; // true, "who's your constructor?"

// Secret link - exposed!
constructed.constructor.prototype === constructed.__proto__; // true

Chaining __proto__ calls lets you get to the bottom of things. And the bottom is the built-in Object() constructor function.

Object.prototype is the mother of all objects. All objects inherit from it. That’s where toString() was defined:

Object.prototype.hasOwnProperty('toString'); // true

You can trace down toString from the constructed object:

constructed.__proto__.__proto__.hasOwnProperty('toString'); // true

What about the literal object? Its chain is one link shorter:

literal.__proto__.hasOwnProperty('toString'); // true

This is because literal wasn’t created by a custom constructor, which means it was created by Object() behind the scenes, not by something (e.g., Builder()) that inherits from Object().

When you need a simple object temporarily, you can use ({}), and the following syntax also works:

({}).__proto__.hasOwnProperty('toString'); // true

Note

It was previously mentioned that object literals like var o = {} create “empty” objects. The word “empty” is in quotes because the objects are not really empty or blank. Every object, even if it doesn’t have any own properties, has some properties and methods already available—the ones that come from its prototype chain.

this or prototype

When using constructor functions, you can add properties to this or to the constructor’s prototype. You may be wondering which one you should use.

Adding to the prototype is more efficient and takes less memory because the properties and functions are created only once and reused by all objects created with the same constructor. Anything you add to this will be created every time you instantiate a new object.

Therefore, any members you plan to reuse and share among instances should be added to the prototype, and any properties that have different values in each instance should be own properties added to this. Most commonly, methods go to the prototype and properties to this, unless they are constant among the instances.

And talking about code reuse brings about the following question: Can you reuse code using inheritance?

Inheritance

So far you have learned:

  • How to create objects with literal notation or with constructor functions
  • What a prototype is (a property of each function)
  • Own versus prototype properties
  • Objects inherit properties from their prototypes and their prototypes’ prototypes, and so on

Now let’s talk a bit more about inheritance, because you’re probably wondering how do inheritance works in a language that doesn’t have classes. It turns out there’s more than one option to implement inheritance, depending on your goals and preferences.

Inheritance via the Prototype

The default way to implement inheritance is to use prototypes. You create one object using a parent constructor and set it as a prototype of the child constructor.

Here’s a constructor function that will be the parent:

function NormalObject() {
  this.name = 'normal';
  this.getName = function () {
    return this.name;
  };
}

And a second constructor:

function PreciousObject() {
  this.shiny = true;
  this.round = true;
}

And the inheritance part:

PreciousObject.prototype = new NormalObject();

Voila! Now you can create precious objects with all the functionality of the normal objects:

var crystal_ball = new PreciousObject();
crystal_ball.name = 'Ball, Crystal Ball.';
crystal_ball.round;     // true
crystal_ball.getName(); // "Ball, Crystal Ball."

Note

Instead of the usual Car extends Vehicle, the examples in this chapter are inspired by Jim Bumgardner’s blog post “Theory of the Precious Object”. It’s a fun read that suggests that treasured objects, such as J. R. R. Tolkien’s fictional One Ring or an iPhone, have certain common qualities.

Notice how you need to create an object with new NormalObject() and assign it to the prototype property of the PreciousObject function because the prototype is just an object. If you think in terms of classes, you know that a class inherits from another. And if you carry that thought to JavaScript, you’d expect that a constructor inherits from a constructor. But that’s not the case. In JavaScript, you inherit an object.

If you have several constructor functions that inherit NormalObject objects, you may create new NormalObject() every time, but it’s not necessary. You can create one normal object and reuse it as a prototype of the children. Even the whole NormalObject constructor may not be needed to begin with. Since you inherit an object, all you need is one object, regardless of how it’s created.

Another way to do the same is to create one (singleton) normal object using the object literal notation and use it as a base for the other objects:

var normal = {
  name: 'normal',
  getName: function () {
    return this.name;
  }
};

Then the objects created by PreciousObject() can inherit normal like this:

PreciousObject.prototype = normal;

Inheritance via Copying Properties

Since inheritance is all about reusing code, another way to implement it is to simply copy over properties from one object to another.

Imagine you have these objects:

var shiny = {
  shiny: true,
  round: true
};
var normal = {
  name: 'name me',
  getName: function () {
    return this.name;
  }
};

How can shiny get the properties of normal? Here’s a simple extend() function that loops through and copies properties:

function extend(parent, child) {
  for (var i in parent) {
    if (parent.hasOwnProperty(i)) {
      child[i] = parent[i];
    }
  }
}

extend(normal, shiny); // Inherit
shiny.getName();       // "name me"

Copying properties may look like overhead and something that can hurt performance, but for many tasks it’s just fine. You can also see that this is an easy way to implement mixins and multiple inheritance.

Note

With this pattern, instanceof and isPrototypeOf() do not work as expected.

Beget Object

Douglas Crockford, a JavaScript luminary and creator of JSON, popularized another way to implement inheritance by using a temporary constructor just to be able to set its prototype:

function begetObject(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

You create a new object, but instead of starting fresh, you inherit some functionality from another, already existing, object.

For example, say you have the following parent object:

var normal = {
  name: 'name me',
  getName: function () {
    return this.name;
  }
};

You can then have a new object that inherits from the parent:

var shiny = begetObject(normal);

The new object can be augmented with additional functionality:

shiny.round = true;
shiny.preciousness = true;

As you can see, there’s no property copying, nor any constructors in sight. A new object inherits from an existing one. This was actually embraced by the community as a good idea, and is now part of ECMAScript 5 in the form of Object.create(), as you’ll see in Chapter 6.

As an exercise in closures and optimization, can you change begetObject() so that F() is not created every time?

“Classical” extend()

Let’s wrap up with yet another way to implement inheritance, which is probably the closest to PHP because it looks like a constructor function inheriting from another constructor function; hence, it looks a bit like a class inheriting from a class.

Here’s the gist:

function extend(Child, Parent) {
  var F = function () {};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
}

With this method, you pass two constructor functions to extend(). After extend() is done, any new objects created with the first constructor (the child) get all the properties and methods of the second (the parent) via the prototype property.

Note

This method is often referred to as “classical” in quotes because it looks the closest to the idea of classes.

There are only two small things to add to extend():

  1. Have the child keep a reference to the parent, just in case.
  2. Reset the constructor property of the child to point to the child’s constructor, in case this is needed for introspection (you’ll see some more about this property in Chapter 5):
function extend(Child, Parent) {
  var F = function () {};
  F.prototype = Parent.prototype;
  Child.prototype = new F();
  Child.prototype.parent = Parent;
  Child.prototype.constructor = Child;
}

In this method, there’s no instance of new Parent() involved. This means that own properties added to this inside the parent constructor will not be inherited. Only the properties added to the prototype of the parent will be inherited. And this is OK in many scenarios. In general, you add the properties you want to reuse to the prototype.

Consider this setup:

function Parent() {
  this.name = "Papa";
}
Parent.prototype.family = "Bear";
function Child() {}
extend(Child, Parent);

The property name is not inherited, but the family is:

new Child().name;   // undefined
new Child().family; // "Bear"

And the child has access to the parent:

Child.prototype.parent === Parent; // true

Borrowing Methods

Using call() and apply() gives you an opportunity to reuse code without having to deal with inheritance at all. After all, inheritance is meant to help us reuse code.

If you see a method you like, you can temporarily borrow it, passing your own object to be bound to this:

var object = {
  name: "normal",
  sayName: function () {
    return "My name is " + this.name;
  }
};

var precious = {
  shiny: true,
  name: "iPhone"
};

If precious wants to benefit from object.sayName() without extending anything, it can simply do the following:

object.sayName.call(precious); // "My name is iPhone"

If you combine method borrowing and classical inheritance, you can also get both own and prototype properties:

function Parent(name) {
  this.name = name;
}
Parent.prototype.family = "Bear";

function Child() {
  Child.prototype.parent.apply(this, arguments);
}

extend(Child, Parent);

All this properties become own properties of the child. And via the magic of arguments and apply(), you can also have arguments passed to the constructors, if you so desire:

var bear = new Child("Cub");
bear.name; // "Cub"
bear.family; // "Bear"
bear.hasOwnProperty('name'); // true
bear.hasOwnProperty('family'); // false

Conclusion

As you can see, there are many options available for implementing inheritance. You can pick and choose depending on the task at hand, personal preferences, or team preferences. You can even build your own solution or use one that comes with your library of choice. Note, however, that deep inheritance chains are not too common in JavaScript projects because the language allows you to simply copy properties and methods of other objects or “borrow” them to achieve your task. Or, as the Gang of Four’s Design Patterns (Addison-Wesley, 1994) says: “Prefer object composition to class inheritance.”

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required