I was always understood JavaScript as a functional programming language, but it turns out there are in fact ways to create basic objects and even “object factories” (what Rubyists know as Classes).

To better understand how to use objects in JavaScript, I ordered two books: Speaking JavaScript by Dr. Axel Rauschmayer and Pro JavaScript Design Patterns by Ross Harmes and Dustin Diaz. (I know the more canonical choice would have been JavaScript: The Good Parts, but a Flatiron T.A. pointed me to Speaking as a more up-to-date alternative.)

I went in to this little adventure assuming JavaScript would be much less intuitive than Ruby, and that I’d mostly be learning an entirely different system than Ruby thanks to its many quirks. But now that I’m about halfway through Speaking I’m realizing JavaScript isn’t so messy! In fact, given how much it can do, it’s not so bad.

Here’s Rauschmayer on the question of whether JavaScript is elegant:

Yes and no. I’ve written fair amounts of code in several programming languages from different paradigms. Therefore, I’m well aware that JavaScript isn’t the pinnacle of elegance. However, it is a very flexible language has a reasonably elegant core, and enables you to use a mixture of object-oriented programming and functional programming.

In this blog post I’ll explore the very basics of object-oriented JavaScript, since that aspect of the language is new to me. There may be some mistakes– I’m new to this pattern (if you see anything incorrect or weird please shoot me a note!). All 3 code examples are up on GitHub.

I’m going to lay out a basic Animal prototype (like a Ruby class) with variables and methods, then create two instances of it. This “prototype” (lowercase ‘p’) pattern seems to be just one way or pattern of creating an “object factory” in JavaScript. Maybe there’s a better or more Ruby-like alternative, but this way seems to work and makes sense to me so let’s run with it at least for today.

Creating a Prototype in JavaScript

This sample code is broken into two parts: the prototype’s definition and the instantiation of the prototype and manipulation of those instances.

var Animal = function (name, type, sound) {  
  this.name = name;
  this.type = type;
  this.sound = sound;
};

Animal.prototype = {
  makeNoise: function () {
    return this.sound + " is the sound that " + this.name + " makes!";
  },
  getWeightAtAge: function(age) {
    if (this.type === "dog"){
      return 5 * age;
    } else if (this.type == "fish"){
      return 2 * age;
    } else {
      return age;
    }
  },
  toString: function () {
    return this.name + " is a " + this.type;
  }
  
}

OK, let’s start at the top. I define a new var called Animal and set it equal to function that has no name and takes three parameters (name, type, sound).

Aside About Creating JavaScript Functions in General

This is just one of three ways to create a function in JavaScript– it’s called a “function expression”. (Notice the semi-colon after the closing curly-brace.)

Alternatively, we could create this Animal function via a function declaration, which would look like:

function Animal (name, type, sound) { 
  this.name = name;
  this.type = type;
  this.sound = sound;
}

There are advantages to both, but Rauschmayer lists two advantages of declarations: they are “hoisted” to the top of the scope (so you can call a function before declaring it if you’re in the same scope) and they by definition are named.

But we’re going to go with the function expression for now.

Back to our Prototype Definition

This Animal function will serve a specific purpose for us. It’s basically the initializing method of our prototype. New instances of Animal will take in 3 arguments, and then we set them equal to this.name, this.type, and this.sound so that we can use them later.

Here is what, in my mind, would be the Ruby equivalent:

def initialize(name, type, sound)
  self.name = name
  self.type = type
  self.sound = sound
end 

The only big difference here, besides the use of this rather than self, is that in JavaScript this initializing function goes outside of the prototype definition, as opposed to Ruby where it goes within the Class definition. Kind of strange/counter-intuitive, but not so crazy.

Next we open up the Animal prototype and define three methods (functions that belong to instances of the object).

Animal.prototype = {
  makeNoise: function () {
    return this.sound + " is the sound that " + this.name + " makes!";
  },
  getWeightAtAge: function(age) {
    if (this.type === "dog"){
      return 5 * age;
    } else if (this.type == "fish"){
      return 2 * age;
    } else {
      return age;
    }
  },
  toString: function () {
    return this.name + " is a " + this.type;
  }
  
}

The above pattern is very similar to the pattern of defining an “object literal”. An object literal is a basic object– it’s not a factory for objects, it’s just one, single object.

What is a JavaScript Object, Anyway?

Let’s go back to the basics here. From Rauschmayer, chapter 17:

Roughly, all objects in JavaScript are maps (dictionaries) from strings to values. A (key, value) entry in an object is called a property. The key of a property is always a text string. The value of a property can be any JavaScript value, including a function. Methods are properties whose values are functions.

So basically they’re kind of like Ruby hashes that can hold functions as values. Oh and JavaScript doesn’t have the equivalent of Ruby symbols, so we roll with strings as keys.

Quick Example of an Object Literal in JavaScript

Again, an object literal seems to be the simplest type of JavaScript object, so let’s take a look at one. Remember, it’s not a factory for object, it’s just one, single object.

var sam = {
  name: "Sam",

  introduce: function(){
    return "Hi, my name is " + this.name;
  }
}

introduce is a method on the object sam. So when we call print(sam.introduce()); we get Hi, my name is Sam. (Note: we can also call sam.name and even reset sam.name by running sam.name = "Theodore" outside of the var sam definition). This shows a key difference between JavaScript and Ruby– we don’t need to specify if a variable should be readable or writable via attr_reader, attr_writer, or attr_accessor. sam.name just works.

