Sneaky Abstractions

Subscribe to my Feed, follow me on , recommend me on Working With Rails or see my code on GitHub

JavaScript Eye for the Ruby Guy

This article is an introduction to JavaScript for those who already know Ruby. Its focus is mostly on the differences between the two. JavaScript and Ruby are very similar in many respects, but they’re also very different in some areas. Both languages are highly dynamic, allowing you to change objects and methods at runtime. Both languages are very object-oriented, but with different approaches. Ruby is a class-based, object-oriented language with functional aspects while JavaScript is a functional, object-oriented language with some class-like functionality.

Objects and types

While both Ruby and JavaScript have the object as the most important part of the language, there are some differences in how they use objects.

Ruby and JavaScript are both dynamically typed and often employ what is known in the Ruby world as duck typing instead of checking for certain types. We don’t care what type the object is, just that it knows how to do what we want it to do.

#Ruby
animal.bark if animal.respond_to?(:bark)
//JavaScript
if (typeof animal.bark === 'function') { animal.bark(); }
//Alternatively (short-curcuiting)
animal.bark && animal.bark();

However, while Ruby is strongly typed, JavaScript is weakly typed. This means that JavaScript does implicit conversion of types where Ruby would throw an exception. This is usually not a problem as long as you’re aware of it and know how the conversion works.

#Ruby
puts "1" + 2 # Raises TypeError
//JavaScript
print("1" + 2); // prints "12"

Ruby prides itself in that everything in it is an object. This is not the case in JavaScript; there are a few basic types that are not objects:

  • Numbers: 42, 3.14 – There is only one type of number, which encompasses both of what we know in Ruby as Fixnum and Float.
  • Booleans: true, false
  • Empty value: null – This is the equivalent of Ruby’s nil.
  • Undefined value: undefined – This has no Ruby equivalent. It is used for undefined function parameters and object properties. Ruby uses nil in similar situations.

That these values are not objects in the OOP sense of the word does not matter much, in my experience. In the Ruby world, we like to brag about being able to add methods to NilClass or Fixnum, but in the real world that doesn’t have much use.

In addition to these basic types, JavaScript has a few basic literals for object types, that are almost exactly the same as their equivalents in Ruby:

  • String: "Hello", 'world' – There is no difference between single and double quotes, and strings are immutable.
  • Array: [1, 2.3, "string", ['another', 'array']]
  • RegExp: /foo|bar/

There is one more to add to this list, but I will wait until after the next section to cover it.

Truthiness

In Ruby, only false and nil are false. JavaScript, like many other languages, has the concept of truthiness, where values are either true or false depending on what they contain. This is a form of implicit conversion, and the following values are falsy:

  • 0 – The number zero.
  • "" – The empty string.
  • NaN – A special value meaning Not a Number. If you do things like 0/0 you will get this value.
  • null – The empty value.
  • undefined – The undefined value.

Anything else is “truthy”. You can convert any truthy or falsy value into true or false by using a double not operator:

!!0 //false
!!1 //true

Variables

Variables in JavaScript, like Ruby, hold data. In the case of basic types, they hold the value of the type itself, but in the case of objects it holds a reference to the object itself. This is similar to Ruby’s notion of references and immediate values with the only difference that the latter group are not real objects in JavaScript.

Variables in JavaScript are declared with the var keyword:

var myString = "Hello, universe"

As JavaScript is not staticly typed there is no need to declare the type of the variable. The var keyword only tells JavaScript that the following is a variable. Always use var when declaring a new variable. It will seem to work well without it, but what’s really happening is that the variable will be created in the global scope, and we all know that global variables are evil. It is conventional in JavaScript to use camelCased identifiers when you need to use more than one word. The first part (word) of an identifier is in all lowercase except for one case which we’ll cover later. You can declare a variable without explicitly giving it a value. The value will then be undefined.

The object

JavaScript, like Ruby, is heavily object-oriented, and objects are used everywhere. Remember a few lines up where I said there was another literal to add to the list in addition to strings and arrays? Well, that missing literal is the object literal, and it’s one of those things that makes JavaScript what it is. It means that you can effortlessly create objects whenever you need them, pass them around and manipulate them at will. The syntax for creating an object is:

