General Assembly Logo

JavaScript Objects: Referencing Own Properties

Prerequisites

  • Creating object literals.

Objectives

  • From within a method, reference properties of the same object using this.

this

Self-Referential Objects

As you might have gathered from the last lab, one way to break up the complexity of a problem is by using multiple kinds of objects together, and having each object be responsible for representing a small part of the problem. But these objects don't need to exist in isolation - objects can have other objects (or even collections of other objects) as properties.

Suppose that we wanted to create a simple app ('RunTracker') that helps people prepare for running a 5k. Each day that a person runs, they create a record of their run which contains

  • the date and time of the run
  • the distance covered, in meters
  • the time taken, in seconds

The app also stores information about the user - the user's name and email address. And it can perform some calculations - total distance run, longest run so far, average speed, and total distance run.

Based on this problem, the kinds of abstractions that we probably want to use are Users and Runs. Why? Because each one represents a 'thing' that we can interact with, complete with attributes and behaviors. And based on how we've constructed this problem, we will presumably want our User to have an array of RunRecords that represent that user's runs.

// 'user'
{
  name: "Person McFace",
  email: "wdi@personmcface.com",
  runs : [
    {
      date: "2015-05-25 15:00",
      distance: "1200",
      timeTaken: "600",
    }
  ],
  totalDistance : function(){ ... },
  longestRun : function(){ ... },
  averageSpeed : function(){ ... }
}

However, when we start thinking about how the methods for 'User' will work, we run into a difficulty. A method for calculating the longest run so far needs to be able to see, and refer to, all of the Runs associated with that particular User. How do we do that?

JavaScript gives us a tool for just this purpose; a special reference called this. In general, when called from inside some object's method, this refers back to that object, allowing our methods to use and manipulate other properties on the object.

this acts differently when it's not used inside an object, but we won't be discussing that right now.

In the specific case of our 'RunTracker' app, here's how our methods might look:

  ...
    totalDistance : function(){
      var totalDist = 0;
      for (var i = 0; i < this.runs.length; ++i) {
        totalDist += this.runs[i].distance;
      }
      return totalDist;
    },
    longestRun : function() {
      var longest = this.runs[0].distance;
      for (var i = 0; i < this.runs.length; ++i) {
        if (this.runRecords[i].distance > longest) {
          longest = this.runs[i].distance;
        }
      }
      return longest;
    },
    averageSpeed : function() {
      var totalTime = 0;
      for (var i = 0; i < this.runs.length; ++i) {
        totalTime += this.runs[i].timeTaken;
      }
      if (totalTime !== 0) {
        var totalDistance = this.totalDistance();
        return totalDistance / totalTime;
      } else {
        return 0;
      }
    }
  ...

Lab :: Self-Referential Objects

Open up the lib/meals.js file. In pairs, you're going to work a similar app to the one mentioned in the example - this time for meal tracking. In particular, you're going to create an example 'User' object, complete with several 'Meals'.

A 'User' needs to have:

  • a name (name)
  • an age (age)
  • a target daily calorie intake (calorieTarget)
  • a list of 'Meals' that they've eaten (meals)

Every 'Meal' must have:

  • a title (title), e.g. 'breakfast', 'lunch', 'dinner')
  • a date (date), represented as a string e.g. "2015-06-25"
  • a description (description)
  • a number of estimated calories (calories)

Then, create the following method for 'User'

  • caloriesEatenOn, which accepts a date (in the format above) and calculates the total number of calories consumed on that date.
  • avgDailyCalories, which (as indicated), calculates the average number of calories consumed per day, rounded down to the nearest whole calorie.
  • onTrack, which compares averageDailyCalories to the User's target daily calorie intake, and returns true if average caloric intake is at or below the target (or false if the reverse is true)

Source code distributed under the MIT license. Text and other assets copyright General Assembly, Inc., all rights reserved.