I feel that JavaScript is a very misunderstood language – especially by many classical Object-Oriented developers (most that I know of anyway, myself included some years ago) that work primarily with C#
or Java
. This post isn’t to serve as a complete guide, it’s not a best practice guide, and it’s not for beginners – It’s a quick run through the JavaScript’s oddities classical OO developers may find.
Everything is an Object
That’s right, everything in JavaScript is an Object, including functions (apart from primitives, kind of…). Primitive types in JavaScript are much like how they are in C#, they coerce into their Object type counter-part when needed to execute a function. i.e. 'abcefg'.indexOf('ce')
and reverts back to primitive type once the function returns.
The “this” keyword
The this
keyword behaves differently in JavaScript. In classical OO languages such as Java
or C#
, this
refers to the current Object, in JavaScript, this isn’t always necessarily true. this
can be changed depending on how it is called.
this
in JavaScript, by default, refers to the object this
is enclosed in, much like in Java and C#. For example:
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
}
}
var cNorris = new Person("Chuck Norris");
cNorris.sayName();
The example snippet shown above would expectedly print “Chuck Norris”.
What if I call the function sayName()
– this
refers the cNorris object.
But as explained previously, this isn’t always the case. Continuing from the previous example:
function Person(name) {
this.name = name;
this.sayName = function() {
console.log(this.name);
}
}
var cNorris = new Person("Chuck Norris");
var bLee = new Person("Bruce Lee");
cNorris.sayName.call(bLee); // prints Bruce Lee
This snippet shown above prints out “Bruce Lee”, but why? Well, as mentioned previously, function’s in JavaScript are also Objects, and we’re actually using the function call
to change this
.
Why would you want to change what this
refers to? For code reuse of course. How? well, let’s take a look at DOM
event handling for a second.
HTML
<input type="text" id="myInput">
<input type="text" id="myOtherInput">
JavaScript
var logValue = function() {
console.log(this.value);
}; // we're reusing this function
var myInput = document.getElementById('myInput');
var myOtherInput = document.getElementById('myOtherInput');
myInput.addEventListener('change', logValue);
myOtherInput.addEventListener('change', logValue);
Makes a lot of sense doesn’t it? this
within logValue
function in this case (as how it’s used) refers to the element that fired the event.
Here’s a list of prototype functions that change lexical this
for reference:
Scope
JavaScript has no keywords specifically for controlling scope, so how do you control the scope of a function or variable?
var
Using the var
keyword, you are making the variable only available to the enclosing function and functions within it. This essentially makes your variable private. For example:
function printOne() {
var myVar = 1; // private
function myFunc() {
function getMyVar() {
return myVar; // has access to myVar
}
console.log(getMyVar());
}
myFunc(); // prints 1
}
myFunc(); // error, myFunc does not exist within this scope
Closure
MDN documentation on closure is a must read to fully understand this effect.
One of the things to keep in mind is that, in JavaScript, the scope of variables are contained within functions rather than blocks.
Look at this example:
for(var i=0; i < 10; i++) {
console.log(i);
}
Coming from a C#
or Java
background, it would be logical to think that this would print out 0
to 9
.
What if we change it slighty – what if we add a timeout?
for(var i=0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
Initially, it might be logical to think that this would print the same result with a 1000ms delay. But it doesn’t, it prints out 10
ten times over (in almost 100% of cases, however, it can theoretically be different making this simple piece of code or algorithm if you will, non-deterministic).
We can fix this using a closure:
for(var i=0; i < 10; i++) {
(function(i) {
setTimeout(function() {
console.log(i);
}, 1000);
})(i);
}
Have a look at the example above carefully – you may be wondering why this worked, and the previous one didn’t. What we are doing is for each loop, we are passing i
to an immediately invoked function, also known as an IIFE. When anything gets passed to an IFFE, it becomes part of the IFFE’s state, so in this case, i
is passed as a value due to it being a primitive type, creating a new i
variable within the scope of the IFFE hiding i
in the global scope. Have a look at this example:
for(var i=0; i < 10; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
console.log(j === i); // false
}, 1000);
})(i);
}
i
from the global scope is passed as j
within the IFFE
, so we are no longer hiding i
. If you run this, you will see that j === i
will always return false – why? well, i
in the global scope has most likely already been incremented to 10
, and j
in the IFFE’s scope only ever gets to the value of 9
.
To summarise, a closure is an assigned self-invoking function, this produces an effect whereby, everything within the returned function’s scope is not deallocated from the memory, meaning it gets to maintain its own state.
var square = (function() {
var val = 2;
return function() {
var result = val;
val = Math.pow(val,2);
console.log(result);
}
})();
for(var i = 0; i < 4; i++) {
square();
}
The example above would print out:
2
4
16
256
this
You can attach variables to the current this
which exposes the function to public through the Object.
function MyObj(val) {
this.val = val;
}
var myObj = new MyObj(11);
console.log(myObj.val); // 11
prototype
functions attached to the prototype
of an Object
is exposed as public through the Object.
function MyObj(val) {
this.val = val;
}
MyObj.prototype.printVal = function() {
console.log(this.val);
}
var myObj = new MyObj(1);
myObj.printVal(); // 1
Want to know more about JavaScript quirks? Continue reading part two.