{ propertyOne: 'a string', propertyTwo: ['an', 'array'] }

Does it remind you of something? Yes, of course, it looks just like a Ruby Hash! And that’s basically just what it is. Objects in JavaScript are hash maps. An object can have anything attached to it with the use of a string key. If you already have an object, and would like to add or change a property on it, there are two ways of doing that. The first will look very familiar:

var myObject = {};
myObject['foo'] = 'bar';

The second will also look familiar, but does not work in the same way as in Ruby:

myObject.foo = 'baz';

In JavaScript, the bracket notation and the dot notation are exactly the same. They just add (or change) a property to an object. In Ruby, we disguise methods as properties, so

my_object.foo = 'bar';

is really

my_object.foo=('bar');

In JavaScript this works the other way; methods are properties of an object.

Functions

JavaScript is a functional language. This means that functions play a major role along with objects. In fact, functions are objects, but let’s not get into that just yet. More important is the fact that functions are first class citizens. You can assign them to variables and pass them around like any other object. There are a couple of ways to define a function:

function myFunction (foo, bar) {
  print(foo + bar);
};

This is the same as:

var myFunction = function (foo, bar) {
  print(foo + bar);
}

Because functions in JavaScript are first-class citizens, parentheses are not optional when invoking them. Using the name of a function without parentheses will return the function object itself instead of executing it and returning the result.

print(myFunction); //Will print a string representation of the function
print(myFunction('hello', 'world')); //Will print "helloworld"

Methods

Functions, like anything else, can be attached to an object. When this happens, they are usually referred to as methods. JavaScript has a this keyword, which corresponds to Ruby’s self. When inside a method, this refers to the object to which the method belongs. More accurately, it refers to the message receiver in OO terminology. This matters because a function can be attached to more than one object, so the context in which it is executed matters.

var horse1 = {name: 'Fearless Flames'};
var horse2 = {name: 'Silver Wind'};
var myFunction = function(){ return this.name; };

horse1.getName = myFunction; //No (), we're passing the function itself
horse2.getName = myFunction;

print(horse1.getName()); //Prints "Fearless Flames"
print(horse2.getName()); //Prints "Silver Wind"

Parameters and return values

JavaScript functions have implicit return values, but not in the same way that Ruby’s methods do. If you omit the return statement in a function, this is the same as ending the function with a simple return; which will return undefined. This is fine for functions that don’t need to return a value, but another thing in JavaScript that is optional is the semicolon to end an expression. You can omit this, like in Ruby, but it’s not recommended because it can cause problems. The JavaScript interpreter actually inserts semicolons where it sees fit, and this isn’t always where you would put them.

In Ruby methods we can have special parameters called splats and blocks. Splats collect 0 or more arguments into an array while block parameters contain the method’s associated block as a Proc variable. The reason for having the splat operator in Ruby is that a method with 3 parameters must be passed 3 arguments, or Ruby will throw an ArgumentError. JavaScript doesn’t enforce the number of arguments passed to a function, so you can pass as many or as few you want. Within the function definition, there exists a special array-like (it’s not really an array) variable which contains all the arguments passed in to it. Its name is simply arguments.

function joinStrings () {
  var joinedString = "";
  for (var i = 0; i < arguments.length; i++) {
    string = string + arguments[i];
  }
  return joinedString;
};

Anonymous functions and closures

JavaScript doesn’t have the concept of blocks that Ruby does. The reason is because it simply doesn’t need them, and the reason it doesn’t need them is because it has anonymous functions, also called lambdas. To create an anonymous function, just use the function operator without providing a name for the function:

function(){ print("Hello from anynymous function!"); }

Of course, the above function will never be executed, because we can’t reference it. One way we can use anonymous functions and reference them is to pass them as arguments to another function, in effect the same as blocks in Ruby:

var map = function(array, iteratorFunction){
  var result = [],
      i;
  for (i = 0; i < array.length; i++) {
    result.push(iteratorFunction(array[i]));
  }
  return result;
};

var array = [1,2,3,4,5];
var arrayPlus1 = map(array, function(n){ return n+1; });

Functions in JavaScript, like blocks in Ruby, provide closure. That is, the variables available in the scope where the function is defined are always available in the function, even if it’s called from another scope.

