- There is no direct way to test the object to determine its origin
- Every object produced by the factory has its own copy its methods and its use of memory is not optimal
For these reasons I only use object literals for occasions when there will only be a small number of instances of the object in question, ideally only one. This is usually the case, but a given system probably has a few objects of which many instances are created. For these objects a different approach is needed. Instead of creating these objects with literals I want to create them from named models that I can reuse and extend as needed. These models are called prototypes.
Class based inheritance versus prototypal inheritance
C# has classes. The structure of a given object instance is defined by its class. C# supports polymorphism via classes; one class inherits from another class and add to, or override, its parent’s definition. In C# all classes share a common ancestor, the Object class.
When you create an object literal you are inheriting from Object.
Every object but one has a prototype
Confusing naming causes confusion
The best detailed explanation I have seen for all this is this article: Constructors considered mildly confusing. I am going to attempt to explain it without spending much time talking about what is going on under the covers and try to make things as simple as possible.
The key thing to remember is that the value of a function’s prototype property is not the function’s prototype – it is the object assigned as the prototype of objects created by the function when the function is a constructor. A function’s prototype property contains an object instance which is applied as the model to all object instances created by the function when the function is used as a constructor function via the new keyword.
This example illustrates that the redShirt object and the blueShirt object exist independently of each other and each maintains its own private state.
The problem with this example is that the constructor initializes the object’s members each time it is invoked. Every instance gets its own identical copy of the getColorSize function just as each would if I used an object literal returned by a factory method. This is where the prototype property’s object instance is beneficial.
An example of a constructor that takes advantage of a constructor’s prototype looks like this (http://jsfiddle.net/DougWare/QYpM6/).
The prototype property is assignable and can be any object
In the examples above, Animal.prototype is extended one member at a time. However, because prototype contains an object instance, any object can be a prototype. Therefore, the example can be rewritten as follows (http://jsfiddle.net/DougWare/4DYGW/).
Extending the prototype, Extension methods
By now it should be clear that all instances created from the same constructor point to the same prototype instance. This is the reason that using prototype offers superior performance and memory usage when you have many instances of the same type of object. One implication of this is that changing or extending the prototype instance affects every object on the prototype chain (http://jsfiddle.net/DougWare/VE52D/).
Multiple levels of inheritance
It is possible to have multiple levels of inheritance by applying the parent object’s prototype to the constructor function of the child object (http://jsfiddle.net/DougWare/nKYtW/).
I feel compelled to editorialize on this point. You should be suspicious of this sort of thing when you see it in a design. There are times when inheritance of this type is valuable, but in my experience this is uncommon. You should carefully weigh the complexity of this technique against the potential benefit of a simpler design.
Using instanceof and is isPrototypeOf to determine an object’s origin
There are times when you may need to test an object instance to determine its origin. Every object has a constructor property which is an object containing a prototype property. You can’t trust these values and you should not use them! Instead you can use the instanceof comparison operator to identify an object’s constructor. One of the benefits of instanceof is that you can test for any level of the prototype chain (http://jsfiddle.net/DougWare/RDQU2/).
The instanceof operator requires you to have a working reference to the constructor function for the right-hand side of the comparison. You may not have such a reference. As an alternative you can check to see if a given object is the prototype of another object with the isPrototypeOf method (http://jsfiddle.net/DougWare/fzSPm/).
In this example the constructor function is f(), but at the location of the test, f() is inaccessible and out of scope.
If you made it all the way through this post you probably get that using prototypes is much more complex than using object literals as described in the previous post. Limiting your use of this technique is a good idea if your code is to be used or maintained by developers who are likely to be confused by prototypes. If you only have one or two instances of a given object you definitely should not use this technique. If you will have many instances the performance and memory savings prototypal inheritance brings justifies the additional complexity.
This is the second to last post I currently have planned for this series. The final post will be about the very confusing this keyword. If you’ve made it this far, you should have no trouble understanding it!
Author: Doug Ware