Yehuda Katz is a member of the Ember.js, Ruby on Rails and jQuery Core Teams; he spends his daytime hours at the startup he founded, Tilde Inc.. Yehuda is co-author of best-selling jQuery in Action and Rails 3 in Action. He spends most of his time hacking on open source—his main projects, along with others, like Thor, Handlebars and Janus—or traveling the world doing evangelism work. He can be found on Twitter as @wycats.

Understanding “Prototypes” in JavaScript

For the purposes of this post, I will be talking about JavaScript objects using syntax defined in ECMAScript 5.1. The basic semantics existed in Edition 3, but they were not well exposed.

A Whole New Object

In JavaScript, objects are pairs of keys and values (in Ruby, this structure is called a Hash; in Python, it’s called a dictionary). For example, if I wanted to describe my name, I could have an object with two keys: `firstName` would point to “Yehuda” and `lastName` would point to “Katz”. Keys in a JavaScript object are Strings.

To create the simplest new object in JavaScript, you can use Object.create:

var person = Object.create(null); // this creates an empty objects

Why didn’t we just use var person = {};? Stick with me! To look up a value in the object by key, use bracket notation. If there is no value for the key in question, JavaScript will return `undefined`.

person['name'] // undefined

If the String is a valid identifier[1], you can use the dot form:

person.name // undefined

[1] in general, an identifier starts with a unicode letter, $, _, followed by any of the starting characters or numbers. A valid identifier must also not be a reserved word. There are other allowed characters, such as unicode combining marks, unicode connecting punctuation, and unicode escape sequences. Check out the spec for the full details

Adding values

So now you have an empty object. Not that useful, eh? Before we can add some properties, we need to understand what a property (what the spec calls a “named data property”) looks like in JavaScript.

Obviously, a property has a name and a value. In addition, a property can be enumerable, configurable and writable. If a value is enumerable, it will show up when enumerating over an object using a for(prop in obj) loop. If a property is writable, you can replace it. If a property is configurable, you can delete it or change its other attributes.

In general, when we create a new property, we will want it to be enumerable, configurable, and writable. In fact, prior to ECMAScript 5, that was the only kind of property a user could create directly.

We can add a property to an object using Object.defineProperty. Let’s add a first name and last name to our empty object:

var person = Object.create(null);
Object.defineProperty(person, 'firstName', {
  value: "Yehuda",
  writable: true,
  enumerable: true,
  configurable: true
});
 
Object.defineProperty(person, 'lastName', {
  value: "Katz",
  writable: true,
  enumerable: true,
  configurable: true
});

Obviously, this is extremely verbose. We can make it a bit less verbose by eliminating the common defaults:

var config = {
  writable: true,
  enumerable: true,
  configurable: true
};
 
var defineProperty = function(obj, name, value) {
  config.value = value;
  Object.defineProperty(obj, name, config);
}
 
var person = Object.create(null);
defineProperty(person, 'firstName', "Yehuda");
defineProperty(person, 'lastName',   "Katz");

Still, this is pretty ugly to create a simple property list. Before we can get to a prettier solution, we will need to add another weapon to our JavaScript object arsenal.

Prototypes

So far, we’ve talked about objects as simple pairs of keys and values. In fact, JavaScript objects also have one additional attribute: a pointer to another object. We call this pointer the object’s prototype. If you try to look up a key on an object and it is not found, JavaScript will look for it in the prototype. It will follow the “prototype chain” until it sees a null value. In that case, it returns undefined.

You’ll recall that we created a new object by invoking Object.create(null). The parameter tells JavaScript what it should set as the Object’s prototype. You can look up an object’s prototype by using Object.getPrototypeOf:

var man = Object.create(null);
defineProperty(man, 'sex', "male");
 
var yehuda = Object.create(man);
defineProperty(yehuda, 'firstName', "Yehuda");
defineProperty(yehuda, 'lastName', "Katz");
 
yehuda.sex       // "male"
yehuda.firstName // "Yehuda"
yehuda.lastName  // "Katz"
 
Object.getPrototypeOf(yehuda) // returns the man object

We can also add functions that we share across many objects this way:

var person = Object.create(null);
defineProperty(person, 'fullName', function() {
  return this.firstName + ' ' + this.lastName;
});
 
// this time, let's make man's prototype person, so all
// men share the fullName function
var man = Object.create(person);
defineProperty(man, 'sex', "male");
 
var yehuda = Object.create(man);
defineProperty(yehuda, 'firstName', "Yehuda");
defineProperty(yehuda, 'lastName', "Katz");
 
yehuda.sex        // "male"
yehuda.fullName() // "Yehuda Katz"

Setting Properties

