OOP (Object Oriented Programming)
I don't know if you, too, have already practiced JavaScript without ever learning the basics… In the past, I occasionally did JavaScript but I never learned this language properly. I also made a mix between JavaScript and jQuery.
Recently, I decided to immerse myself in this controversial universe. JavaScript is sometimes adored, sometimes hated… I think that its detractors may have remained on the JavaScript of the beginnings of the Web which was used to display intrusive popups… (I know I'm kidding a little ;-p).
Today, JavaScript has undeniably become essential! For what? For the following few reasons… We use it for the Web on the client side, but also on the server side (with Node.js and Rhino). It is used more and more in other languages. Google is developing increasingly sophisticated APIs. Mobile apps can be developed in HTML5/CSS3 and JavaScript rather than heavy technologies for the purpose of portability (iOS, Android, etc.).
I encountered quite a few difficulties in finding clear explanations of this language and how to use it. I often found “cooking recipes” but not often explained and schematized resources. That's why I wanted to write this modest post. Hope it can help you in your learning...
JavaScript? What is this?
Everyone (or almost) can say: “yes! obviously! JavaScript is a prototype-oriented language, didn't you know!!? phew…”. Certainly, but concretely, what does a prototype mean?
First of all, you should know that it's totally out of step with what most developers use on a daily basis (I'm thinking of "classic" object languages: PHP, Java, C++, C#, Python, etc.) . In these object languages, we have the notion of class which is an abstraction of a family of objects. A class is described by code.
Conversely, in JavaScript, the notion of class does not exist (although we will talk about classes later, but this will be a shortcut, or rather even an abuse of language). This notion of class is replaced by the notions of constructor and prototype. In JavaScript, absolutely everything is an object!
The constructor is an object, so it lives in memory, and it allows us to create new objects. All objects resulting from the same constructor will be of the same family (a bit as if they were instances of the same class). The constructor defines instance variables to make the analogy to Java/C++ for example.
A prototype is also an object (therefore also loaded in memory). A prototype encapsulates everything that is shared between objects of the same family (instance methods to make the analogy to Java/C++).
The power of prototype-oriented languages is their dynamism ! We can modify everything at run-time because everything is an object. As I told you just before, the notion of class is replaced by objects that live in memory (prototype and constructor). You can therefore, at run-time, modify these elements. It's exactly as if in Java, we were going to modify the physical file that defines and implements a class!! Imagine the perspectives when you have a minimum mastery of the subject… You can do everything: modify the attributes and methods of an object, the inheritance relationships, you can do some introspection, etc.
Another major advantage of JavaScript: it is a multi-paradigm language. It can be used with a procedural approach (like C for example) or with an object approach . JavaScript also allows for functional programming (anonymous functions, closures, iterations with map, reduce, every, some, etc. methods).
Know that functional programming is an extremely powerful concept. More and more languages add elements of this family to their possibilities:
C++ with Boost (Lambda functions for example)
C++11 (Lambda functions, closures, etc)
Java7 (closures, etc.)
PHP5.3 (closures, anonymous functions)
Last advantage: its asynchronism . It is also radically different from other languages. We are even starting to seriously use server-side JavaScript code with Node.js for example. This is an extremely promising and interesting project! The advantage of asynchronism is the non-blocking side of the operations. No need to wait for the result of an ongoing operation before starting another! Callback functions are then necessarily used.
In short, you understand that if your colleagues tell you that JavaScript is rotten, they won't get anything out of JS!!. And you especially understand why Google is developing this technology so much (for example with its incredibly fast V8 engine, the basis of the Chrome browser).
To end the blabla, a negative point: JavaScript is a complex language … Difficult to understand for beginners, not easy to use without immersing yourself in it… In addition, there are often several methods to achieve your ends.
Last drawback: its resemblance to other languages (C, Java). You feel like you know how to code in JavaScript when you know how to do Java for example, but there are so many fundamental differences… Ruby at least is out of step (due to its syntax), which may seem more difficult to access , but at least it does not deceive!
Finally, the most important, accepting to question your knowledge in object-oriented programming... Vanity in the closet...
Object approach in JavaScript
First example of structuring on the "model of a class"
Not to mention reproducing all the facets of class-based object languages, it is still important to structure the code. Otherwise, we make an infamous and incomprehensible JavaScript…
Anyway, we cannot create a class in JavaScript because this concept does not exist! On the other hand, we are going to see how to create a constructor (which allows you to create new instances) and how to pool methods between the different instances (in an object called “prototype”).
Before going into details, here is a first example of structuring a code (commented with YUI DOC syntax ):
/**
* Modeling a robot
*
* @class Robot
*/
var Robot = ( function ( ) {
'use strict' ;
// --- Class attributes
/**
* Possible directions to make a robot walk
* @ property DIRECTIONS
* @type Array
* @static
*/
Robot.DIRECTIONS = [ 'NORTH' , 'EAST' , 'SOUTH' , 'WEST' ] ; // --- Class methods / ** * Factory method of a robot
* @method build
* @static
*/
Robot. build = function ( name ) {
return new Robot ( name ) ;
} ;
// --- Constructor + instance attributes (set in constructor)
/**
* Constructor
* @constructor
* @param {String} name Robot name
*/
function Robot ( name ) {
// --- Attributes d 'instance
/**
* Robot name
* @property name
* @type String
*/
this . name = name ;
// --- Processing done in the constructor
// We put the name in capitals
this . name = this . name . toUpperCase ( ) ;
}
// --- Instance Methods
/**
* Instance Methods
* @property prototype
* @type Object
*/
Robot. prototype = {
/**
* A robot can speak: it can say its name.
* @method speak
*/
speak : function ( ) {
console. log ( ' My name is' + this.name + '.' ) ; } , /** * A robot can walk one unit in a direction * @method walk */ walk : function ( direction ) { // Check direction requested if ( typeof direction !== 'string' & ;&
& ; Robot. DIRECTIONS . indexOf ( direction.toUpperCase ( ) ) === - 1 ) { throw 'Direction argument invalid! ' ; } console. log ( 'Walk one step in' + direction + 'direction.' ) ; } } ; // We think of returning the constructor (in order to be able to build instances, otherwise all // the code of our class would be useless because it is not visible from the outside) return Robot
;
} ( ) ) ;
(code-box)
As you can see, it looks very much like a class but in reality, we are just describing objects (which will exist in memory) and which will be useful for creating instances of Robots. In the following, I will use the word "class" but remember that this is an abuse of language!
Using this class:
let mike = new Robot ( 'Mike' ) ;
Mike. speak ( ) ;
Mike. walk ( 'east' ) ;
Mike. walk ( 'north' ) ;
var titus = Robot. build ( 'Titus' ) ;
titus. speak ( ) ;
titus. walk ( 'north' ) ;
titus. walk ( 'north' ) ;
titus. walk ( 'east' ) ;
(code-box)
The example is really reduced to its bare minimum but there is already tons of things to say.
Constructor concept
A constructor allows, as generally in other languages, to build a new instance. In Java we would construct an instance from the class. In JavaScript, it will be from a constructor, and a prototype (see the following paragraph).
A constructor is simply a function that will be invoked by the following code for example:
// Defining an empty constructor
function A ( ) { }
// Using the constructor to create an instance
var a = new A ( ) ;
(code-box)
If we look at our instance in the JavaScript console, we see that we have an object from constructor A, and that this object contains a prototype (see next paragraph):
// New objectvar b = { } ; // Equivalent to: var b = new Object();(code-box)
Let's visualize the differences between our 2 instances a and b :
We can see that a and b were not built with the same constructor. We notice that in the prototype of a , we find another prototype: the same as that of b . We just saw our first heirloom! (A paragraph will be dedicated to inheritance a little later).
As a result, as in Java, the a object has inherited a number of generic methods: isPrototypeOf, hasOwnProperty, etc.
Concept of prototype
A prototype is an object containing the equivalent of the "classic" instance methods of a class in C++, Java and many others...
Instantiating an object creates a new reference to the prototype. Consequently, all Robot objects share the methods contained in the prototype.
Important note : the code is shared in the prototype for all instances, but this code can act on instance attributes.
To illustrate this rather complex point to explain, the "speak" method will be available for all Robots, but its result will be specific to an instance: Titus will assert "My name is Titus" while Mike will assert "My name is Mike".
A little illustration...
The first line of the console shows the mike and titus objects after running our first example code. We add a new method to the prototype, and this, in a dynamic way! (Try to do this in Java, it's possible but a little less natural…). After this addition, we look at our objects again: they have access to the new method!
Not bad is not it?
Illustration of the notions of constructor, prototype and created objects
This UML object diagram shows the relationships between different JavaScript objects. Remember that everything is an object: constructor, prototype, our Robot instances, etc.
All instances created by the constructor have a reference to the prototype. This makes it possible to pool methods that are similar for all Robot objects. We could also have defined methods in the constructor, but in this case, they would have been duplicated for each Robot and not shared (which would have been a shame).
The prototype is referenced in the constructor, and the constructor is referenced in the prototype.
The screenshots of the JavaScript console (on the diagram above) perfectly illustrate all this.
It is essential to fully understand these 3 notions before going any further. All the following uses these notions in order to reproduce the notions of usual “object” oriented programming .
Note for UML purists : I used a so-called “class” UML schema because my editor did not allow me to make an “object” schema. Yet UML2 understands the object schema well. So we can absolutely use UML for JavaScript design! ;-).
Notion of namespace
A namespace makes it possible to encapsulate code in a “name” in order to avoid conflicts with other codes. For example, the "build" method has a very common name, so there is a non-negligible probability that another script uses the same name...
Using a namespace avoids this kind of problem. Indeed, we access the “buid” method by: “Robot.build” which does not collide with for example: “OtherNamespace.build”.
In JavaScript, a namespace can be defined using an Object or the scope of a function. (Yes I already mentioned to you that JavaScript is complex because there are hundreds of ways to do the same thing…). In the example of our Robot class, I chose the second method (that of the scope of a function) because this method makes it possible to emulate private attributes and methods (we will see this later). It is therefore a less restrictive method.
Summary: To create a namespace, one can make a self-evaluating anonymous function as follows.
var Robot = ( function ( ) {
// everything in here is wrapped in the "Robot" namespace
} ( ) ) ;
(code-box)
In our example, the constructor has the same name as the namespace in order to make it natural to use. For example, we will access the class variables by Robot.DIRECTIONS and we will build a new instance by new Robot('Titus') . It would have been a shame not to have the same name (Robot) in both cases…
Equivalent to "class methods"
In class-based object languages, we have the notion of class methods. These are methods shared by all objects. You can even call them directly, without going through an instance.
In JavaScript, we just need to place a function in our namescape materializing the class. This function will then be encapsulated in the namespace and it will not be instantiated with each object.
Example:
var Robot = ( function ( ) {
'use strict' ;
// static method (class method )
Robot.build = function ( name ) { return new Robot ( name ) ; } ; return Robot ; } ( ) ) ; // Its use is done through the namespace, but without going through an instance var titus = Robot. build ( 'Titus' )
;
// The following statement displays: titus.build is undefined
console. log ( ' titus.build is' , titus.build ) ;
(code-box)
Second example: inheritance in JavaScript
We are going to code a Human class which will extend the Robot class. I know this may sound weird to you but I was thinking that we were sort of robots… And then don't we know how to do what robots do (walk, talk) and many more other things ( drink, …).
Inheritance mechanism
Previously, we have already visualized inheritance in the Notion of constructor paragraph .
The basis of inheritance is:
to create a reference of the prototype of the parent class inside the prototype of the child class,
copy the members of the parent object to the child object
Illustration of an inheritance in JavaScript:
This diagram is abstract but it represents “who contains what”. All this will be clarified with the example of the child class of the Robot class.
Function with fallback to simplify writing inheritance
Before introducing you to the daughter class of the Robot class, we need to introduce a function to make our life easier later on.
This function ensures the first basis of inheritance: encapsulating a reference of the prototype of the parent class in the prototype of the child class.
Since ECMAScript5 (latest recommended standards for JavaScript to keep it simple), we have an extremely simple feature to do this:
// We assume that the Robot class is already loaded (constructor+prototype are therefore defined)
// We assume that the Human constructor is already defined...
// We create the inheritance of the
Human prototype. prototype = Object. create ( Robot.prototype ) ; _
(code-box)
The problem is that old browsers don't understand this method... So here is a function that will work all the time:
/**
* This function returns an object that inherits the properties of the
* prototype of the parameter object p.
*
* This function contains a "fallback", ie a trick that
allows the function * to provide a correct result even in the case
* of old browsers (not implementing the ECMAScript5 standard).
*
* This function is taken from "Javascript, The Definitive
* Guide" by David Flanagan, O'Reilly Edition, 6th Edition (2011).
*
* @method inherit
*/
function inherit ( p ) {
'use strict' ;
p == null ) throw new TypeError ( ) ;
// --- Normal case ---
// If Object.create() is defined (case of recent browsers
// implementing ECMAScript5 functions)...
if ( Object. create ) return Object. create ( p ) ;
// --- Fallback (for naughty browsers) ---
// Type of object passed as function parameter
var t = typeof p ;
// Otherwise do some more type checking
if ( t !== "object" & ;& ; t !== "function" ) throw new TypeError ( ) ;
// We define an empty prototype
function F ( ) { }
// We apply p to this prototype (inheritance)
F. prototype = p ;
// Create a new heir of p, and return it
return new F ( ) ;
}
(code-box)
Example of a child class of the Robot class
Here is finally our daughter class!
/**
* Modeling a human.
*
* In our society, we are often likened to
* robots... We are therefore going to extend the Robot class. Thus, a
* Human will be "a kind" of Robot.
*
* @class Human
* @extends Robot
*/
var Human = ( function ( ) {
'use strict' ;
// The prototype of humans inherits from the prototype of robots
// So a human can walk and talk
Human. prototype = inherit ( robot.prototype ) ; / **
* Possible genders for a human
* @property SEX
* @static
* @type Array
*/
Human. SEX = [ 'MAN' , 'WOMAN' ] ;
/**
* Constructor
* @constructor
* @param {String} name Human name
* @param {Number} age Human age
*/
function Human ( name , age ) {
this . age = age ;
// We call the constructor of the parent class in order to not
// no need to rewrite code (eg. this.name = name;)
Robot. apply ( this , arguments ) ;
}
/**
* A human can drink (while robots cannot because
* they would rust...)
* @method drink
*/
Human. prototype . drink = function ( ) {
console. log ( 'I am drinking' ) ;
} ;
/**
* Humans can talk, but they're smarter than
* the robots. They are also more talkative...
*
* We overload the Robot.speak method here
*
* @method speak
*/
Human. prototype . speak = function ( ) {
// Call the method of the parent class
Robot. prototype . speak . apply ( this , arguments ) ;
// We add a specific process after the call to the inherited
console method. log ( 'I am human and I am ' + this. age + 'years old.' ) ;
} ;
// The Human.prototype.walk method is not defined, we
simply inherit // from that of the parent class.
// Indeed, a human walks exactly like a Robot, well less
// jerky all the same...
// We're thinking of returning the constructor
return Human ;
} ( ) ) ;
(code-box)
Example of use:
var corben = new Human ( 'Corben' , 38 ) ;
corben. speak ( ) ;
corben. walk ( 'south' ) ;
corben. walk ( 'west' ) ;
corben. drink ( ) ;
(code-box)
Remarks:
- By inheriting the prototype, a human can now walk and talk. We can then be satisfied with this definition, or overload it. In this second case, you can still call the method of the parent class (for example if you want to do the same process to which you add other actions).
- In the constructor, we are not obliged to call the constructor of the mother class but it is a strongly recommended operation. Otherwise you have to duplicate the code, which is an anti-pattern…
- JavaScript's “apply” method is really powerful. It allows invoking a method of any object on another object. Basically, this amounts to simulating a deportation of code from one object to another just for one call! This is the magic of ultra-dynamic languages like JavaScript!
If we analyze the type of the corben instance with the instanceof operator , we can see that corben is a Human (we already knew that) but he is also a Robot ! Our inheritance can therefore be easily viewed at run-time.
Hope you now understand JavaScript better! ;-) It's not complicated but you have to understand the basic concepts which are really very different from the rest of the languages...
For further
Principle of Duck-Typing and polymorphism
Polymorphism is natural in JS since it is a weakly typed language.
JavaScript is ultra flexible (at the antipodes of C++ for example). In the JavaScript community, the principle of DuckTyping is often applied .
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. [Heim, Michael]
To put it simply, when manipulating an object, it is more important to know what an object can do than to find out what its type or family is.
In our example, we want our objects to speak, no matter if they are robots, humans or ducks...
// Suppose we have a "bipeds" array containing objects (Robot, Human, possibly other types of objects...)
bipeds. forEach ( function ( v , i , a ) {
// What should not be done, because we are not sure that all objects can speak...
// v.speak();
// 1st possibility correct
try {
v. speak ( ) ;
}
catch ( error ) {
}
// 2nd correct possibility
if ( typeof v. speak !== 'undefined' ) v. speak ( ) ;
} ) ;
(code-box)
Here is how to iterate on a heterogeneous collection and try to make these objects speak. I showed 2 possible solutions to not get a crash if the object couldn't speak. There are probably dozens…
This example demonstrates the ease of using polymorphism and DuckTyping.
Visibility of object attributes
In JavaScript, it is possible to use the scope of a function to limit visibility. Indeed, in the following code, the "privateAttr" attribute is not visible from the outside:
var Robot = ( function ( ) {
function Robot ( ) { }
// Private attribute
var privateAttr = 'private member' ;
Robot. prototype . speak = function ( ) {
// This method knows about privateAttr
console. log ( privateAttr ) ;
} ;
return Robot ;
} ( ) ) ;
console. log( ( new Robot ( ) ) . speak ( ) ) ; // Display:
console private member. log ( 'privateAttr is' , typeof privateAttr ) ; // Displays: privateAttr is undefined
(code-box)
Another practice is to leave everything public and prefix attribute and method names with “underscores” to indicate whether they are public, protected, or private. This is the case for Python for example. In PHP, too, this was a very common practice before the introduction of the “private”, “protected” and “public” keywords.
Two conventions are possible for this:
1.Everything that is public remains unchanged. Everything that is protected (ie private but from which the child classes still inherit), is prefixed by _. Anything private is prefixed with __.
Example:
var Robot = ( function ( ) {
'use strict' ;
function Robot ( name ) { //
Public instance attribute
this . name = name ;
// Indicate that this instance attribute is protected
this._category = null ;
// Indicate that this instance attribute is private
this .__specimen = null ;
}
return Robot ;
} ( ) ) ;
(code-box)
2.We only consider public and private visibility and we prefix with a single _ in the case of private data.
Obviously these are conventions and they do not protect anything like the mechanisms of C++ or Java. They are just indicative. We can read and modify private data if a bit of madness crossed our minds...
By using such conventions, we empower the user developer of an API for example. If for his debugging needs, he needs to access private variables, he will not be blocked! It's rather interesting. On the other hand, if in his final code, he does the same, and if the internal code of the API is modified later… he will be obliged to redo his code! Too bad for him… He was warned that the data was private, so not to be used…
Main pitfalls in JavaScript
The forgetting of the new
Forgetting the new keyword is very problematic when you want to use a non-trivial constructor (ie a constructor that you have coded yourself).
Example:
In this example, we have instantiated a but the object b is not correctly instantiated (omission of the new keyword ). As a result, it is “undifined”…
A trick is to do a test in the constructor, and possibly return an instance. Here is the most common trick to overcome this problem:
function Robot ( name ) {
// With this trick, omitting the "new" keyword is of no consequence...
if ( ! ( this instanceof Robot ) ) return new Robot ( name ) ;
// Normal continuation of the
this constructor . name = name ;
}
(code-box)
The particularity of the scope of variables in JavaScript
The scope of variables in JavaScript is very tricky! The scope of a variable is the function. If a variable is defined outside of any function, then it is global! If the var keyword is not used, then the variable is global, regardless of where it is defined.
Example:
var a = 'a' ; // global variable
//b = 'b'; // global variable because not declared with "var" (invalid in 'use strict' mode: exception raised)
function f ( ) {
var c = 'c' ; // local variable to function f()
// d = 'd'; // global variable because not declared with "var" (invalid in 'use strict' mode: exception thrown)
}
f ( ) ;
(code-box)
Until then, not too violent you will tell me... It's quite close to C and Java...
Now here are some features that may surprise you when you discover JavaScript…
function g ( ) {
if ( true ) {
var e = 'e' ; // Pitfall: here the scope being the function, e is local to g() and not to the if() {}
}
console. log ( 'Here "e" does exist because the scope is the function => ' , typeof e !== 'undefined' ) ;
}
g ( ) ;
function h ( ) {
var i = 'i' ; // local to the h() function
function p ( ) {
console. log ( 'Here "i" is accessible because in JavaScript we have the notion of lexical scope => ' , typeof i !== 'undefined' ) ;
i += 'i' ;
}
p ( ) ;
console. log ( '"i" changed in subfunction, new value => ' , i ) ;
}
h ( ) ;
function n ( ) {
varj = 'j' ;
function q ( ) {
var j = 'dd' ; // Here we define a local variable to q() which will be different from the one defined in n()
console. log ( 'In q(), we aj === "dd" => ' , j === 'dd' ) ;
}
q ( ) ;
console. log ( 'In n(), we aj === "j" => ' , j === 'j' ) ;
(code-box)
Result of this test script:
To remember :
- The scope in JavaScript is not the brace block but the function!
- Forgetting the var keyword has serious consequences because the global scope is polluted. We then risk collisions with other global variables… Hence the usefulness of namespaces…
- Always use strict mode, it generates errors and avoids side effects when you forget the keyword “var” for example… All you have to do is add 'use strict'; at the start of the function or at the start of the file if you want to apply strict mode globally.
- Good practice: always define the variables at the beginning of the function, it requires rigor and avoiding the old habits of C for example…
Example of good practice:
// Good practice for defining variables
function function ( ) {
// Defining all variables local to the function
var a = 1 ,
b , // not initialized, equivalent to: b = undefined; which is not a problem, unlike C++ where the initial values are arbitrary
c = 'c' ;
// Code of the function that no longer defines variables...
// code...
}
(code-box)
The keyword this
next paragraph
In the meantime, here are the best resources I could find:
http://example.com/index.php/2009/09/an-introduction-to-javascripts-this/
http://example.com/2010/08/30/understanding-javascripts-this/
asynchronism
next paragraph
The references
It's not really a trap. It's exactly like in Java. Primitive types (undefined, null, string, number, boolean) are not manipulated by reference, while all objects (which respond "object" to the "typeof" operator), are manipulated by reference.
Brief reminder of the consequences on the manipulation of objects by references:
Explanations: We define a literal object o containing an integer i initialized with the value 1 . We define a second object o2 equal to o (by equal, we must understand that the references point to the same object in memory). We increment i which is a field of the object o , we can clearly see that i is worth 2 . By looking at o2 , we confirm that the references o and o2 do indeed point to the same object in memory (because o2.i is indeed 2 ).
Consequently, if one wishes to duplicate the object in memory and thus obtain 2 distinct instances, one must use cloning (exactly as in Java). Cloning consists of copying field by field one object into another (recursively). This comes down to the C++ copy constructor that you have to define yourself if you want to make a "deep" copy.
Documenting a JavaScript project
To document a JavaScript project, there are several documentation engines based on the model of the famous JavaDoc (for Java as you might have guessed…).
I only tested YUIDoc from Yahoo. Do not hesitate to advise me of other solutions if you know of better ones.
Here is an example of the small test project developed for the purposes of the tutorial: example documentation.
Source code
The source code associated with this post isn't very interesting on its own, but in case anyone wants to see it, here it is source code.
(getButton) #text=(Source Code) #file=(Size: 60k) #icon=(download) #size=(1) #color=(#1bc517) #info=(Download)
To see part of it online:
- https://www.successbeta.com/p/robotjs.html
- https://www.successbeta.com/p/humanjs.html
- https://www.successbeta.com/p/maternityjs.html
Conclusion
We have taken a good tour of the JavaScript language. I hope this post has helped you better understand the basics and some subtleties of this language.
JavaScript is not simple but it is an exciting language. I admit that it is one of the languages that I find the most interesting (along with Python which is also excellent). Its dynamic appearance is really impressive!
The post is not exhaustive, I invite you to dig into the following points which I have not (or little) addressed:
closures
patterns in Javascript (namespaces, modules, classes, sandbox, …)
design-patterns as in other languages (factory, decorator, singleton, facade, etc.)
And then, consider experimenting! This is how we learn. Just open a JavaScript console in a browser…;-)