The Many a One JavaScript Quirks
The Mastering the Arcane Art of JavaScript-mancy series are my humble attempt at bringing my love for JavaScript to all other C# developers that haven’t yet discovered how awesome this language and its whole ecosystem are. These articles are excerpts of the super duper awesome JavaScript-Mancy book a compendium of all things JavaScript for C# developers.
For many years has JavaScript been frown upon and look down on by many developers due to its quirky nature, obscure behaviors and many a one WTFs that populate its hairy APIs and operations.
Frown upon no more! For with modern design patterns, libraries, tools and the long awaited JavaScript 6 Harmony (ES6, ES2015) writing JavaScript is now a pleasure.
Join me at the school of JavaScript-mancy as we travel along the modern landscape of writing JavaScript in 2015 and beyond, as we discover the organic evolution of this beautiful language and its thriving ecosystem, and delve in the latest features/spells/incantations of the JavaScript Arcana.
You my friend, cannot ignore JavaScript no longer, JavaScript is the most deployed language on earth, a beautiful and expressive language that supports many paradigms and which has a thriving community that continuously expands and improves its ecosystem with patterns, libraries, frameworks and tools. You don’t want to miss this train.
But JavaScript, though forgiving and specially conceived to be easy to learn, can be either daunting for us that have a strongly-typed mindset baggage and come from languages such as C# or Java or even more often laughed at as a toy.
For you who consider it daunting and hate working with it worry not! I will show you the most common missconceptions and all the patterns you need to know to become as fluent in JavaScript as you are in C#.
For you who consider it a toy language, something associated not with real programming but with copy-paste coding or scripting to add flare to websites, I will show you all the different patterns and programming paradigms that JavaScript supports and which make it just as good and powerful as C#.
Let’s get started!
JavaScript Quirks
One of the main things that missguides C# developers when they come to JavaScript is that while JavaScript looks like a C-like language, it does not behave like one in many ways. Four examples of these are: the unbecoming/unexpected behavior of this
, that variables have function and not block scope, the strange phenomenon know as variable hoisting and the lack of explicit namespacing.
In the next few sections I will dive deeper into each of these quirks and try to save you some frustration the next time you write some JavaScript. And before you continue, you can find all the examples I use in this blog post in this jsFiddle, you can even play with the examples side by side with the article to get a better understanding of how JavaScript works.
This and JavaScript
One of the most common problems when a C# developer comes to JavaScript is that it expects this
to work exactly as it does in C#. And She or He will write this code unaware of the dangers that lurk just an HTTP call away…
function UsersCatalog() {
this.users = []
getUsers()
function getUsers() {
$.getJSON('https://api.github.com/users').success(function(users) {
this.users.push(users)
// BOOOOOOOM!!!!!
// => Uncaught TypeError: Cannot read property 'push' of undefined
})
}
}
var catalog = new UsersCatalog()
Let’s see why this happens, how this
works in JavaScript and we will round up and come back to this example at the conclusion once we have demystified this
and become this-xperts.
So this
in JavaScript is weird, and unlike other languages this depends on the context in which a function is invoked. This basically means that depending on how you call a function this
will have a value or another:
this
and objectsthis
unboundthis
explicitlythis
bound
This And Objects
In the most common scenario for an OOP developer we call a function as a method, that is a function that is a property within an object (using the dot notation as usual). In this case and as we would expect this
will take the value of the object itself:
// #1. A function invoked in the context of an object (a method)
var hellHound = {
attackWithFireBreath: function() {
console.log(
this +
' jumps towards you and unleashes his terrible breath of fire! (-3 hp, +fear)'
)
},
toString: function() {
return 'Hellhound'
},
}
hellHound.attackWithFireBreath()
// => Hellhound jumps towards you and unleashes his terrible breath of fire! (-3 hp, +fear)
// 'this' is the hellHound object
This Unbound
But you can also invoke a function without the context of its object:
// #2. A function invoked without the context of its object
var attackWithFireBreath = hellHound.attackWithFireBreath
attackWithFireBreath()
// => [object Window] jumps towards you and unleashes his terrible breath of fire! (-3 hp, +fear)
// ups, 'this' is no longer the hellHound object but the Window object. What?
// By default the context of execution of a function is the Window object
// if you use strict mode it will be undefined => use strict mode
Whenever you invoke a function without an object as context the this
automatically becomes the window object
unless you have set strict mode on in which case this
will take the value of undefined
.
We could even take that very same function and add it to another object different from the original:
// we could add the same method to another object:
var jaime = {
toString: function() {
return 'jaime'
},
}
jaime.attackWithFireBreath = attackWithFireBreath
jaime.attackWithFireBreath()
// => jaime jumps towards you and unleashes his terrible breath of fire! (-3 hp, +fear)
// => 'this' is the jaime object
And again, when we invoke the original function in the context of an object, even another one different from the original one, this
takes the value of that object.
This Explicitly
JavaScript Function
prototype, from which all functions within JavaScript descend, provides two helpful methods that allow you to explicitly set the context within which a function is executed. These methods are call
and apply
and they work by taking the context (this
) as an argument:
// #3. We can also explicitly set the context of execution of a function
// all functions have call and apply:
attackWithFireBreath.call(jaime /*, arg1, arg2, */)
// => jaime...
// => 'this' is jaime
attackWithFireBreath.apply(hellHound /*, [arg1, arg2, arg3...] */)
// => hell hound...
// => 'this' is hellHound
This Bound
And additionally, as of ECMAScript 5, the Function
prototype also provides the bind
function that lets us create new functions that always have a fixed context, that is, a fixed value for this
.
It is important to note that bind
will not alter the original function but will return a new function that is bound to the object given as an argument. Once a function is bound it is not possible to un-bound it nor re-bind it to another object (but you can always use the original unbound function):
// #4. And as of ES5 we can bind the context of execution of a function FOR EVER
attackBound = attackWithFireBreath.bind(hellHound)
attackBound()
// => Hellhound jumps towards you and unleashes his terrible breath of fire! (-3 hp, +fear)
// `this` is Hellhound even though I am not using the dot notation
// even if I give the function to another object
jaime.attackBound = attackBound
jaime.attackBound()
// => Hellhound ...
// `this` is Hellhound even though I am using the dot notation with another object
// You cannot rebind a function that is bound
var attackReBound = attackBound.bind(jaime)
attackReBound()
// => Hellhound ...
attackBound()
// => hellHound ...
// But you can still bind the original
var attackRebound = attackWithFireBreath.bind(jaime)
attackRebound()
// => jaime...
Concluding This
So in summary, this
can take different values based on how a function is invoked. It can:
- Be an object if we call a function within an object with the dot notation
- Be the
window object
orundefined
(strict mode) if a function is invoked by itself - Be whichever object we pass as argument to
call
orapply
- Be whichever object we pass as argument to
bind
.
And if now that you are a this-xperts you go back to the original example you will be able to easily spot the problem. Since the success
function is not invoked in the context of our object, the UsersCatalog
, this
is not our original object. In this particular case this
is the jqXHR object which for sure does not have a users
property and thus the cannot read property of undefined error.
function UsersCatalog(type) {
this.users = []
getUsers()
function getUsers() {
$.getJSON('https://api.github.com/users').success(function(users) {
this.users.push(users)
// BOOOOOOOM!!!!!
// => Uncaught TypeError: Cannot read property 'push' of undefined
// 'this' in this context is the jqXHR object
// not our original object
})
}
}
var catalog = new UsersCatalog()
You can solve this issue in either of two ways. You can take advantage of JavaScript support for closures, declare a self
variable that “captures” the value of this
and use it within the closure function as depicted below (very common practice in JavaScript):
function UsersCatalogWithClosure() {
'use strict'
var self = this
self.users = []
getUsers()
function getUsers() {
$.getJSON('https://api.github.com/users').success(function(users) {
self.users.push(users)
console.log('success!')
})
}
}
var catalog = new UsersCatalogWithClosure()
Or you can use bind
and ensure that the function that you use as callback is bound to the object that you want:
//#2. Using bind
function UsersCatalogWithBind() {
'use strict'
this.users = []
getUsers.bind(this)()
function getUsers() {
$.getJSON('https://api.github.com/users').success(updateUsers.bind(this))
}
function updateUsers(users) {
this.users.push(users)
console.log('success with bind!')
}
}
var catalog = new UsersCatalogWithBind()
Function Scope and Variable Hoisting
Another interesting quirk about JavaScript is that, unlike many other languages, variables in JavaScript have function scope and not block scope. This can be better illustrated by the example below:
function aFunctiontoIllustrateFunctionScope() {
if (true) {
var x = 10
}
console.log(x)
}
aFunctiontoIllustrateFunctionScope()
// => 10 WAT
What happens in this example is that the JavaScript runtime hoists all variables within a function definition to the beginning of the function body. This means that the function that you’ve seen above it actually works like this:
function aFunctiontoIllustrateFunctionScope() {
// hoisted variables
var x = undefined
if (true) {
x = 10
}
console.log(x)
// => 10
}
And yet another example of hoisting:
function aFunctionToIllustrateHoisting() {
// in reality
// var i = undefined
console.log('before i =' + i)
// => before i = undefined
for (var i = 0; i < 10; i++) {
// jara jara
}
console.log('after i = ' + i)
// => after i = 10
}
And that’s the reason why JavaScript developers usually write all variable declarations in the beginning of a function, to be more explicit about what happens in reality when a given function is evaluated by the JavaScript runtime and avoid unnecessary bugs. Take a look at jQuery for instace:
/**
* Load a url into a page
*/
jQuery.fn.load = function(url, params, callback) {
var selector,
type,
response,
self = this,
off = url.indexOf(' ')
//... more codes
}
As a final note, you’ll be happy to hear that the next version of JavaScript ECMAScript 2015 introduces the let
keyword that brings block scope variables to JavaScript.
Namespacing in JavaScript
As you will come to appreciate by the end of the series, JavaScript has a minimalistic design and a limited number of primitive constructs that can be used and composed to achieve higher level abstractions and constructs that are native to other languages, one of these being namespaces.
Since we do not have namespaces in JavaScript and yet we want to avoid declaring all of our variables in the global “namespace” (where they would be visible to every single module):
var dice = 'd12'
dice
// => d12
window.dice
// => d12
// ups... we are in the global scope/namespace
We use objects to emulate the construct of namespaces. A common pattern is depicted below where we use a IIFE (immediately invoked function expression) to create/augment a namespace:
// IIFE - we invoke the function expression as soon as we declare it
;(function(armory) {
// armory being the namespace
armory.sword = { damage: 10, speed: 15 }
armory.axe = { damage: 15, speed: 8 }
armory.mace = { damage: 16, speed: 7 }
armory.dagger = { damage: 5, speed: 20 }
})((window.armory = window.armory || {})) // either augment or create the armory namespace
console.log(armory.sword.damage)
// => 10
I will come back to namespacing and higher level code organization in JavaScript when we discuss JavaScript modules.
Summary
And that’s that for the first chapter of the JavaScript-mancy series. Hope that you have enjoyed and learned something new about JavaScript and if you already knew all this stuff, don’t worry, there are more exciting and interesting things coming like functional programming, cool OOP techniques and ES2015 (ES6), the next version of JavaScript.
Interested in Learning More JavaScript? Buy the Book!
Are you a C# Developer interested in learning JavaScript? Then take a look at the JavaScript-mancy book, a complete compendium of JavaScript for C# developers.
Have a great week ahead!! :)
More Articles In This Series
- Chapter 0: The Basic Ingredients of JavaScriptMancy
- Chapter 1: The Many a One JavaScript Quirks
- Functions
- Object Oriented Programming
- Basics
- ES6
- OOP
- Introduction to OOP in JavaScript for C# Developers
- Summoning Fundamentals: Introduction to OOP In JavaScript – Encapsulation
- Summoning Fundamentals: Introduction to OOP In JavaScript – Inheritance
- Summoning Fundamentals: Introduction to OOP In JavaScript – Polymorphism
- White Tower Summoning: Mimicking C# Classical Inheritance in Javascript
- White Tower Summoning Enhanced: The Marvels of ES6 Classes
- Black Tower Summoning: Object Composition with Mixins
- Safer JavaScript Composition With Traits and Traits.js
- JavaScript Ultra Flexible Object Oriented Programming with Stamps
- Object Oriented JavaScript for C# Programmers
- Tooling
- Data Structures
- Functional Programming
- And more stuff to be shaped out:
- Functional Programming with JavaScript
- Modules
- Asynchronous Programming
- The JavaScript Ecosystem
P.S. Did you know that these series started as a talk? Check Mastering the Arcane Art of JavaScript-mancy and other of my talk slides at SpeakerDeck.
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.