function makeCounter () {
  var count = 0;
  return function(){
    return count++;
  };
};

var counter1 = makeCounter();
var counter2 = makeCounter();
print(counter1()); // Prints 0
print(counter1()); // Prints 1
print(counter2()); // Prints 0

Functions as scope

In JavaScript, the only construct which provides scope is the function. This means that for and while loops, for example, do not have their own scope. This is the same in Ruby, but while Ruby has classes and modules to provide shelter from the global scope, JavaScript doesn’t have anything like that. But we can exploit the fact that functions have their own scope to achieve the same.

var myObject = (function(){
  //These variables are only available inside the anonymous function
  var foo = 'bar';
  var bar = 'baz'; //But don't forget the "var"!
  var foobar = foo + bar;
  
  //This object will be returned and assigned to myObject
  return {
    barfoo: foobar;
  };
})(); //We're calling the anonymous function right away

This example is contrived, but this pattern of defining an anonymous function and immediately calling it is very useful for creating scope whenever you want to work with some variables without polluting the existing scope.

Objects for namespacing

We can take this further and use objects to create a namespace for our stuff. We do this simply by creating one global object which will contain all of our objects as properties:

var MyModule = {};
MyModule.foo = 'bar';

We can combine this with the anonymous function to provide both namespacing and scope:

MyModule.myObject = (function(){
  var privateVar = 'foo';
  var privateFunction = function(){
    return privateVar.toUpperCase();
  };
  
  return {
    foo: function(){ //Closure
      return privateFunction();
    };
  };
})();

MyModule.myObject.foo(); //Returns "FOO"

This pattern is called the module pattern.

call and apply

Two interesting methods that every function in JavaScript has are call and apply. These methods allow you to call a function while setting what the this keyword should point to inside that function.

var printName = function(){
  print(this.name);
};

var horse = {name: 'Dapple Apple'};
printName.call(horse); // Prints "Dapple Apple"

The difference between call and apply is the way arguments are passed on to the function. With call, you give it the arguments like you would when calling it directly, but with apply you pass the arguments as an array (or the arguments array from the current function).

myFunction.call(object, var1, var2);
myFunction.apply(object, [var1, var2]);

The apply version is kind of similar to splatting an array in a similar situation in Ruby:

object.myMethod(*an_array)

Prototypal inheritance

Ruby is a class-based object-oriented language, where classes inherit other classes. In JavaScript, on the other hand, objects inherit directly from other objects. Every object in JavaScript contains a (secret) link to the object from which it inherits. The inherited object is called the prototype, hence the term prototype-based inheritance. When you try to access a property on an object and that object doesn’t have the property, the object’s prototype is searched, then the prototype’s prototype and so on until you reach Object.prototype, which is the endpoint for this search from all objects. That is, all objects inherit from the Object.prototype object, directly or indirectly. To create a new object based on an existing object, you use the object function:

var oldObject = {foo:'bar', bar:'baz'};
var newObject = object(oldObject);
newObject.bar = 'BAZ';
newObject.baz = 'QUUX';

print(newObject.foo); // Prints "bar", from oldObject
print(newObject.bar); // Prints "BAZ", newObject.bar "overrides" oldObject.bar
print(newObject.baz); // Prints "QUUX", from newObject

The object function doesn’t actually exist in JavaScript, but it’s easy to create.

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

To explain how this function works we have to take a look at..

Classes

Classes? Yes, even though JavaScript is prototype-based, it has the concept of classes. Kind of. There is a new keyword which is very familiar to those having used class-based languages. In Ruby this is not a keyword, but a class method, but the concept is the same: It instantiates a new object from that class. But what does new do in JavaScript?

Every function in JavaScript has a prototype property (functions are objects, remember?). When a function is called with the new keyword, this function becomes a constructor. What this means is that when the constructor is called (with new), a new object is created, whose (secret) prototype link points to that function’s prototype property. This new object is also the default return value of the constructor function. So, in effect, the object returned by a constructor inherits the constructor’s prototype property. While in the constructor function, the this keyword refers to this new object.