Of course we can not instantiate new instances of sam– it’s just a single object. For that “factory” feature, let’s return to the prototype.

Now that we’ve (1) taken in three variables on initialization and (2) declared 3 methods (instance methods in Ruby-speak), we’re ready to make some Animal instances and see what they can do.

var ziggy = new Animal("Ziggy", "dog", "Woof!");
var nemo = new Animal("Nemo", "fish", "bubble!");

print(ziggy.name + " is a " + ziggy.type + " and makes the sound " + ziggy.sound);

print(ziggy.makeNoise());
print(nemo.makeNoise());

print(nemo.toString());

print("When " + ziggy.name + " is 7 she'll be " + ziggy.getWeightAtAge(7) + " pounds!");

print("Setting Nemo's type to shark...");
nemo.type = "shark";
print("Now " + nemo.name + " is a " + nemo.type);

A Quick Note on How to Actually Run This Code

As you can see I’m using the print function to display information to the screen. That’s because I’m using the V8 JavaScript engine via my command line to run these JavaScript programs. to do that I just run v8 app.js from the Terminal. I wish I remembered how I installed it or if I even needed to install it on my Mac, but here is what looks like some good installation instructions from Google.

Back to the Animals

Instantiating a new Animal is pretty simple: var ziggy = new Animal("Ziggy", "dog", "Woof!"); (Ziggy is the name of our first family dog). I go on to test methods like ziggy.sound and nemo.toString() and they work just as expected.

Methods can take arguments if they accept parameters, as we can see by the call to getWeightAtAge(7). And we can even write over variables with nemo.type = "shark";.

All Together, With Its Ruby Equivalent

Now let’s look at the full code in JavaScript:

var Animal = function (name, type, sound) {  // this could also be `function Animal (name, type, sound) { ` 
  this.name = name;
  this.type = type;
  this.sound = sound;
};

Animal.prototype = {
  makeNoise: function () {
    return this.sound + " is the sound that " + this.name + " makes!";
  },
  getWeightAtAge: function(age) {
    if (this.type === "dog"){
      return 5 * age;
    } else if (this.type == "fish"){
      return 2 * age;
    } else {
      return age;
    }
  },
  toString: function () {
    return this.name + " is a " + this.type;
  }
  
}


var ziggy = new Animal("Ziggy", "dog", "Woof!");
var nemo = new Animal("Nemo", "fish", "bubble!");

print(ziggy.name + " is a " + ziggy.type + " and makes the sound " + ziggy.sound);

print(ziggy.makeNoise());
print(nemo.makeNoise());

print(nemo.toString());

print("When " + ziggy.name + " is 7 she'll be " + ziggy.getWeightAtAge(7) + " pounds!");

print("Setting Nemo's type to shark...");
nemo.type = "shark";
print("Now " + nemo.name + " is a " + nemo.type);

And here’s how I would do the same thing in Ruby:

class Animal 
  attr_accessor :name, :type, :sound

  def initialize(name, type, sound)
    self.name = name
    self.type = type
    self.sound = sound
  end 

  def make_noise
    "#{self.sound} is the sound that #{self.name} makes."
  end

  def get_weight_at_age(age)
    if self.type == "dog"
      5 * age
    elsif self.type == "fish"
      2 * age
    else 
      age
    end
  end

  def to_s
    "#{self.name} is a #{self.type}"
  end 

end 

ziggy = Animal.new("Ziggy", "dog", "Woof!")
nemo = Animal.new("Nemo", "fish", "bubble!")

puts ziggy.name + " is a " + ziggy.type + " and makes the sound " + ziggy.sound
puts ziggy.make_noise
puts nemo.make_noise

puts nemo.to_s

puts "When " + ziggy.name + " is 7 she'll be " + ziggy.get_weight_at_age(7).to_s + " pounds!"

puts "Setting Nemo's type to shark..."
nemo.type = "shark";
puts "Now " + nemo.name + " is a " + nemo.type

Differences of Note

I was surprised how similar the programs turned out to be. Small differences: (1) More necessary punctuation (mostly parenthesis and semi-colons) in the JavaScript, (2) explicit returns in JavaScript.

Some larger things I ran into to:

(1) In the JavaScript version, the result of ziggy.getWeightAtAge(7) was coerced into a string because I was calling it within a string with a print. In Ruby I had to use the .to_s method on the result of the get_weight_at_age method to avoid an error.

(2) As I mentioned above, in JavaScript the “initializing” function goes outside of the prototype definition, whereas in Ruby there’s the pretty-intuitive def initialize. As much as I struggle to spell “initialize” correctly almost every time I declare a new class, I’ll give a point to Ruby here for semantics.

(3) In Ruby we have control of which variables are accessible outside of the class definition and how accessible they are (attr_reader vs. attr_writer vs. attr_accessor). In JavaScript, at least by default, all initialized variables are available and over-writable outside of the prototype definition, as if they had been declared attr_accessors in Ruby.

(4) In JavaScript the method definitions are separated by commas, which kind of sucks if you’re re-ordering them often. But again, nothing crazy.

Again, here’s all of the code on GitHub.