This blog post is for developers who already know how to write software and want to learn about Javascript. I am not teaching you how to write software because I assume you might code in a different language. Plus I hope Getify isn’t going to read this
In the early nineties, object-oriented programming made some noise with in the software industry. The idea wasn’t really new but it just had enough momentum to start and get the ball rolling. Books were being written, courses given, programming languages developed. All of a sudden, everybody became an expert of object-orientation, enthusiastically applying it to every problem, convinced they had found a better way to write programs. Really?
As the name implies, object-oriented is coupled to objects. So far, we have used objects as loose aggregations of values, adding and altering their properties whenever we saw fit. Lets start with the first pattern called prototype.
Javascript Prototype
What is prototype? You can call prototype a blueprint for object instances. Any time you create a function or a class you create a prototype blueprint. The class is actually declared as a function in Javascript.
var Person = function(attributes) { // here you create a prototype - "blueprint" // the class name has a capital letter this.something = attributes.something; this.method = function(){ return a value; } } // declare an instance of type Person and that happens every single time you create a new instance. var michel = new Person("Michel","Herszak");
So when you instantiate a Person class you create a prototype. The prototype
is an Object
(the JSON kind) that contains the methods that each instance will share. Every instance can share these same methods because when called, they will be called in the context of that instance. To actually make share happen we have to refactor our code a little bit:
// Class constructor var Person = function(attributes) { this.something = attributes; } // This prototype function will be shared with all instances that // are of the same type, e.g. Person. Person.prototype.sayHello = function() { console.log("Hi, my name is " + this.something); }; // So all following instances actually SHARE a function called // sayHello. var bob = new Person({ firstName: "Bob", lastName: "Ject", age: 33, }); var ella = new Person({ firstName: "Ella", lastName: "Siff", age: 1, });
So what is this new keyword doing? It looks at all related prototypes and assigns them to the instance.
Where do I put my static properties?
Sometimes, methods do not belong to a specific instance and instead belong to the class itself. Back in the day, we called these static or class methods.
Person.bob = function(healthy, sick) { sick.person != healthy; }
Remember, classes are simply functions and they are first-class. Those can obtain properties through dot notation. Meaning, we simply define a property on the “Class” function itself.
Let’s add a tool to create “inheritance” between classes. This tool will just have to do one single thing: clone the prototype from the original prototype:
/** * This is exactly where the magic happens! * @param child * @param parent */ var inheritsFrom = function (child, parent) { // we transfer all members and functions to the new class child.prototype = Object.create(parent.prototype); };
How do we use it using OOP principals?
var Employee = function(attributes,employeeId) { this.something = attributes.something; this.employeeId = employeeId; } inheritsFrom(Employee, Person);
There is one problem, when ever we instantiate a new variable we overwrite another special property called constructor. A way to solve it is depicted in the following snippet:
var inheritsFrom = function (child, parent) { // before child.prototype = Object.create(parent.prototype); // after with reseting the constructor. child.prototype.prototype = child; };
You’ll see this a lot in code. So now lets create a new class using the Employee type.
/** * Lets extend the * @param attributes * @param employeeId * @constructor */ var Employee = function(attributes,employeeId) { /** * Now we have the ability to call our Person class * with the this pointer and and extend Employee class. */ Person.call(this,arguments); this.employeeId = employeeId; }
When we call this function we not only get the properties from Employee, but also from Person. Great! So how do we call a super class in case we want to overwrite an object function?
Employee.prototype.sayHello = function () { /** * This is a very object oriented pattern. */ var personArgument = Person.prototype.sayHello().call(this); /** * You call the super class Person and extend the function by concatenation. */ return personArgument + this.employeeId; }
This is fantastic and mind blowing, too. But what if we want to keep track of contained variables? We choose anonymous-auto-executed function and the module pattern in order to leverage its api abilities:
var Person = (function () { var num = 3; // private variable var Person = function () { }; Person.findOne = function () { } return Person; })();
We create the class within the module and expose only public methods as an api reference. I think this is probably not it but thats is for now.
What have we learned:
- DO NOT define instance methods as
this
properties in a “Class”constructor
. This will create a new function in memory for each instance, which is expensive and unnecessary – make use of prototype!
Email — michel.herszak@gmail.com
Twitter — @MHerszak (twitter.com/MHerszak)
Want to know about the way in which I work? Great, you can get started with an overview of just how I approach a project right here.