The prototype property should not be confused with the property which links to an object’s actual prototype, or “ancestor”. The prototype property, despite its name, is just another property and only has special meaning for function objects. The real prototype of an object is hidden in most implementations. Mozilla does reveal this link through the property __proto__, so if it helps make things clearer, you can think of it by that name to avoid confusion with prototype.

var Horse = function(name){
  this.name = name;
};

Horse.prototype.getName = function(){
  return this.name;
};

var myHorse = new Horse('Touch of Frost');
var myOtherHorse = new Horse('Westerly Winds');

print(myHorse.name); // Prints "Touch of Frost"
print(myOtherHorse.getName()); // Prints "Westerly Winds"

By convention, constructor functions start with an uppercase letter, unlike normal functions or methods. This is to easily distinguish between the two, because you don’t want to call normal functions with the new keyword and you don’t want to call constructors without it.

In the example above, two Horses are “instantiated”. They each get their own name, and the names are printed. The first horse’s name is accessed directly with the name property. This property is defined directly on the object in the constructor. The second horse’s name is accessed through a getter method. This method (getName) is defined on Horse.prototype, so when we try to access myOtherHorse.getName, JavaScript can’t find the getName property on myOtherHorse, so it searches its secret link, which points to Horse.prototype, and finds the method there. Because this in a method definition points to the message receiver (that is, myOtherHorse), we can access this.name and return it.

If we define a new method getName directly on either of our horses, this will take precedence over the one defined on Horse.prototype because of the way prototypal inheritance works. It is the same as defining a singleton method on an object in Ruby.

So, now we know enough to be able to explain what the object function we defined in the last section does. Since JavaScript doesn’t provide this function and we can’t edit the actual prototype link for an object, we have to jump through some hoops to create it ourselves. Let’s have a look at it again..

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

Firstly, we create a function F, which because of the function scope, is private to object. Then we set F.prototype to point to the object which was passed in, o. This is the object we want the new object to inherit from. Lastly, we return the result of calling F with the new keyword. This creates a new object and points its prototype link to o. Mission accomplished.

Extending the default toolset

In Ruby, classes are “open” and you can extend any class with new methods. This is also the case for JavaScript. To extend a class in JavaScript, we just add the property to the constructor’s prototype:

String.prototype.trim = function(){
  return this.replace(/^\s+|\s+$/g, '');
};

"  hello  ".trim() // "hello"

There are, however, some gotchas with doing this. The most important has to do with Object.prototype. Properties added to this object will be available to every object. This in itself is not really a problem. The problem lies with the way we iterate through an object’s properties:

var myObject = {foo: 'bar', bar: 'baz'};
for (var p in myObject) {
  print(p + ': ' + myObject[p]); // Prints "foo: bar\nbar: baz"
}

The for..in expression will iterate through an object’s properties, but it will also include all properties added to every object in its prototype chain, including Object.prototype. You can avoid this by checking if the property belongs to the object itself or to one of its ancestors:

var myObject = {foo: 'bar', bar: 'baz'};
for (var p in myObject) {
  if (myObject.hasOwnProperty(p)) {
    print(p + ': ' + myObject[p]); // Prints "foo: bar\nbar: baz"
  }
}

This is the recommended way of iterating an object’s properties, because when you write code for a shared environment like the web browser, you have no idea what other code may be used at the same time as yours. Someone may have extended Object.prototype, and it it could mess up your code. There also seems to be a wide conscensus that everyone should avoid extending Object.prototype.

For the same reason, some advocate that you shouldn’t extend Array.prototype, but not everyone agrees with this because the only way one would have properties added to this object show up when iterating an array is when treating the array as an object by using the for..in expression to iterate it:

var array = [1,2,3];
for (var i in array) {
  print(array[i]); //Prints "1\n2\n3"
}

The reason this works is that arrays in JavaScript are just specialised objects. They are hash maps that only use integers (represented as strings) as keys and have some special methods and other properties to work with the values in it, like the length property. The “correct” way of iterating an array is to use this property in a for or while loop:

var array = [1,2,3];
for (var i = 0; i < array.length; i++) {
  print(array[i]);
}

Further reading

This article covers some basics, but there is much more to learn about JavaScript. Some recommended resources are:

Comments

76 comments

Karma Police, arrest this man. He talks in maths, he buzzes like a fridge, he's like a detuned radio.