Pages

Sunday, October 2, 2016

Objects With Eyes That Age (JavaScript)

Objects That Age (JavaScript) explains how a JavaScript constructor function and the value of the function's prototype property can be used to construct new instances of objects that have their __proto__ property set to an initial value other than the the default value—namely, the value of Object.prototype.

Another way to create an instance of an object with a custom __proto__ property value is to call the method Object.create, to which at least one argument must be passed. If the value of the argument passed to the method is valid (assuming one argument is passed), then Object.create returns a new object with its __proto__ property set to the value of the argument passed. The following image illustrates a few ways that Object.create may and may not be called to create new objects.

Following are the constructor function, the associated prototype property, and the particular property value that were presented in Objects That Age (JavaScript):
function ObjectThatAges() {
  this.createDate = new Date();
}
ObjectThatAges.prototype = {
  ageInMs: function() {
    return new Date() - this.createDate;
  }
};
And here is the constructor function that was presented in Objects With Eyes (JavaScript):
function ObjectWithEyes(EyeColor) {
  this.eyeColor = EyeColor;
}
Borrowing a keyword from the Java programming language, it's not a stretch to say that we can create a constructor function called ObjectWithEyesThatAges that extends, so to speak, the constructor ObjectThatAges. The following implementation of ObjectWithEyesThatAges may be used to produce objects belonging to a class of objects that is a subclass of the superclass of objects produced by ObjectThatAges.
function ObjectThatAges() {
  this.createDate = new Date();
}
ObjectThatAges.prototype = {
  ageInMs: function() {
    return new Date() - this.createDate;
  }
};

function ObjectWithEyesThatAges(EyeColor) {
  ObjectThatAges.call(this);
  this.eyeColor = EyeColor;
}
ObjectWithEyesThatAges.prototype = Object.create(ObjectThatAges.prototype); 
In the code snippet above, note the use of the method Function.prototype.call() coupled with the keyword this to “chain constructors” when creating an instance of an object of type ObjectWithEyesThatAges. As described in an MDN article about this: “[i]nside a function, the value of this depends on how the function is called.” If we called ObjectThatAges as follows from within the function ObjectWithEyesThatAges,
function ObjectWithEyesThatAges(EyeColor) {
  ObjectThatAges(); // DON'T DO THIS!
  this.eyeColor = EyeColor;
}

then the value of this might default to the global object (possibly the Window object depending on the context of the code), or the value of this might “remain at whatever it's set to when entering the execution context” (if the JavaScript code is being interpreted in strict mode).

When using the function ObjectThatAges as a constructor, we definitely want to create a property called createDate on the object we're creating and set the value of that property equal to a Date object representing the current date and time. We absolutely do not want to risk creating a new property called createDate on, say, the Window object! So how do we ensure that our constructor works as intended?

As usual, we need to be careful. When creating a new object of type ObjectThatAges in isolation, we would use the function ObjectThatAges as a constructor by preceding the function name with the keyword new when calling the function: new ObjectThatAges(), for example. But when chaining constructors together (e.g. invoking the function ObjectWithEyesThatAges as a constructor and, within that constructor involving ObjectThatAges as a constructor), we don't want to create more than one object, so we can't just use the keyword new when calling the constructor for a superclass of objects from the constructor for a subclass of objects! Fortunately, the method Function.prototype.call() lets us pass in a value of this to be used by the function we're calling, and that's the trick we use “to chain constructors together” when we call ObjectThatAges, within the context of the function ObjectWithEyesThatAges, like this:
function ObjectWithEyesThatAges(EyeColor) {
  ObjectThatAges.call(this); // DO THIS INSTEAD
  this.eyeColor = EyeColor;
}

This post was initially titled Objects With Aging Eyes, hence the permalink (URL) value. Objects With Eyes That Age is a better title, IMHO. Maybe Aging Objects With Eyes would be even better (Who Knows?), but there comes a time in every newly constructed object's life when she has a raging desire to move on; hence the current title. See also: Objects With Eyes That Age, Objects That Age, and Objects With Eyes