Since creating a new writable, configurable, enumerable property is pretty common, JavaScript makes it easy to do so using assignment syntax. Let’s update the previous example using assignment instead of defineProperty:

var person = Object.create(null);
 
// instead of using defineProperty and specifying writable,
// configurable, and enumerable, we can just assign the
// value directly and JavaScript will take care of the rest
person['fullName'] = function() {
  return this.firstName + ' ' + this.lastName;
};
 
// this time, let's make man's prototype person, so all
// men share the fullName function
var man = Object.create(person);
man['sex'] = "male";
 
var yehuda = Object.create(man);
yehuda['firstName'] = "Yehuda";
yehuda['lastName'] = "Katz";
 
yehuda.sex        // "male"
yehuda.fullName() // "Yehuda Katz"

Just like when looking up properties, if the property you are defining is an identifier, you can use dot syntax instead of bracket syntax. For instance, you could say man.sex = "male" in the example above.

Object Literals

Still, having to set a number of properties every time can get annoying. JavaScript provides a literal syntax for creating an object and assigning properties to it at one time.

var person = { firstName: "Paul", lastName: "Irish" }

This syntax is approximately sugar for:

var person = Object.create(Object.prototype);
person.firstName = "Paul";
person.lastName  = "Irish";

The most important thing about the expanded form is that object literals always set the newly created object’s prototype to an object located at Object.prototype. Internally, the object literal looks like this:

prototype-chain.png

The default Object.prototype dictionary comes with a number of the methods we have come to expect objects to contain, and through the magic of the prototype chain, all new objects created as object literal will contain these properties. Of course, objects can happily override them by defining the properties directly. Most commonly, developers will override the toString method:

var alex = { firstName: "Alex", lastName: "Russell" };
 
alex.toString() // "[object Object]"
 
var brendan = {
  firstName: "Brendan",
  lastName: "Eich",
  toString: function() { return "Brendan Eich"; }
};
 
brendan.toString() // "Brendan Eich"

This is especially useful because a number of internal coercion operations use a supplied toString method.

Unfortunately, this literal syntax only works if we are willing to make the new object’s prototype Object.prototype. This eliminates the benefits we saw earlier of sharing properties using the prototype. In many cases, the convenience of the simple object literal outweighs this loss. In other cases, you will want a simple way to create a new object with a particular prototype. I’ll explain it right afterward:

var fromPrototype = function(prototype, object) {
  var newObject = Object.create(prototype);
 
  for (var prop in object) {
    if (object.hasOwnProperty(prop)) {
      newObject[prop] = object[prop];      
    }
  }
 
  return newObject;
};
 
var person = {
  toString: function() {
    return this.firstName + ' ' + this.lastName;
  }
};
 
var man = fromPrototype(person, {
  sex: "male"
});
 
var jeremy = fromPrototype(man, {
  firstName: "Jeremy",
  lastName:  "Ashkenas"
});
 
jeremy.sex        // "male"
jeremy.toString() // "Jeremy Ashkenas"

Let’s deconstruct the fromPrototype method. The goal of this method is to create a new object with a set of properties, but with a particular prototype. First, we will use Object.create() to create a new empty object, and assign the prototype we specify. Next, we will enumerate all of the properties in the object that we supplied, and copy them over to the new object.

Remember that when you create an object literal, like the ones we are passing in to fromPrototype, it will always have Object.prototype as its prototype. By default, the properties that JavaScript includes on Object.prototype are not enumerable, so we don’t have to worry about valueOf showing up in our loop. However, since Object.prototype is an object like any other object, anyone can define a new property on it, which may (and probably would) be marked enumerable.

As a result, while we are looping through the properties on the object we passed in, we want to restrict our copying to properties that were defined on the object itself, and not found on the prototype. JavaScript includes a method called hasOwnProperty on Object.prototype to check whether a property was defined on the object itself. Since object literals will always have Object.prototype as their prototype, you can use it in this manner.

The object we created in the example above looks like this:

prototype-chain-2.png

Native Object Orientation

At this point, it should be obvious that prototypes can be used to inherit functionality, much like traditional object oriented languages. To facilitate using it in this manner, JavaScript provides a new operator.

In order to facilitate object oriented programming, JavaScript allows you to use a Function object as a combination of a prototype to use for the new object and a constructor function to invoke:

var Person = function(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
 
Person.prototype = {
  toString: function() { return this.firstName + ' ' + this.lastName; }
}

Here, we have a single Function object that is both a constructor function and an object to use as the prototype of new objects. Let’s implement a function that will create new instances from this Person object:

function newObject(func) {
  // get an Array of all the arguments except the first one
  var args = Array.prototype.slice.call(arguments, 1);
 
  // create a new object with its prototype assigned to func.prototype
  var object = Object.create(func.prototype);
 
  // invoke the constructor, passing the new object as 'this'
  // and the rest of the arguments as the arguments
  func.apply(object, args);
 
  // return the new object
  return object;
}
 
var brendan = newObject(Person, "Brendan", "Eich");
brendan.toString() // "Brendan Eich"

The new operator in JavaScript essentially does this work, providing a syntax familiar to those comfortable with traditional object oriented languages:

var mark = new Person("Mark", "Miller");
mark.toString() // "Mark Miller"

In essence, a JavaScript “class” is just a Function object that serves as a constructor plus an attached prototype object. I mentioned before that earlier versions of JavaScript did not have Object.create. Since it is so useful, people often created something like it using the new operator:

var createObject = function (o) {
  // we only want the prototype part of the `new`
  // behavior, so make an empty constructor
  function F() {}
 
  // set the function's `prototype` property to the
  // object that we want the new object's prototype
  // to be.
  F.prototype = o;
 
  // use the `new` operator. We will get a new
  // object whose prototype is o, and we will
  // invoke the empty function, which does nothing.
  return new F();
};

I really love that ECMAScript 5 and newer versions have begun to expose internal aspects of the implementation, like allowing you to directly define non-enumerable properties or define objects directly using the prototype chain.

37 Responses to “Understanding “Prototypes” in JavaScript”

So if I know I will never use prototypal properties of an object, I should use Object.create(), and have none to begin with? Are there significant memory savings that way?

The prototype concept in Javascript is the hardest to understand for beginners (or even intermediate developers). While the newObject and createObject functions are useful, I think it only obscures the concept. I recommend checking out http://phrogz.net/js/classes/OOPinJS.html for a more technical in-depth about using prototypes.

Another problem that Javascript is having is people wanting to force OOP on the language, when fundamentally it’s a prototype-based language. If developers would embrace that fact we wouldn’t have as many ‘idioms’ in Javascript that only confuse people who are being introduced to the language.

Hi Yehuda

FireFox 3.6.19 didn’t parse you first code snip but Safari 5.0.6 did (on OS X Leopard). Safari didn’t parse the JS on second code snip:

var config = {
writable: true,
enumerable: true,
configurable: true
};

var defineProperty = function(obj, name, value) {
config.value = value;
Object.defineProperty(obj, name, config);
}

var person = Object.create(null);
defineProperty(person, ‘firstName’, “Yehuda”);
defineProperty(person, ‘lastName’ “Katz”);

I get the error on the last line of code:

defineProperty(person, ‘lastName’ “Katz”);
prototypes.html:24SyntaxError: Parse error

Any idea why?

Thanks
Alastair

:) Way to bury the lead on the real 5.1 dream: Proxy Objects

http://jsconf.eu/2010/speaker/be_proxy_objects.html

(ps I’d love your take on these)

I like the idea of Prototypes. We kinda set defaults for keys/properties of an object. It is similar to method invocation chain in ruby. like..

class A
def return_a
return 1
end
end

class B < A
def return_others
end
end

B.new.return_a

:) nice post.

@Alastair. I guest that the problem is in:

defineProperty(person, ‘lastName’ “Katz”);

You miss a comma.

@Matt King:

> Another problem that Javascript is having is people wanting to force *OOP* on the language, when fundamentally it’s a prototype-based language.

I hope you meant to say “class-based object-orientation” in that sentence, because prototype-based languages are as object-oriented as class-based ones. To claim otherwise would show a gross misunderstanding of what constitutes object-orientation (hint: it has less to do with how the blueprint looks and all to do with how the end product behaves).

Thanks for the post.

I think you meant “If à property is enumerable [...]” when you wrote

>> If a value is enumerable, it will show up when enumerating [...]

@Alastair: Missing a comma (original article is too) in the erroring line.

// defineProperty(person, ‘lastName’ “Katz”);
// … should be
defineProperty(person, ‘lastName’, “Katz”);

Alastair – here is why. http://kangax.github.com/es5-compat-table/

Unfortunately not quite widely supported enough to use in a production web app. Of course, I learned this because I refactored some code to use prototypes only to discover browser testing didn’t go well.

Here is an alternative method that works in older browsers that can provide similar results http://timkadlec.com/2008/01/using-prototypes-in-javascript/

Here on:

function newObject(func) {
// get an Array of all the arguments except the first one
var args = Array.prototype.slice.call(arguments, 1); // <—–

where is arguments var coming from? Did u mean function newObject(func,arguments) or is this var automatically filled?

Great explanation btw, clarified a lot of things to me. I really like your writing style!

Great summary of the prototype concept in JS, built from an interesting reverse perspective. I enjoyed reading it.

Just a small code correction: Shouldn’t the second line of the third code example in the “Object Literals” section read like this?

alex.toString() // “[object Object]”

It would make the example more clear.

Thanks Yehuda, at last I got how JS prototypes works.

Missing return statement on the fromPrototype function expression. Newly constructed Object is never returned.

Awesome post!

Looks like minor note/typo: In your example under “Object Literals”, your `fromPrototype` function should have a `return newObject` at the bottom (under the for loop).

Cheers :)

@Alastair: you miss “,” at ur last line. it should be:

defineProperty(person, ‘lastName’, “Katz”);

This article makes me understands what I have been coding :)
Thank you.

@Alastair

I ran the snippet you provided in Firefox 3.6.18 and ran into the same problem. I’m assuming Firefox 3.6 does not support ECMAScript 5.

The snippet works in Firefox 5.

Great article! I think you forgot to add a return statement to the fromPrototype function.

Yehuda, you have missed a return here:

var fromPrototype = function(prototype, object) {
var newObject = Object.create(prototype);

for (var prop in object) {
if (object.hasOwnProperty(prop)) {
newObject[prop] = object[prop];
}
RETURN NEWOBJECT
}
};

Thanks for a great article! You wrap the problem very friendly and with a wit! :)

Shouldn’t the fromPrototype function return the created newObject after the loop?

there’s a typo, return is missing:

var fromPrototype = function(prototype, object) {
var newObject = Object.create(prototype);

for (var prop in object) {
if (object.hasOwnProperty(prop)) {
newObject[prop] = object[prop];
}
}

return newObject;

};

Hello,

Is the fromPrototype() function missing a “return newObject;” line at the end?

Interesting article!

Mark

Oh, and Alastair, looks like there’s a comma missing from:

defineProperty(person, ‘lastName’ “Katz”);

Should be:

defineProperty(person, ‘lastName’, “Katz”);

Hi Yehuda
May be the line
return newObject
is missing in the end of fromPrototype function.
Thank
Myroslav

Hi Yehuda, I didn’t understand one thing that why did you create the fromPrototype function when we can use Object.create’s second argument for this purpose, will you mind explaining that please? :)

Do you think you could post about the Sproutcore approach to OOP?

Excellent article. Very useful for people wanting to move from just using JavaScript to jazz up a page to actually coding an application.

You really break it down in a simple and understandable way. Much clearer than a lot of other renditions I’ve seen.

This is a really good explanation (perhaps the best I’ve seen so far)!

Thanks!

Another edit you could make if you have the time and inclination, in the second boxout of the Prototypes section:

// instead of using defineProperty and specifying writable,
// configurable, and enumerable, we can just assign the
// value directly and JavaScript will take care of the rest
person['fullName'] = function() {
return this.firstName + ‘ ‘ + this.lastName;
});

The final ‘)’ isn’t needed! Great article though ;-)

in “Setting Properties”, at the end of the first function, you’re closing a parenthese that hasn’t been opened,left over defineProperty on top. great article btw! Thanks.

I agree with Matt King there. “While the newObject and createObject functions are useful, I think it only obscures the concept.”

@Lucas Prim:

The `arguments` variable/reference is always available within a function. It is array-like (not actually an array), but you can cast it to an array using a generic function such as “slice”.

This line:
var args = Array.prototype.slice.call(arguments, 1);

Since `slice` works on arrays or array-like objects (object with a length property), this above works. The following would have worked as well (minor performance differences aside):

var args = [].slice.call(arguments, 1);

Helpful explanation – thanks for taking the time to write it. Noticed a minor typo:

var alex = { firstName: “Alex”, lastName: “Russell” };
person.toString() // “[object Object]”

should presumably be

var alex = { firstName: “Alex”, lastName: “Russell” };
alex.toString() // “[object Object]”

Thanks
/Eoin/

Fixed!

I think there is a mistake on the page, shown at the end of this message: 2 ‘{‘ followed by 3 ‘}’. Shouldn’t it be ‘{{}}’?

Happy New Year!
Ray

We can wrap this chunk of HTML in a SproutCore view, which will define the sources of each of the properties used in {{}}}.

Leave a Reply

Archives

Categories

  • Bundler (2)
  • Conferences (9)
  • DataMapper (1)
  • Engine Yard (2)
  • Food (1)
  • JavaScript (9)
  • jQuery (15)
  • Merb (26)
  • Other (80)
  • Personal (4)
  • Publications (1)
  • Rails 3 (18)
  • Rubinius (3)
  • Ruby (52)
  • Ruby 2.0 (1)
  • Ruby on Rails (39)
  • SproutCore (1)
  • TextMate (7)
  • Tokaido (1)
  • Meta