Tome I. Mastering the Arcane Art of JavaScript-mancy
Introduction
For many years JavaScript has been frowned upon and looked down on by many developers due to its quirky nature, obscure behaviors and many WTFs that populate its hairy APIs and operations.
Frown upon no more! For with modern design patterns, libraries, tools and the long awaited ECMAScript 6 (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, can no longer ignore JavaScript. 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 and come from languages such as C# or Java or, 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 misconceptions 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!
Once Upon a Time…
Once upon a time, in a faraway land, there was a beautiful hidden island with captivating white sandy beaches, lush green hills and mighty white peaked mountains. The natives called it Asturi and, if not for an incredible and unexpected event, it would have remained hidden and forgotten for centuries.
Some say it was during his early morning walk, some say that it happened in the shower. Be that as it may, Branden Iech, at the time the local eccentric and today considered the greatest Philosopher of antiquity, stumbled upon something that would change the world forever.
In talking to himself, as both his most beloved companions and his most bitter detractors would attest was a habit of his, he stumbled upon the magic words of JavaScript and the mysterious REPL.
In the years that followed he would teach the magic word and fund the order of JavaScriptmancers bringing a golden age to our civilization. Poor, naive philosopher. For such power wielded by mere humans was meant to be misused, to corrupt their fragile hearts and bring their and our downfall. It’s been ten thousand years, ten thousand years of wars, pain and struggle.
It is said that, in the 12th day of the 12th month of the 12th age a hero will rise and bring balance to the world. That happens to be today.
12th Age, Guardian of Chronicles
This book has a story in it. It is a story of a fantasy1 world where some people can wield JavaScript to affect the world around them, to essentially program the world and bend it to their will. Cool right? The story follows the step of a heroine that comes to this hypothetical world to save it from evil, but of course, she needs to learn JavaScript first. Care to join her in her quest to learn JavaScript and save the world?
The Essential Ingredients Of JavaScript-Mancy
The importance of the fundamentals cannot be enough overstated,
the gathering of the proper ingredients for an incantation,
the carefulness and caring of the preparations,
the timing and intimate knowledge of the rituals,
everything affects the end result.
To practice is to be,
practice as you want to be,
for do you want to be an Artful Wizard, or a mediocre one?
- Kely Sompsin,
Maester of the Guild,
Fourth Age
An Introduction to JavaScript-Mancy
I expect the reader of this manuscript to be well acquainted with the basics of programming, but I couldn’t, in good faith, start the book without some sort of introduction to the arts of JavaScript. This first chapter will therefore take you through the whole breadth of the JavaScript language, albeit in a superficial way. I will show you all the basic features of JavaScript, even those of its latest incarnation ECMAScript 6, and how they differ or are similar to C#. I abhorr starting programming books with page after page on for loops and if statements so I will attempt to be as brief, interesting and entertaining as I can.
If you feel like you are well versed in the basics of JavaScript (and the new ES6 features) then by all means jump over this chapter, but be aware that there’s a story happening in the examples, so you might be interested in taking a look. In any case, here we go…
JavaScript
JavaScript, as we the guardians of JavaScript-mancy usually call the arts, is a multi-paradigm dynamic programming language. The multi-paradigm bit means that JavaScript lends itself well to different styles (paradigms) of programming like object-oriented or functional programming. The dynamic part means that… well the dynamic part is widely disputed even today… but for all intents and purposes we can say that JavaScript is evaluated as it is executed, there’s no compilation step in which variables are bound, types are checked and expressions analyzed for correctness. The JavaScript runtime defers all this work until the code itself is being executed and this allows for a lot more freedom and interesting applications like metaprogramming 2.
JavaScript is also dynamically typed so a variable can reference many different types during its lifetime and you can augment objects with new methods or properties at any point in time.
I can, for example, summon a minion from the depths of hell using the var keyword and let it be a number:
1 > var minion = 1
2 > minion
3 // => 1
And I can do some alchemy thereafter and transform the minion into something else, a string, for example:
1 > minion = "bunny";
2 // => "bunny"
I can keep doing that for as long as I want (as long as I have enough mana3 of course), so let’s make my minion an object:
1 > minion = {name: 'bugs', type: 'bunny'};
2 // => Object {name: 'bugs', type: 'bunny'}
In JavaScript I don’t need a class to create an object. I can just create an object with whichever properties I desire and then later on augment 4 it with new properties to my heart’s content:
1 > minion.description = 'A mean looking bunny';
2 > console.log(minion);
3 // => Object {name: bugs, type: bunny,
4 // description: A mean looking bunny}
JavaScript Has Some Types
JavaScript supports the following types: Number, String, Object, Boolean, undefined, null and Symbol.
As you may have guessed, JavaScript has a single type to represent all numbers. Which is pretty nice if you ask me, not having to ever think about doubles, and shorts, and longs and floats…
1 > var one = 1
2 > typeof(one)
3 // => "number"
4
5 > var oneAndAHalf = 1.5
6 > typeof(1.5)
7 // => "number"
There’s a string type that works as you would expect any respectable string to behave. It lets you create string literals. Interestingly enough, JavaScript strings support both single (') and double quotes ("):
1 > var text = "Thou shalt not pass!"
2 > typeof text
3 // => "string"
4
5 > var anotherBitOfText = 'No! Thou Shalt Not Pass!'
6 > typeof anotherBitOfText
7 // => "string"
JavaScript also has a boolean type to represent true and false values:
1 > var always = true
2 > var never = false
3 > typeof(always)
4 // => "boolean"
And an object type that we can use to create any new custom types:
1 > var skull = {name: 'Skull of Dark Magic'}
2 > typeof(skull)
3 // => object
JavaScript differs from other languages in that it has two different ways of representing the lack of something. Where C# has null, JavaScript has both null and undefined. Unlike in C#, the default value of anything that hasn’t been yet defined is undefined, whereas null must be set explicitly.
1 > skull.description
2 // => undefined
3 > typeof(skull.description)
4 // => undefined
5
6 > skull.description = null;
7 > typeof(skull.description)
8 // => object :)
This can get even more confusing because of the fact that there’s a third possibility. That a variable hasn’t been declared:
1 > abracadabra
2 // => Uncaught ReferenceError: abracadabra is not defined
The confusion coming mainly from the error message: abracadabra is not defined. You can just think about these variables as undeclared instead of not defined and stick to the previous definition of undefined.
ECMAScript 6 brings a new primitive type, the symbol. Symbols can be seen as constant and immutable tokens that can be used as unique IDs.
1 > var crux = Symbol()
2 > typeof(crux)
3 // => symbol
Later within the book, we’ll see how we can use Symbols to enable new patterns for hiding data in JavaScript.
Everything Within JavaScript Behaves Like an Object
In spite of JavaScript not having the concept of value types or reference types, numbers, strings and booleans behave like C# value types and objects behave like C# reference types. In practice, however, everything within JavaScript can be treated as an object.
Numbers for instance, provide several useful methods:
1 > (1).toString()
2 // => 1
3 > (3.14159).toPrecision(3)
4 // => 3.141
5 > (5000).toLocaleString('sv-SE')
6 // => 5 000
And so do strings:
1 > "a ghoul".toUpperCase()
2 // "A GHOUL"
Interesting right? If a number is a primitive value type, how come it has methods? Well, what is happening is that, whenever we call a method on a number or other primitive type, the JavaScript runtime is wrapping the primitive value in a special wrapper object. So even though 1 is not an object itself, when JavaScript evaluates (1).toPrecision(3) it wraps the value within the Number object on-the-fly. You can test the reverse process and instantiate a number using the wrapper object directly:
1 > var number = new Number(1);
2 // => Number {}
3 > typeof(number)
4 // => 'object'
Then unwrap the original value with valueOf:
1 > number.valueOf()
2 // => 1
Even more remarkable than numbers and strings, functions behave like objects. They have their own methods:
1 > var fireBall = function(){ world.spell('A blazing ball of fire mat\
2 erializes from the palm of your hand!');};
3 > fireBall.apply
4 // => function apply(){}
And you can even add properties to a function:
1 {lang="javascript"}
2 > fireBall.maxNumberOfCharges = 5
3 // => 5;
4 > fireBall.maxNumberOfCharges
5 // => 5;
Let’s take a quick look at each one of these types and how they work in JavaScript.
Strings in JavaScript
Strings, like in C#, let you represent text literals.
1 > "hi there"
2 // => hi there
3 > "creepy"
4 // => creepy
5 > "stop repeating what I say"
6 // => stop repeating what I say
Unlike in C# you can use both single (') and double quotes (") to create a string. Oftentimes you will see one used to escape the other:
1 > "this ain't cool man"
2 // => this ain't cool man
3 > 'you think you are so "funny"'
4 // => you think you are so "funny"
Any string has a number of useful methods:
1 > "I am tired of you devious REPL".split(' ');
2 // => ["I", "am", "tired", "of", "you", "devious", "REPL"]
3 > "I am tired of you devious REPL".replace('tired', 'ecstatic');
4 // => I am ecstatic of you devious REPL
5 > "I am tired of you devious REPL".indexOf('tired');
6 // => 5
ES6 also brings a number of new methods like startsWith, endsWith, repeat:
1 > "Stop REPL!".startsWith("Stop");
2 // => true
3 > "Stop REPL!".endsWith("REPL!");
4 // => true
5 > "NaN".repeat(10) + " BatMan!!!"
6 // => NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN BatMan!!!!
7 > "ha! Now I beat you at your own game!"
8 // => "ha! Now I beat you at your own game!"
Until recently, there was no such thing as C# String.format nor StringBuilder so most injecting values in strings was done using the + operator or string.concat:
1 > var target = 'minion';
2 > "obliterate " + target + " with hellfire!"
3 // => obliterate minion with hellfire!
4 > "obliterate ".concat(target, " with hellfire!")
5 // => obliterate minion with hellfire!
Fortunately, ES6 brings template strings and a more elegant approach to string interpolation.
Better String Interpolation ES6 Template Strings
The new ES6 Template Strings improve greatly the way you can operate with strings. They allow string interpolation based on the variables that exist in the scope where the template is evaluated, thus providing a much cleaner and readable syntax.
In order to create a template string you surround the string literal with backticks and use ${variable-in-scope} to specify which variable to include within the resulting string:
1 > var spell = 'hellfire';
2 > `obliterate ${target} with ${spell}!`
3 // => obliterate minion with hellfire!
Template strings also let you easily create multi-line strings.
Where in the past you were forced to make use of string concatenation and the new line character \n:
1 > "One ring to rule them all\n" +
2 "One ring to find them;\n" +
3 "One ring to bring them all\n" +
4 "and in the darkness bind them"
5 // => One ring to rull them all
6 // One ring to find them;
7 // One ring to bring them all
8 // and in the darkness bind them
ES6 Template strings let you write a multi-line string in a more straightforward fashion:
1 > `One ring to rull them all
2 One ring to find them
3 One ring to bring them all
4 and in the darkness bind them`
5 // => One ring to rull them all
6 // One ring to find them;
7 // One ring to bring them all
8 // and in the darkness bind them
Functions in JavaScript
Functions are the most basic building component in JavaScript. As such, they can live more independent lives than methods in C# which are always tied to a class. So, you’ll oftentimes see functions alone in the wild:
1 > function obliterate(target){
2 console.log(`${target} is obliterated into tiny ashes`);
3 }
4 > obliterate('rabid bunny')
5 // => rabid bunny is obliterated into tiny ashes
JavaScript, in a radically different way than C#, lets you call a function with any number of arguments, even if they are not defined in a function’s signature:
1 > obliterate('rabid bunny', 'leprechaun', 'yeti')
2 // => rabid bunny is obliterated into tiny ashes
And even with no arguments at all, although depending on the function implementation it may cause some chaos and some mayhem:
1 > obliterate()
2 // => undefined is obliterated into tiny ashes
You can use the very special arguments array-like object to get hold of the arguments being passed at runtime to a given function:
1 > function obliterateMany(){
2 // ES6 method to convert arguments to an actual array
3 var targets = Array.from(arguments).join(', ');
4 console.log(`${targets} are obliterated into tiny ashes`);
5 }
6 > obliterateMany('Rabid bunny', 'leprechaun', 'yeti')
7 // => Rabid bunny, leprechaun, yeti are obliterated into tiny ashes
Or the finer ES6 rest syntax reminescent of C# params:
1 > function obliterateMany(...targets){
2 console.log(`${targets} are obliterated into tiny ashes`);
3 }
4 > obliterateMany('Rabid bunny', 'leprechaun', 'yeti')
5 // => Rabid bunny, leprechaun, yeti are obliterated into tiny ashes
In addition to functions working as… well… functions, they perform many other roles in JavaScript and are oftentimes used as building blocks to achieve higher-order abstractions: they are used as object constructors, to define modules, as a means to achieve data hiding and have many other uses.
ES6 changes this complete reliance on functions a little bit as it provides new higher level constructs that are native to the language, constructs like ES6 classes and ES6 modules which you’ll be able to learn more about later in this series. Indeed throughout this series I’ll show you both ES5 constructs, the present and the past, and ES6 ones, the present-ish and the future, so you’ll feel at home in any JavaScript codebase you happen to work with.
Functions as Values
An interesting and very important aspect of functions in javascript is that they can be treated as values, this is what we mean when we say functions are first-class citizens of the language. It means that they are not some special construct that you can only use in certain places, with some special conditions and grammar. Functions are just like any other type in JavaScript, you can store them in variables, you can pass them as arguments to other functions and you can return them from a function.
For instance, let’s say you want to create a very special logger that prepends your name to any message that you wish to log:
1 > var log = function(msg){ console.log(msg);}
2 > var logByRandalf = function (msg, logFn){
3 logFn(`Randalf logs: ${msg}`);
4 }
5 > logByRandalf('I am logging something, saving it to memory for ever\
6 ', log);
7 // => Randalf logs: I am logging something, saving it to memory for \
8 ever
But that was a little bit awkward, what if we return a function with the new functionality that we desire:
1 > var createLogBySomeone = function (byWho){
2 return function(msg){
3 return console.log(`${byWho} logs: ${msg}`);
4 };
5 }
6 > var logByRandalf = createLogBySomeone('Randalf');
7 > logByRandalf('I am logging something, saving it to memory for ever\
8 ');
9 // => Randalf logs: I am logging something, saving it to memory for \
10 ever
If you feel a little bit confused by this don’t worry, we will dive deeper into functional programming, closures and high-order functions later in the series. For now just realize that functions are values and you can use them as such.
JavaScript Has Function Scope
Another very interesting aspect of JavaScript that is diametrically opposed to how things work in C# is the scope of variables. JavaScript variables have function scope and not block scope. This means that functions define new scopes for variables and not blocks of code (if statements, for loops, code between {}, etc…) which highlights once more the importance of functions in JavaScript.
You can appreciate function scope in all its glory in these examples. First if you declare a single function with an if block you can verify how the if block doesn’t define a new scope as you would expect in C#:
1 > function scopeIsNuts(){ // new scope for scopeIsNuts
2 console.log(x); // => undefined
3 if (true){
4 var x = 1;
5 }
6 console.log(x); // => 1
7 }
But if we replace the if block with a new function inner, then we have two scopes:
1 > function outer(){ // new scope for outer
2 var x = 0;
3 console.log(x); // => 0
4
5 function inner(){ // new scope for inner
6 var x = 1;
7 console.log(x); // => 1
8 }
9 inner();
10
11 console.log(x); // => 0
12 }
ES6 let, ES6 const and Block Scope
ES6 attemps to bring an end to the confusion of JavaScript having function scope with the let keyword that allows you to create variables with block scope. With ES6 you can either use var for function scoped variables or let for block scoped ones.
If you rewrite the example we used to illustrate function scope with let, you’ll obtain a very different result:
1 > function scopeIsNuts(){ // new scope for scopeIsNuts
2 console.log(x); // => undefined
3 if (true){
4 let x = 1;
5 console.log(x); // => 1
6 }
7 console.log(x); // => undefined
8 }
Now the x variable only exists within the if statement block. Additionally, you can use the const keyword to declare constant variables with block scope.
1 > function scopeIsNuts(){ // new scope for scopeIsNuts
2 console.log(x); // => undefined
3 if (true){
4 const x = 1;
5 console.log(x); // => 1
6 x = 2; // => TypeError
7 }
8 console.log(x); // => undefined
9 }
ES6 Default Arguments
ES6 finally brings default arguments to JavaScript functions, and they work just like in C#:
1 > function fireBall(target, mana=10){
2 var damage = 1.5*mana;
3 console.log(`A huge fireball springs from
4 your fingers and hits the ${target} with ${damage} damage`);
5 }
6 > fireBall('troll')
7 // => A huge fireball springs from your fingers and hits the troll
8 // with 15 damage
9 > fireBall('troll', /* mana */ 50)
10 // => A huge fireball springs from your fingers and hits the troll
11 // with 75 damage
ES6 Destructuring
Another nifty ES6 feature is destructuring. Destructuring lets you unwrap any given object into a number of properties and bind them to variables of your choice. You can take advantage of destructuring with any object:
1 > var {hp, defense} = {
2 name: 'conan',
3 description: 'cimmerian barbarian king of thieves',
4 hp: {current: 9000, max: 9000},
5 defense: 100, attack: 400};
6 > console.log(hp);
7 // => {current: 9000, max: 9000}
8 > console.log(defense);
9 // => 100
Even when passing an object to a function:
1 function calculateDamage({attack}, {hp, defense}){
2 var effectiveAttackRating = attack - defense + getHpModifier(hp);
3 var damage = attackRoll(effectiveAttackRating);
4 return damage > 0 ? damage: 0;
5
6 function getHpModifier(hp){
7 return hp.current < 0.1*hp.max ? 10 : 0;
8 }
9
10 function attackRoll(dice){
11 // do some fancy dice rolling
12 return dice;
13 }
14 }
15
16 var troll = {
17 name: 'Aaagghhhh',
18 description: 'nasty troll',
19 hp: {current: 20000, max: 20000},
20 defense: 40, attack: 100
21 };
22 var conan = {name: 'conan',
23 hp: {current: 200, max: 200},
24 defense: 1000, attack: 1000
25 };
26 console.log(calculateDamage(troll, conan));
27 // => 0
28 // => no troll gonna damage conan
ES6 Arrow Functions
Another great feature brought by ES6 are arrow functions which resemble C# lambda expressions. Instead of writing the obliterate function as we did before, we can use the arrow function syntax:
1 /*
2 > function obliterate(target){
3 console.log(`${target} is obliterated into tiny ashes`);
4 }
5 */
6 > let obliterate = target =>
7 console.log(`${target} is obliterated into tiny ashes`);
8 > obliterate('minion');
9 // => minion is obliterated into tiny ashes
10 > obliterate('rabid bunny')
11 // => rabid bunny is obliterated into tiny ashes
And if you have a function with more arguments or statements, you can write it just like we do in C#:
1 > let obliterateMany = (...targets) => {
2 targets = targets.join(', ');
3 console.log(`${targets} are obliterated into tiny ashes`);
4 };
5 > obliterateMany('bunny', 'leprechaun', 'yeti');
6 // => Bunny, leprechaun, yeti are obliterated into tiny ashes
We will dive deeper into arrow functions later in the book and see how they not only provide a terser and more readable syntax but also serve a very important function in what regards to safekeeping the value of this in JavaScript. (We’ve got ourselves a very naughty this in JavaScript as you’ll soon appreciate yourself)
OOP and Objects in JavaScript
JavaScript has great support for object-oriented programming with objects literals, constructor functions, prototypical inheritance, ES6 classes and less orthodox OOP paradigms like mixins and stamps.
Objects in JavaScript are just key/value pairs. The simplest way to create an object is using an object literal:
1 > var scepterOfDestruction = {
2 description: 'Scepter of Destruction',
3 toString: function() {
4 return this.description;
5 },
6 destruct: function(target) {
7 console.log(`${target} is instantly disintegrated`);
8 }
9 }
10 > scepterOfDestruction.destruct('apple');
11 // => apple is instantly disintegrated
ES6 makes easier to create object literals with syntactic sugar for functions also known as method shorthand:
1 > var scepterOfDestruction = {
2 description: 'Scepter of Destruction',
3 toString() {
4 return this.description;
5 },
6 destruct(target) {
7 console.log(`${target} is instantly disintegrated`);
8 }
9 }
And for creating properties from existing variables also known as property shorthand:
1 > var damage = 10000;
2 > var scepterOfDestruction = {
3 description: 'Scepter of Destruction',
4 damage, // as opposed to damage: damage
5 toString() {
6 return this.description;
7 },
8 destruct(target) {
9 console.log(`${target} is instantly disintegrated`);
10 }
11 }
12 > scepterOfDestruction.damage;
13 // => 10000
This works great for one-off objects. When you want to reuse the same type of object more than once you can either use a vanilla factory method or a constructor function with the new keyword:
1 // by convention constructor functions are capitalized
2 > function Scepter(name, damage, spell){
3 this.description = `Scepter of ${name}`,
4 this.damage = damage;
5 this.castSpell = spell;
6 this.toString = () => this.description;
7 }
8 > var scepterOfFire = new Scepter('Fire', 100,
9 (target) => console.log(`${target} is burnt to cinders`));
10 > scepterOfFire.castSpell('grunt');
11 // => grunt is burnt to cinders
Prototypical Inheritance
Yet another big diffence between C# and JavaScript are their inheritance models. JavaScript exhibits what is known as prototypical inheritance. That means that objects inherit from other objects which therefore are called prototypes. These objects create what is known as a prototypical chain that is traversed when the JavaScript runtime tries to determine where in the chain a given method is defined.
Let’s say that you have an object that represents an abstraction for any item that can exist in your inventory:
1 > var item = {
2 durability: 100,
3 sizeInSlots: 1,
4 toString(){ return 'an undescriptive item';}
5 }
6 > item.toString();
7 // => an undescriptive item
And a two handed iron sword that in addition to being an item (and an awesome item at that) has its own specific set of traits:
1 > var ironTwoHandedSword = {
2 damage: 60,
3 sizeInSlots: 2,
4 wield() {
5 console.log('you wield your iron sword crazily over your head'\
6 );
7 },
8 material: 'iron',
9 toString() {return 'A rusty two handed iron sword';}
10 };
You can take advantage of JavaScript prototypical inheritance to reuse the item properties across many items, by setting the item object as the prototype5 of the ironTwoHandedSword (and any other specific items that you create afterwards).
1 > Object.setPrototypeOf(ironTwoHandedSword, item);
This will establish a prototypical chain, so that, if we attempt to retrieve the sword durability, the JavaScript runtime will traverse the chain and retrieve the property from the item prototype:
1 > ironTwoHandedSword.durability;
2 // => 100
If, on the other hand, you attempt to access a property that exists in both the prototype and the object itself, the nearest property in the chain will win:
1 > ironTwoHandedSword.toString();
2 // => A rusty two handed iron sword
ES6 exposes the __proto__ property that lets you directly assign a prototype through an object literal:
1 > var ironTwoHandedSword = {
2 __proto__: item,
3 damage: 60,
4 // etc...
5 };
6 > ironTwoHandedSword.prototype = item;
There’s a lot more to prototypical inheritance and the many different OOP paradigms supported by JavaScript. But we’ll look into them further later in the series.
ES6 Classes
A new addition to JavaScript you might have heard about and celebrated are ES6 classes. The existence of ES6 classes doesn’t mean that JavaScript gets classes just like C# and we’re not going to worry about constructor functions and prototypical inheritance anymore. ES6 classes are “just” syntactic sugar over the existing inheritance model and the way we craft objects in JavaScript. That being said, it is a great declarative way to represent constructor/prototype pairs.
A JavaScript class looks very similar to a C# class:
1 class Item {
2 constructor(durability = 100, sizeInSlots = 1){
3 this.durability = durability;
4 this.sizeInSlots = sizeInSlots;
5 }
6 toString(){
7 return 'an undescriptive item';
8 }
9 }
10 var item = new Item();
11 item.toString();
12 // => an undescriptive item
And so does inheritance:
1 class Sword extends Item {
2 constructor(durability = 500, sizeInSlots = 2,
3 damage = 50, material = 'iron'){
4 super(durability, sizeInSlots);
5 this.damage = damage;
6 this.material = material;
7 }
8 wield() {
9 console.log(`you wield your ${this.material} sword
10 crazily over your head`);
11 }
12 toString() {
13 return `A ${this.material} sword`;
14 }
15 };
16 var sword = new Sword();
17 sword.wield();
18 // => you wield your iron sword crazily over your head
Arrays, Maps and Sets in JavaScript
Up until recently JavaScript had only one single data structure, albeit very verstatile, to handle collections of items: the array. You can create an array using using square brackets []:
1 > [1, 2, 3, 4 ,5]
2 // => [1,2,3,4,5]
You can mix and match the different elements of an array. There’s no type restrictions so you can have numbers, strings, objects, functions, arrays, etc… in much the same way that you can find the most strange items in a kender’s 6 pouch:
1 > var aKendersPouch = [
2 'jewel',
3 '3 stones',
4 1,
5 {name: 'Orb of Power'},
6 function() { return 'trap!';}
7 ];
You can access the items of an array via their indexes:
1 > aKendersPouch[0]
2 // => jewel
3 > aKendersPouch[4]()
4 // => trap!
You can also traverse the indexes of an array using the for/in loop:
1 > for (var idx in aKendersPouch) console.log(aKendersPouch[idx]);
2 // => jewel
3 // => 3 stones
4 // => ...etc
5 // => function() { return 'trap!';}
And even better the items of an array using ES6 for/of loop:
1 > for (var item of aKendersPouch) console.log(item);
2 // => jewel
3 // => 3 stones
4 // => ...etc
5 // => function() { return 'trap!';}
Arrays have a lot of cool and useful methods that you can use to add/remove or otherwise operate on the items within the array:
1 > aKendersPouch.length
2 // => 5
3
4 // add item at the end of the array
5 > aKendersPouch.push('silver coin');
6 // => 6 (returns the current length of the array)
7 > aKendersPouch.push('6 copper coins', 'dental floss');
8 // => 8
9
10 // pop item at the end of the array
11 > aKendersPouch.pop();
12 // => dental floss
13
14 // insert item at the beginning
15 > aKendersPouch.unshift('The three Musketeers');
16 // => 8
17
18 // extract item from the beginning of the array
19 > aKendersPouch.shift();
20 // => 'The three musketeers'
And even LINQ-like methods to perform functional style transformations within an array:
1 > const isValuable = item => parseInt(item) > 5;
2 > const toGoldCoins = item => parseInt(item) || 0;
3 > const sumCoins = (sum, price) => sum + price;
4
5 > var goldCoins = aKendersPouch
6 .filter(isValuable) // ES6 analogous to LINQ Wh\
7 ere
8 .map(toGoldCoins) // analogous to LINQ Select
9 .reduce(sumCoins, 0); // analogous to LINQ Aggr\
10 egate
11 > console.log(goldCoins);
12 // => 6
You will learn a ton more about arrays later in the book.
ES6 Spread Operator and Arrays
The ES6 spread operator can also be used to merge or flatten an array within another array:
1 > var newRecruits = ['Sam', 'John', 'Connor'];
2 > var merryBandits = ['Veteran Joe', 'Brave Krom', ...newRecruits];
3 > merryBandits;
4 // => ["Veteran Joe", "Brave Krom", "Sam", "John", "Connor"]
ES6 Maps and Sets
ES6 gives us magicians two new data structures to work with: maps, a true key/value pair data structure and sets to handle collections of unique items.
You can create a new map using the Map constructor:
1 > var libraryOfWisdom = new Map();
2 > libraryOfWisdom.set('A',
3 ['A brief history of JavaScript-mancy', 'A Tale of Two Cities']);
4 > libraryOfWisdom.get('A')
5 //=> ['A brief history of JavaScript-mancy', 'A Tale of Two Cities']\
6 ;
You can even seed a map with existing information by sending an array of key/value pairs7:
1 > var libraryOfWisdom = new Map([
2 ['A', ['A brief history of JavaScript-mancy', 'A Tale of ...']],
3 ['B', ['Better Dead Than Powerless: Tome I of Nigromantics']]
4 ]);
5 > libraryOfWisdom.get('B');
6 // => ['Better Dead Than Powerless: Tome I of Nigromantics']
In a similar fashion, you create sets using the Set constructor:
1 > var powerElements = new Set(['earth', 'fire', 'water', 'wind']);
2 > powerElements
3 // => Set {"earth", "fire", "water", "wind"}
Sets will ensure that you don’t have duplicated data within a collection:
1 > powerElements.add('water').add('earth').add('iron');
2 > console.log(powerElements);
3 // => Set {"earth", "fire", "water", "wind", "iron"}
JavaScript Flow Control
JavaScript gives you the classic flow control structures that you are accustomed to: if, for, while loops behave much in the same way in JavaScript than in C# (but for the function scoped variables of course).
In addition to these, JavaScript has the for/in loop that lets you iterate over the properties of an object:
1 > var spellOfFarseeing =
2 { name: 'Spell of Farseeing',
3 manaCost: 10,
4 description: 'The spell lets you see a limited' +
5 'portion of a far away location;'}
6
7 > for (var prop in spellOfFarseeing) {
8 console.log(`${prop} : ${spellOfFarseeing[prop]}`);
9 }
10 // => name : Spell of Farseeing
11 // => manaCost : 10
12 // => description : The spell lets you see a limited
13 // portion of a far away location
And the ES6 for/of loop that lets you iterate over collections8 (arrays, maps and sets):
1 > for (var element of powerElements) console.log(element);
2 // => earth
3 // => fire
4 // => water
5 // => etc...
Logical Operators in JavaScript
Abstract Equality and Strict Equality
JavaScript equality operators behave in a particularly special way. The operators that you are accustomed to use in C# == and != are called abstract equality operators and evaluate the equality of expressions in a loose way. If the two expressions being evaluated by one of these operators don’t match in type, they’ll be converted to a matching type. For instance, in evaluating the abstract equality of 42 and "42", the string will be converted to a number resulting in both values being equal:
1 > 42 == '42'
2 ==> true
Fortunately JavaScript also provides operators that performs strict equality ( === and !==) which is basically a comparison of two values without the implicit type conversion.
1 > 42 === '42'
2 ==> false
Implicit Type Conversion Also Known As Type Coercion
This implicit conversion that takes place in JavaScript gives birth to the concept of truthy and falsey. Since any value can be evaluated as a boolean, we say that some values like an array [] or an object {} are truthy, and some other values like empty string '' or undefined are falsey. In the examples below we use the !! to explicitly convert values to boolean for clarity purposes:
1 > !![]
2 // => true
3 > !!{}
4 // => true
5 > !!""
6 // => false
7 > !!undefined
8 // => false
9 > !!null
10 // => false
11 > !!0
12 // => false
This allows for a terser way to write if statements
1 > if (troll) // as opposed to (troll != null && troll != undefined)
Since troll is coerced to a boolean type, having the troll variable holding an object value will evaluate to truthy and having it holding null or undefined will be falsey. In either case the if statement will fulfill its purpose while being much nicer to read.
OR and AND
OR (||) and AND (&&) operations also behave in an interesting way. The OR operation will return the first truthy expression or the last falsey expression (if all expressions are falsey):
1 // 0 falsey
2 // 'cucumber' truthy
3 // 42 truthy
4 > 0 || 'cucumber' || 42
5 // => 'cucumber'
6 > 0 || false || undefined
7 // => undefined
The AND operator will return the last truthy expression or the first falsey expression (if any falsey expression is encountered):
1 // 0 falsey
2 // 'cucumber' truthy
3 // 42 truthy
4 > 0 && 'cucumber' && 42
5 // => 0
6 > true && 'cucumber' && 42
7 // => 42
Exception Handling
Exception handling works similar as in C#, you have your familiar try/catch/finally blocks:
1 > try { asdf; }
2 catch (e) { console.log(e.message);}
3 finally { console.log('done!');}
4 // => asdf is not defined
5 // => done!
And you can throw new exceptions with the throw keyword:
1 > throw new Error("We're all gonna die!");
2 // => Uncaught Error: We're all gonna die!
Additionally, you can improve your error semantics and create custom errors by inheriting the Error prototype.
Regular Expressions
JavaScript also supports regular expressions. You can create a regular expression in two ways, either by wrapping the expression between slash (/):
1 > var matchNumbers = /\d+/;
2 > matchNumbers.test('40 gold coins');
3 // => true
4 > matchNumbers.exec('40 gold coints');
5 // => ["40"]
Or by creating a RegExp object:
1 > var matchItems = new RegExp('\\D+');
2 > matchItems.test('40 gold coins');
3 // => true
4 > matchItems.exec('40 gold coints');
5 // => [" gold coins"]
Strings have built-in support for regular expressions as well with the match and search methods:
1 > var bagOfGold = '30 gold coins';
2 > bagOfGold.match(/\d+/);
3 // => ['30']
4 > bagOfGold.search(/\d+/);
5 // => 0 (index where first match is found)
But Beware, JavaScript Can Be Weird and Dangerous
So far you’ve seen the bests parts of JavaScript and nothing too weird or inconsistent. But sometimes you’ll experience strange behaviors in some less visited corners of JavaScript like any of the following:
1 > x = 10;
2 // => added a variable to the global scope (window.x)
3 > NaN == NaN
4 // => false
5 > null == undefined
6 // => true
7 > typeof(null)
8 // => object
9 > [] + []
10 // => ''
11 > [] + {}
12 // => {}
13 > {} + []
14 // => 0
15 > {} + {}
16 // => NaN
Oftentimes you won’t run into these issues when building real web applications and my advice is that you ignore them. Be aware that they exist but just don’t use them, or use patterns or conventions to avoid them.
Concluding
And that was a summary of pretty much the whole JavaScript language. I really hope it has sparkled your interest for JavaScript and that you cannot wait to turn the next page and learn more. But first, let’s make a quick review of what you’ve learned in this chapter.
We’ve seen that JavaScript is a very flexible dynamic language that supports many paradigms of programming and has a lot of great features.
You have learned the many things you can do with strings and ES6 string templates. How functions are very independent entities that can live their own lives completely separate from objects and how they are a fundamental building block of applications in JavaScript. You also discovered arrow functions that resemble lambdas in C# and let you write super terse and beautiful code.
We took a look at objects, object initializers, prototypical inheritance and ES6 classes. We saw the different data structures supported, the versatility of the array and an overview of the new ES6 Map and Set.
We also examined more general language features like the flow control structures and logical operators. We saw the difference between abstract comparison and strict comparison, highlighted the implicit type conversion inherent to JavaScript, the existence of the concepts of truthy and falsey and the way the OR and AND operators work.
Finally we reviewed exception handling and regular expressions, and we saw some of the weird and best-avoided behaviors in JavaScript.
The Basics Of JavaScript Functions
functions,
functions everywhere
- Buzz Lightyearvascript
Explorer
The Basics Of Functions
Functions are the most fundamental building blocks of JavaScript applications. They are the safekeepers of the logic within our programs but also the primitives upon which we build programmatic constructs such as classes and modules.
JavaScript provides different ways to declare and use functions, each with their own nuances and limitations. So given the fact that they are such a fundamental part of the language it is important that you are aware of these characteristics when you are writing JavaScript.
Welcome to the first step in your journey to JavaScript mastery! Let’s get started!
Functions are the Key
Did you know that there are not one but two ways to write functions in JavaScript? Well, now you do. You can either write them as function expressions or function declarations. It will be very helpful for you to understand the difference and the implications of writing functions in either of these two styles because they work in a very different manner that will impact the readability of your code and how easy or hard it is to debug.
This is a function expression:
1 // anonymous function expression
2 var castFireballSpell = function(){
3 // chaos and mayhem
4 };
And this is a function declaration:
1 // function declaration
2 function castFireballSpell(){
3 // it's getting hot in here...
4 }
As you can appreciate, in their most common incarnations function expressions and function declarations look very similar. That’s why it is especially important to understand that they are actually very different and behave in disparate ways from each other.
Let’s examine each of them in greater detail.
Function Expressions
We use the function expression style whenever we declare a function like an expression, either by assigning it to a variable:
1 // an anonymous function expression
2 var castFireballSpell = function(){
3 console.log('...chaos and mayhem');
4 };
5 castFireballSpell();
6 // => ...chaos and mayhem
A property within an object:
1 // another anonymous function expression as a property of an object
2 var magician = {
3 castFireballSpell: function() {
4 console.log('muahaha! Eat this fireball!');
5 }
6 };
7 magician.castFireballSpell();
8 // => muahaha! Eat this fireball!
Or passing it as an argument to another function (like a lambda - note that I am using the term lambda in the sense of function as value):
1 // yet another anonymous function expression passed as an argument
2 var castCombo = function(spellOne, spellTwo){
3 console.log('Combo Magic!!!!');
4 spellOne();
5 spellTwo();
6 }
7
8 castCombo(function(){
9 console.log('FireBalllllzzz!!');
10 }, function(){
11 console.log('And Spectral Shield!!');
12 });
13
14 // => Combo Magic!!!!
15 // => FireBalllllzzz!!
16 // => And Spectral Shield!!
There are a couple of important considerations you need to take into account when using function expressions like the ones above: they are all anonymous functions - they don’t have a name - and if stored within a variable they are subjected to the same hoisting rules that apply to any other variable in JavaScript. But what is hoisting? And how a function being anonymous can affect our programs?.
JavaScript Arcana: Function Scope and Hoisting
An interesting quirk about JavaScript is that, unlike many other languages, variables in JavaScript have function scope (as opposed to block scope). That is, it is the functions themselves that create scopes for variables and not the blocks. This can be better illustrated by an example:
1 function openPandoraBox(){
2
3 if (true){
4 var treasure = 'mouse';
5 }
6 console.log('the pandora box holds a: **' + treasure + '**');
7 }
8 openPandoraBox();
9 // => the pandora box holds a: **mouse**
10 // WAT!? x is declared inside an if block.
11 // How can it be picked up by the console.log??
12 // one word: function-scope
What happens in this piece of code above is that the JavaScript runtime hoists (moves up) all variables within a function definition to the beginning of the function body. And does it even with variables declared within blocks of code such as if statements. This means that the function that you see above is equivalent to this one:
1 function openPandoraBox(){
2 // The variable definition is hoisted up here
3 var treasure = undefined;
4 if (true){
5 treasure = 'mouse';
6 }
7 console.log('the pandora box holds a: **' + treasure + '**');
8 // => the pandora box holds a: **mouse**
9 }
Where the scope of the treasure variable isn’t only the if statement but the whole function. In yet another example of hoisting the variable i that we declare inside the for loop is actually part of the entire function (Shocker!!):
1 function aMagicFunctionToIllustrateHoisting(){
2 // in reality
3 // var i = undefined
4
5 console.log('before i =' + i);
6 // => before i = undefined
7
8 for(var i = 0; i < 10; i++){
9 // jara jara
10 }
11
12 console.log('after i = ' + i);
13 // => after i = 10
14 }
This is the reason why JavaScript developers - seasoned JavaScriptmancers - usually write all variable declarations at the beginning of a function. They do it in order to be more explicit about what is really happening when a function is evaluated and to avoid unnecessary bugs.
If you take a look at jQuery for instance, a popular JavaScript open source library, you’ll be able to appreciate this technique everywhere:
1 /**
2 * Load a url into a page
3 */
4 jQuery.fn.load = function( url, params, callback ) {
5 var selector, type, response,
6 self = this,
7 off = url.indexOf(" ");
8
9 //... more codes here
10 }
Functions being the stewards of scope of an application is pretty interesting because, all of the sudden, a function is not only used for encapsulating and abstracting a piece of logic but also for structural reasons. That is, a way to organize and distribute bits and pieces of functionality and code. For instance, you’ll often see functions being declared inside other functions:
1 // declaring a function inside a function?
2 // Ain't that weird????
3 function blessMany(many){
4 many.forEach(bless);
5
6 function bless(target){
7 console.log('You bless ' + target + '. (+5 WillPower) ');
8 }
9 }
10
11 blessMany(['john snow', 'sansa stark']);
12 // => You bless John Snow (+5 Willpower)
13 // => You bless Sansa Stark (+5 Willpower)
This probably looks very weird if you are coming from C#. But up until the recent advent of ES6 modules this was the only way we had to group pieces of loosely related functionality.
JavaScript Arcana Resolved: Variables with Block Scope With ES6 let and const
Happily for you and happily for me ES6 comes with not one, but two ways to declare variables with block scope: the new let and const keywords. From this point forward I invite you to start using let and achieve a more C#-like style of writing code where you declare variables closer to where you use them.
And so, if we rewrite the example we used to illustrate function scope with let, we’ll obtain a very different yet more familiar result:
1 function openPandoraBoxWithBlockScope(){ // new scope for function b\
2 lock
3
4 if (true){ // new scope for if block
5 let treasure = 'mouse';
6 }
7 console.log('the pandora box holds a: **' + treasure + '**');
8 }
9 openPandoraBoxWithBlockScope();
10 // ReferenceError: treasure is not defined
11 // fiuuuu now everything makes sense again
Now the treasure variable only exists within the if statement block.
Alternatively, you can use the const keyword to declare constant variables with block scope.
1 function shallIPass(){ // new scope for youShallNotPass block
2 let youShallPass = 'you Shall Pass!',
3 youShallNotPass = 'You Shall Not Pass!';
4 // console.log(canIPass); // => ReferenceError
5
6 if (true){ // new scope for if block
7 const canIPass = youShallNotPass;
8 console.log(canIPass); // => 'You Shall Not Pass!'
9 canIPass = youShallPass;
10 // => TypeError: Assignment to a constant variable
11 }
12
13 console.log(canIPass); // => undefined
14 // ReferenceError: x is not defined
15 }
16 shallIPass();
17 // => you Shall not Pass!
18 // => TypeError: Assignment to a constant variable
Variables declared with the const keyword behave in a very similar way to C#. Attempting to make the constant refer to a different value will cause an exception.
However, it is important to understand that if the constant refers to an object, you can still modify its properties:
1 const fourHorsemen = ['conquest', 'war', 'famine', 'death'];
2 fourHorsemen.push('jaime');
3 console.log(`${fourHorsemen} waaat`);
4 // => ['conquest', 'wat', 'famine', 'death', 'jaime'] waaat
This means that const only affects the act of binding a value to a variable, and not the act of modifying that value itself. In order to make an object immutable you need to use Object.freeze but that’s knowledge best kept for another chapter about the beauty of objects. We’ll stick to functions for a little bit longer.
Another aspect of let and const that is interesting is that they do not hoist variables to the top of a block. Instead, if you attempt to use a variable before it has been defined you’ll get an exception (cheers for some sane behavior):
1 function openPandoraBoxWithBlockScopeAndHoisting(){
2 // new scope for function block
3
4 if (true){
5 // new scope for if block
6 console.log('the pandora box holds a: **' + treasure + '**');
7 // => ReferenceError: treasure is not defined;
8 let treasure = 'mouse';
9 }
10 }
11 openPandoraBoxWithBlockScopeAndHoisting();
12 // => ReferenceError: treasure is not defined;
Now that you’ve learnt some of the main characteristics of function expressions let’s take a look at the two types of function expressions that are available to you in JavaScript: anonymous function expressions and named function expressions.
Anonymous Function Expressions
Anonymous function expressions are particularly interesting because even though you read the following:
1 var castFireballSpell = function(){
2 console.log('...chaos and mayhem');
3 };
And you may be tempted to think that the name of the function is castFireballSpell, it is not!?! castFireballSpell is just a variable we use to store an anonymous function. You can appreciate this anonymity by inspecting the name property of the function itself:
1 var castFireballSpell = function(){
2 console.log('...chaos and mayhem');
3 };
4 console.log(castFireballSpell.name);
5 // => ""
6 // no name!
Luckily for us, as long as an anonymous function is bound to a variable, the developer tools in modern browsers will use that variable when displaying errors in call stacks (which is a savior when debugging):
1 // inspecting a call stack for an anonymous function bound
2 // to a variable
3 var castFireballSpellWithError = function(){
4 console.log('...chaos and mayhem');
5 try {
6 throw new Error();
7 } catch (e) {
8 console.log('stack: ', e.stack);
9 }
10 };
11 castFireballSpellWithError();
12 //=> stack: Error
13 // at castFireballSpellWithError (somefile:53:15)
14 // at window.onload (somefile:58:1)
Even if we use this function as a argument:
1 // If you use this function as a lambda the name is
2 // still shown in the call stack:
3 var spellLauncher = function(f){ f(); }
4 spellLauncher(castFireballSpellWithError);
5 // => stack: Error
6 // at castFireballSpellWithError (somefile:56:15)
7 // at spellLauncher (somefile:68:35)
8 // at window.onload (somefile:69:1)
However, an unbound anonymous function will show as completely anonymous within the call stack making it harder to debug when errors occur (I will refer to these functions as strict anonymous function from now on):
1 // strict anonymous function don't appear in the call stack
2 spellLauncher(function(){
3 console.log('...chaos and mayhem');
4 try {
5 throw new Error();
6 } catch (e) {
7 console.log('stack: ', e.stack);
8 }
9 });
10 //=> stack: Error
11 // at somefile:76:15
12 // at spellLauncher (somefile:68:35)
13 // at window.onload (somefile:73:1)
This lack of name will also affect the ability to use recursion because a function that doesn’t have a name cannot call itself from inside its body. In spite of that and just like in the previous examples, if we have the anonymous function bound to a variable we take a free pass and can take advantage of JavaScript lexical scope to access the function through that variable:
1 // you can use recursion when an anonymous function is bound to a va\
2 riable
3 var castManyFireballsSpell = function(n){
4 // this function encloses the castManyFireballsSpell variable
5 // and thus becomes a closure
6 console.log('... SHOOOOOOOOOSH ....');
7 if (n > 0)
8 castManyFireballsSpell(n-1);
9 };
10 castManyFireballsSpell(3);
11 // => ... SHOOOOOOOOOSH ....
12 // ... SHOOOOOOOOOSH ....
13 // ... SHOOOOOOOOOSH ....
14 // ... SHOOOOOOOOOSH ....
Notice that this is a pretty brittle way of using recursion. In this example we are using the variable castManyFireballsSpell to access the anonymous function from within itself. If, at some later point in time, you happen to set the variable to another function you’ll get into a pickle. A tricky situation with a very subtle bug where the original function will call this new function instead of itself (so no more recursion and weird unexpected stuff happening).
A strict anonymous function, on the other hand, has no way to refer to itself and thus you lose the ability to use recursion. For instance, this is the case when we define an anonymous function expression and we invoke it right away:
1 // but there's no way for an anonymous function
2 // to call itself in any other way
3 (function(n){
4 console.log('... SHOOOOOOOOOSH ....');
5 if (n > 0) {
6 // I cannot call myself... :(
7 }
8 }(5));
9 // => ... SHOOOOOOOOOSH ....
In summary, the fact that common function expressions are anonymous makes them harder to debug and complicates the use of recursion. And the fact that they are hoisted as variables, a trait common to all function expressions, also makes them less readable as we’ll see in a bit with a larger example program.
Let’s see some ways to ameliorate or lessen these issues.
Named Function expressions
You can solve the problem of anonymity that we’ve seen thus far by using named function expressions. You can declare named function expressions by adding a name after the function keyword:
1 // named function expression
2 var castFireballSpell = function castFireballSpell(){
3 // mayhem and chaos
4 };
5 console.log("this function's name is: ", castFireballSpell.name);
6 // => this function's name is castFireballSpell
The example above shows a variable and a function expression both named castFireballSpell. A named function expression always appears correctly represented in the call stacks even when used as an argument (and not bound to a variable):
1 // A named function expression always appears in the call stack
2 spellLauncher(function spreadChaosAndMayhem(){
3 console.log('...chaos and mayhem');
4 try {
5 throw new Error();
6 } catch (e) {
7 console.log('stack: ', e.stack);
8 }
9 });
10 // stack: Error
11 // at spreadChaosAndMayhem (somefile:134:15)
12 // at spellLauncher (somefile:68:35)
13 // at window.onload (somefile:131:1)
This helps while debugging and makes your code more readable since you can read the name of the function and understand what it’s meant to do without looking at its implementation.
An interesting fact about named function expressions is that you cannot call them by their name from the outside:
1 var castFireballSpell = function cucumber(){
2 // cucumber?
3 };
4 cucumber();
5 // => ReferenceError: cucumber is not defined
The name of a function expression is more of an internal identifier that can be used from inside the function body. This is very useful when working with recursion. For instance, if we declare a recursive named function expression and invoke it right away it just works:
1 // but you can call it from the function body
2 // which is helpful for recursion
3 (function fireballSpellWithRecursion(n){
4 console.log('... SHOOOOOOOOOSH ....');
5 if (n > 0) {
6 fireballSpellWithRecursion(n-1);
7 }
8 }(5));
9 // => ... SHOOOOOOOOOSH ....
10 // ... SHOOOOOOOOOSH ....
11 // ... SHOOOOOOOOOSH ....
12 // ... SHOOOOOOOOOSH ..... etc..
In summary, named function expressions improve on anonymous function expressions by increasing readability, improving the debugging process and allowing for a function to call itself.
Function Expressions are Hoisted as Variables
Function expressions are still problematic because they are hoisted like variables. But what does this mean exactly? It means that you can only use a function expression after you have declared it and therefore it forces you to write your code starting from the implementation details and continuing into higher levels of abstraction.
This is the opposite of what you want. Think about it. When you write a class as a C# developer, you start with the public API at the top of the class definition. Then you write the implementation of each method from top to bottom, from higher to lower levels of abstraction so that reading becomes natural. You open a file at the top, understand what it does at a glance by reading the intentional names that compose its public API and then you traverse the file down looking at the implementation details only when you need or want to.
Being forced to start from the opposite direction will have a negative impact in the readability of your code:
1 (function (magic){
2 // this function represents a module 'magic'
3 // it's just a way to group like-minded pieces of code
4
5 var oven = {
6 open: function(){},
7 placeBaking: function(){},
8 increaseTemperature: function(){},
9 claimVictory: function(){ return 'awesome cake';}
10 };
11
12 var mix = function mix(ingredients){
13 console.log('mixin ingredients:', ingredients.join(''));
14 return 'un-appetizing mixture';
15 }
16
17 var work = function work(mixture){
18 console.log('working ' + mixture);
19 return 'yet more un-appetizing dough';
20 };
21
22 var bake = function bake(dough){
23 oven.open();
24 oven.placeBaking(dough);
25 oven.increaseTemperature(200);
26 // insta-oven!
27 return oven.claimVictory();
28 };
29
30 var enchant = function enchant(ingredients){
31 var mixture = mix(ingredients),
32 dough = work(mixture),
33 cake = bake(dough);
34 return cake;
35 };
36
37 // This is the public API of this module
38 // and it's almost hidden down here
39 magic.enchant = enchant;
40
41 }(window.magic || (window.magic = {})));
42
43 var cake = magic.enchant(['flour', 'mandragora', 'dragon', 'chicken \
44 foot']);
45 // => mixin ingredients: flour mandragora dragon chicken foot
46 // => working un-appetizing mixture
47 console.log(cake);
48 // => awesome cake
If you try to reorder the different functions within the module so that they start from the public API and continue from top to bottom, from higher to lower levels of abstraction you’ll encounter many issues:
1 (function (magic){
2 // this function represents a module 'magic'
3 // it's just a way to group like-minded pieces of code
4
5 // exposing enchant as the API for the 'magic' module
6 magic.enchant = enchant;
7 // => hoisting issue, enchant is undefined at this point
8 // so we are just exposing an undefined variable thinking it is a \
9 function
10
11 // if uncommented this would cause an exception
12 // enchant();
13 // => TypeError: enchant is not a function
14 // => hoisting issue, enchant is undefined at this point
15
16 var enchant = function enchant(ingredients){
17 var mixture = mix(ingredients),
18 dough = work(mixture),
19 cake = bake(dough);
20 return cake;
21 };
22
23 // if uncommented this would cause an exception
24 // enchant();
25 // => TypeError: mix is not a function (it's undefined at this poi\
26 nt)
27 // hoisting issue, mix is undefined at this point
28
29 /* rest of the code...
30 var mix = function mix(ingredients){}
31 var work = function work(mixture){};
32 var bake = function bake(dough){};
33 var oven = {};
34 */
35
36 }(window.magic || (window.magic = {})));
37
38 try {
39 var cake = magic.enchant(['flour', 'mandragora', 'dragon', 'chicke\
40 n foot']);
41 console.log(cake);
42 } catch (e) {
43 console.warn('ups!!!!!!', e);
44 // => ups!!!!!! TypeError: magic.enchant is not a function
45 }
In this example we use function expressions to define every function. Because they are hoisted like variables when we try to assign the enchant function to our magic.enchant object its value is undefined. This results in us exposing an undefined value to the outside world instead of a helpful function to enchant delicious cakes. In a similar way, when we attempt to call the enchant function before either enchant or mix have been initialized we get a TypeError exception.
In summary, both named and anonymous function expressions are hoisted as variables. This affects their readability and can cause bugs when we try to run a function or expose it as part of the public API of a module before it has been defined. Although you could use let and const to prevent hoisting, there’s a better way you can declare your functions: Function declarations.
Function Declarations
Function declarations have some advantages over function expressions:
- They are named, and you can use that name from outside and from within the function.
- They are not hoisted as variables but as a whole, which makes them impervious to hoisting problems.
This is how you write a function declaration:
1 // function declaration
2 function castFireballSpell(){
3 // it's getting hot in here...
4 }
As you learned just a moment ago, function declarations are hoisted in a very special way. When the JavaScript runtime processes this piece of code:
1 var message = "hello";
2
3 // some more code here...
4
5 // function expression
6 var sayHi = function(){console.log('hi!')};
7
8 // some more code here...
9
10 // function declaration
11 function sayHello(){
12 console.log(msg);
13 }
It’s going to rearrange it by hoisting the whole sayHello function and only the declaration of the variables message and sayHi:
1 // hoisted as variables
2 var message, sayHi;
3
4 // hoisted as a whole
5 function sayHello(){
6 console.log(msg);
7 }
8
9 message = "hello";
10 // some more code here...
11 sayHi = function(){console.log('hi!')};
12 // some more code here...
Because of this special hoisting behavior, function declarations will enable you to write your JavaScript modules from higher to lower levels of abstraction just like we discussed earlier and as you can see in this example below:
1 // with function declarations you can write functions like this
2 // and don't worry about hoisting issues at all
3 (function(magic){
4
5 // public API of the magic module
6 magic.enchant = enchant;
7
8 // functions from higher to lower level of abstraction
9 function enchant(ingredients){
10 var mixture = mix(ingredients),
11 dough = work(mixture),
12 cake = bake(dough);
13 return cake;
14 }
15
16 // these are private functions to this module
17 function mix(ingredients){
18 console.log('mixin ingredients:', ingredients.join(''));
19 return 'un-appetizing mixture';
20 }
21
22 function work(mixture){
23 console.log('working ' + mixture);
24 return 'yet more un-appetizing dough';
25 }
26
27 function bake(dough){
28 oven.open();
29 oven.placeBaking(dough);
30 oven.increaseTemperature(200);
31 // insta-oven!
32 return oven.claimVictory();
33 }
34
35 var oven = {
36 open: function(){},
37 placeBaking: function(){},
38 increaseTemperature: function(){},
39 claimVictory: function(){ return 'awesome cake';}
40 };
41
42 }(window.magic || (window.magic = {})));
43
44 var cake = magic.enchant(['flour', 'mandragora', 'dragon', 'chicken \
45 foot']);
46 // => mixin ingredients: flour mandragora dragon chicken foot
47 // => working un-appetizing mixture
48 console.log(cake);
49 // => awesome cake
And, just like function expressions, you can also use function declarations as values (and arguments). Notice the disintegrate function below:
1 var orc = {toString: function(){return 'a mighty evil orc';}};
2 var warg = {toString: function(){return 'a terrible warg';}};
3 var things = [1, orc, warg, false];
4
5 // using the disintegrate function declaration as an argument
6 // nice and readable
7 things.forEach(disintegrate);
8
9 function disintegrate(thing){
10 console.log(thing + ' suddenly disappears into nothingness...');
11 }
12
13 // => 1 suddenly disappears into nothingness...
14 // a mighty evil orc suddenly disappears into nothingness...
15 // a terrible warg suddenly disappears into nothingness...
16 // false suddenly disappears into nothingness...
There’s something interesting happening in this example above that is worthy of note: type coercion. When the body of the disintegrate function is evaluated, the expression below:
1 thing + ' suddenly disappears into nothingness...'
Coerces the thing variable to a string type. In the case of the orc and warg which are objects that means calling the toString method and obtaining a string representation of either of these objects.
Concluding: Prefer Function Declarations and Named Function Expressions
Function expressions have some limitations:
- They are anonymous, which can make them less readable, harder to debug and use in recursion
- They are hoisted as variables which can lead to bugs and forces you to declare them before you can use them
Named function expressions solve the problem of anonymity. They make your vanilla function expressions more readable, easier to debug and enable recursion.
Function declarations solve both problems of anonymity and hoisting (they are hoisted as a whole), and even allow you to write code from higher to lower levels of abstraction.
You can use the let and const keywords to solve the problems with hoisting related to function expressions but you don’t get the nice top to bottom narrative that you get with function declarations. That is, with let and const you cannot use a function before you have declared it, if you attempt to do so you’ll get an exception.
In summary, and based on the characteristics of functions in JavaScript, prefer named function expressions and function declarations over anonymous function expressions.
Hope you have enjoyed this chapter about the different ways you can write functions in JavaScript. Up next, We will discuss the most common patterns to achieve default values, multiple arguments and function overloading when writing functions in JavaScript and we’ll see how some of the new features in ES6 provide native support for some of these.
Exercises
Useful Function Patterns: Default Arguments
Put yourself in the place of the listener,
of the eater,
of the reader,
of the user of your carefully crafted spells.
Think from the outside in,
and you'll be rewarded manyfold.
Think developer experience!!
- Llroc
Warrior Poet
Have You Heard Of Defaults?
I don’t know you but I’m always trying to write less code and build beautiful APIs for myself and other developers. One small way to achieve that is by using default arguments. Defaults let you add more intention behind your APIs and provide a shortcut to the most common functionality or task carried out by a function.
In this and the next few chapters we’ll discuss several patterns that you can use to improve the usability of the functions you write in JavaScript: defaults, multiple arguments and function overloading. Let’s get started with defaults in ES5 and ES6.
Using Default Arguments in JavaScript Today
If you have had the chance to take a look at some Javascript code, you may have encountered the following statement and wondered what (the heck) it meant:
1 var type = type || "GET";
Hold on tight because we are about to unveil the mystery. That statement right there has been the prevalent approach to writing defaults in JavaScript for ages. If you do a little of a mental exercise and try to remember what you read about the || (OR) operator in the introduction you’ll recall that this operator behaves in quite a special way: It returns the first truthy expression of those being evaluated or the last expression if none is truthy. As usual, this behavior is better illustrated through an example:
1 > false || 42 || false
2 // => 42
3
4 > "" || false
5 // => false
6
7 > "" || {}
8 // => Object {}
9
10 > var numberOfPotatos = undefined
11 > numberOfPotatos || 3
12 // => 3
13
14 > numberOfPotatos = 5
15 > numberOfPotatos || 3
16 // => 5
Since up until ES6 there was no native support for defaults and using the || operator was the most compact way to achieve it, this pattern soon became the de facto standard for defaults throughout the community and is often used in JavaScript applications. You just need to take a sneak peak in any popular open source library to find it used in innumerable situations:
1 // from jquery loads.js
2 // (https://github.com/jquery/jquery/blob/master/src/ajax/load.js)
3 type: type || "GET",
4
5 // or from yeoman router.js
6 // (https://github.com/yeoman/yo/blob/master/lib/router.js)
7 this.conf = conf || new Configstore(pkg.name, {
8 generatorRunCount: {}
9 });
One of the most common use of defaults happens when evaluating the arguments of a function like in this castIceCone spell:
1 function castIceCone(mana, options){
2 // we take advantage of the || operator to define defaults
3 var mana = mana || 5,
4 options = options || {direction: 'forward', damageX: 10},
5 direction = options.direction || 'forward',
6 damageX = options.damage || 10,
7 damage = 2*mana + damageX;
8
9 console.log("You spend " + mana +
10 " mana and cast a frozen ice cone " + direction +
11 " (" + damage + " DMG).");
12 }
The castIceCone function has two arguments: a mana argument that represents the amount of mana a powerful wizard is going to spend in casting the ice cone and an additional options object with finer details.
The function makes extensive use of the || operator to provide defaults for all possible cases. In the simplest and most convenient of scenarios the user of this function would just call it directly, and when more finesse is needed she or he could populate the richer options argument:
1 castIceCone();
2 // => You spend 5 mana and cast a frozen ice cone forward (20 DMG)
3 castIceCone(10);
4 // => You spend 5 mana and cast a frozen ice cone forward (20 DMG)
5 castIceCone(10, { damage: 200});
6 // => You spend 5 mana and cast a frozen ice cone forward (220 DMG)
7 castIceCone(10, { direction: 'to Mordor'})
8 // => You spend 10 mana and cast a frozen ice cone to Mordor (30 DMG)
9 castIceCone(10, { direction: 'to Mordor', damage: 200})
10 // => You spend 10 mana and cast a frozen ice cone to Mordor (220 DM\
11 G)
An alternative way to do defaults is to wrap them within an object.
Defaults with Objects
In addition to relying on the || operator, you can use an object to gather your default values. Whenever the function is called you merge the arguments provided by the user with the object that represents the defaults. The default values will only be applied when the actual arguments are missing.
You can define a mergeDefaults function to perform the merge operation:
1 function mergeDefaults(args, defaults){
2 if (!args) args = {};
3 for (var prop in defaults) {
4 if (defaults.hasOwnProperty(prop) && !args[prop]) {
5 args[prop] = defaults[prop];
6 }
7 }
8 return args;
9 }
And then apply it to the arguments passed to the function like in this castLightningBolt spell:
1 function castLightningBolt(details){
2 // we define the defaults as an object
3 var defaults = {
4 mana: 5,
5 direction: 'forward'
6 };
7 // merge details and defaults
8 // properties are overwritten from right to left
9 details = mergeDefaults(details, defaults);
10
11 console.log('You spend ' + details.mana +
12 ' and cast a powerful lightning bolt ' +
13 details.direction + '!!!');
14 }
Which provides a similar defaults developer experience to that of the first example:
1 castLightningBolt();
2 // => You spend 5 and cast a powerful lightning bolt forward!!!
3 castLightningBolt({mana: 100});
4 // => You spend 100 and cast a powerful lightning bolt forward!!!
5 castLightningBolt({direction: 'to the right'});
6 // => You spend 5 and cast a powerful lightning bolt to the right!!!
7 castLightningBolt({mana: 10, direction: 'to Mordor'});
8 // => You spend 10 and cast a powerful lightning bolt to Mordor!!!
More often though you will probably rely on a popular open source library. Libraries like jQuery, underscore or lodash usually come with a lot of utility functions that you can use for this and many other purposes. For instance, jQuery comes with the $.extend function and underscore comes with both the _.defaults and _.extend functions that could help you in this scenario.
Let’s update the previous example with code from these two libraries:
1 function castLightningBoltOSS(details){
2 // we define the defaults as an object
3 var defaults = {
4 mana: 5,
5 direction: 'in front of you'
6 };
7 // extend details with defaults
8 // properties are overwritten from right to left
9 // jQuery:
10 details = $.extend({}, defaults, details);
11 // underscore:
12 //details = _.extend({}, defaults, details);
13
14 // to use defaults switch argument places
15 // properties are only overwritten if they are undefined
16 // underscore:
17 //details = _.defaults({},details, defaults);
18
19 console.log('You spend ' + details.mana +
20 ' and cast a powerful lightning bolt ' +
21 details.direction + '!!!');
22 }
If you have kept an eye on ES6 you may know that it comes with a native version of the jQuery $.extend method called Object.assign. Indeed, you can update the previous example as follows and achieve the same result:
1 //details = $.extend({}, defaults, details);
2 details = Object.assign({}, defaults, details);
However, if you are planning to use ES6, there’s an even better way to use defaults.
Native Default Arguments with ECMAScript 6
ES6 makes it dead easy to declare default arguments. Just like in C# you use the equal sign "=" and assign a default value beside the argument itself:
1 function castIceCone(mana=5){
2 console.log(`You spend ${mana} mana and casts a terrible ice cone`\
3 );
4 }
5
6 castIceCone();
7 // => You spend 5 mana and casts a terrible ice cone
8 castIceCone(10);
9 // => You spend 10 mana and casts a terrible ice cone
JavaScript takes defaults even further because they are not limited to constant expressions like C# optional arguments. In JavaScript, any expression is a valid default argument.
For instance, you can use entire objects as defaults:
1 function castIceCone(mana=5, options={direction:'forward'}){
2 console.log(`You spend ${mana} mana and casts a ` +
3 `terrible ice cone ${options.direction}`);
4 }
5
6 castIceCone();
7 // => You spend 5 mana and casts a terrible ice cone forward
8 castIceCone(10);
9 // => You spend 10 mana and casts a terrible ice cone forward
10 castIceCone(10, {direction: 'to Mordor'});
11 // => You spend 10 mana and casts a terrible ice cone to Mordor
12 castIceCone(10, {duck: 'cuack'});
13 // => You spend 10 mana and casts a terrible ice cone undefined
If you take a closer look at the end of the example above, you’ll realize that we have a small bug in our function. We are setting a default for the entire options object but not for parts of it. So if the developer provides an object with the direction property missing, we will get a strange result (writing undefined to the console).
We can solve this problem by taking advantage of the new destructuring syntax which allows you to assign argument properties directly to variables within a function, and at the same time provide defaults to parts of an object:
1 function castIceConeWithDestructuring(mana=5,
2 {direction='forward'}={direction:'forward'}){
3
4 console.log(`You spend ${mana} mana and casts a ` +
5 `terrible ice cone ${direction}`);
6 }
7 castIceConeWithDestructuring();
8 // => You spend 5 mana and casts a terrible ice cone forward
9 castIceCone(10, {direction: 'to Mordor'});
10 // => You spend 10 mana and casts a terrible ice cone to Mordor
11 castIceConeWithDestructuring(10, {duck: 'cuack'});
12 // => You spend 10 mana and casts a terrible ice cone forward
In this example we use argument destructuring {direction='forward'} to:
-
Extract the property
directionfrom the argument provided to the function at once. This allows us to writedirectioninstead of the more verboseoptions.directionthat we used in previous examples. -
Provide a default value for the
directionproperty in the case that the function is called with a options object that misses that property. It therefore solves the problem with the{duck: 'cuack'}example.
Finally, taking the freedom of defaults to the extreme, you are not limited to arbitrary objects either, you can even use a function expression as a default (I expect your mind has just been blown by this, this… very… second):
1 function castSpell(spell=function(){console.log('holy shit a callbac\
2 k!');}){
3 spell();
4 }
5
6 castSpell();
7 // => holy s* a callback!
8 castSpell(function(){
9 console.log("balefire!!!! You've been wiped out of existence");
10 });
11 // => balefire!!!! You've been wiped out of existence
Concluding
Yey! In this chapter you’ve learned several ways in which you can use defaults in JavaScript whether you are using ES5 or ES6 and beyond. Taking advantage of defaults will let you write less code and provide a slightly better user experience to the consumers of the functions that you write.
Up next, more function patterns with multiple arguments and the rest operator!
Exercises
More Useful Function Patterns: Arbitrary Arguments
What works for one,
may work for many...
- Eccason,
Maester of the Aethis
An Arbitrary Number of Arguments
Sometimes you write a function that performs some sort of action based on a single argument. Some time later you realize that it would be nice if you could use that same function, but this time, with an arbitrary number of arguments. You may be tempted to change the interface of the original function to take an array instead of a single item. In doing so, however, you break the code that is using the function and force any future developers to wrap the arguments being sent inside an array, even when there’s only a single argument.
In this specific scenario, you may want to do something different. You may want to keep the function interface as it is and elegantly extend it to support multiple arguments without affecting any existing code. That’s what we do in C# with the params keyword, and what you can achieve in JavaScript via the arguments object or ES6 rest parameter syntax.
This is what you’ll learn in this chapter. But first, did you know that JavaScript functions are pretty peculiar about their arguments?
The Craziness Of Function Arguments in JavaScript
JavaScript gives you a lot of freedom when it comes to handling function parameters and arguments. For instance, you can declare a function with a specific number of parameters and, if you wish, call it without any arguments at all.
Imagine a function heal that casts a healing spell on a person:
1 function heal(person, inchantation, modifier){
2 var healedHp;
3 modifier = modifier || 1;
4 healedHp = modifier * 100;
5
6 console.log('you heal ' + person + ' with ' + inchantation +
7 ' spell (+' + healedHp + 'hp)' );
8 person.hp += healedHp;
9 }
This function has an arity of 3, that is, it expects 3 arguments. You can verify this accessing its length property. (A popular interview question by the way):
1 console.log(heal.length);
2 // => 3
Thanks to the magic of JavaScript’s infinite freedom you can call it without any arguments at all. Although this doesn’t mean that it will work:
1 try {
2 heal();
3 } catch (e) {
4 console.log(e)
5 }
6 // => you heal undefined with undefined spell (+100hp)
7 // => TypeError: cannot read property hp of undefined
You can also call it with as many of those 3 arguments as you want:
1 var JonSnow = {name: 'Jon Snow', hp: 5,
2 toString: function(){return this.name;}};
3
4 heal(JonSnow);
5 // => you heal Jon Snow with undefined spell (+100hp)
6
7 heal(JonSnow, 'cure');
8 // => you heal Jon Snow with cure spell (+100hp)
9
10 heal(JonSnow, 'super heal', /* modifier */ 2);
11 // => you heal Jon Snow with super heal spell (+200hp)
Or even with more arguments than the parameters defined in the function itself:
1 heal(JonSnow, 'heal', 1, 3, 2, 3, 'cucumber',
2 {a: 1, b:2}, 'many arguments');
3 // => you heal Jon Snow with heal spell (+100hp)
It is up to you to implement the body of a function and handle each case as you see fit. But what happens when you want to write a function that takes an arbitrary number of arguments?
Functions with Arbitrary Arguments Right Now!
In C#, whenever we want to write such a function we use the params keyword. In JavaScript (up to ES5), on the other hand, there’s no keyword nor operator that allows us to do that.
The only possible approach is to use the arguments object. arguments, like this, is a special kind of object present within every function in JavaScript. Its whole purpose is to give access to the arguments used to call a function from inside the function itself.
Think about an obliterate spell that you could use to completely wipe out an enemy:
1 function obliterate(enemy){
2 console.log(enemy + " wiped out of the face of the earth");
3 }
4
5 obliterate('batman');
6 // => batman wiped out of the face of the earth
But why stop at one? With arguments you can make an obliterate spell to wipe out all your enemies at once!
1 function obliterate(){
2 // Unfortunately arguments is not an array :O
3 // so we need to convert it ourselves
4 var victims = Array.prototype.slice.call(arguments, /* start */ 0);
5
6 victims.forEach(function(victim){
7 console.log(victim + " wiped off of the face of the earth");
8 });
9 console.log('*Everything* has been obliterated, ' +
10 'oh great master of evil and deceit!');
11 }
Indeed, we can use our obliterate function with one or as many arguments as we want. We have extended our function API without breaking it:
1 obliterate('batman');
2 // => batman wiped out of the face of the earth
3 // *Everything* has been obliterated, oh great master
4 // of evil and deceit!
5
6 obliterate("John Doe", getPuppy(), 1, new Minion('Willy', 'troll'));
7 /*
8 John Doe wiped off of the face of the earth
9 cute puppy wiped off of the face of the earth
10 1 wiped off of the face of the earth
11 Willy the troll wiped off of the face of the earth
12 *Everything* has been obliterated, oh great master
13 of evil and deceit!
14 */
15
16 function getPuppy(){
17 return {
18 cuteWoof: function(){console.log('wiii');},
19 toString: function(){return 'cute puppy';}
20 };
21 };
22
23 function Minion(name, type){
24 this.name = name;
25 this.type = type;
26 this.toString = function(){ return name + " the " + type;};
27 }
As it goes, there are a couple of interesting things to point out in this example.
The first one is the fact that we are sending arguments of different types to the function and they are all treated seamlessly. That’s an example of duck typing in action where an object is defined by what it can do and not by its type. As long as the values that we pass as arguments support the interface the function expects - in this case the toString method - everything will work just fine.
The second thing to point out is the use of the Array.prototype.slice function. We use it to convert the arguments variable into a real array victims. While you would expect the arguments variable to be an array, it is not, it is an array-like object (and this, my friend, is another of JavaScript quirks right here).
Array-Like Objects
Here are the things you can do with an array-like object:
1 function inspectArguments(){
2
3 // you can index it:
4 console.log("the first argument is ", arguments[0]);
5 console.log("the second argument is ", arguments[1]);
6
7 // you an enumerate it:
8 // as in the arguments are enumerable like
9 // any common array or object, and thus you can use
10 // the for/in loop
11 for (var idx in arguments) {
12 console.log("item at position " + idx +
13 " is " + arguments[idx]);
14 }
15
16 // it has a length property
17 console.log("there are " + arguments.length + " arguments");
18 }
19
20 inspectArguments("cucumber", "dried meat", "dagger", "rock");
21 // => the first argument is cucumber
22 // => the second argument is dried meat
23 // => item at position 1 is cucumber
24 // => etc...
25 // => there are 4 arguments
Because it is not an array it does not have any of the array functions that you would expect:
1 function inspectArgumentsFunctions(){
2 console.log("arguments.foreach is ", arguments.foreach);
3 console.log("arguments.map is ", arguments.map);
4 console.log("arguments.some is", arguments.some);
5 }
6
7 inspectArgumentsFunctions();
8 // => arguments.foreach is undefined
9 // => arguments.map is undefined
10 // => arguments.some is undefined
Using the slice function of Array.prototype allows you to convert arguments to an array and take advantage of all the nice array functions. This is what we did with the obliterate function:
1 function obliterate(){
2 //...
3 var victims = Array.prototype.slice.call(arguments, /* start */ 0);
4 victims.forEach(function(victim){
5 console.log(victim + " wiped out of the face of the earth");
6 });
7 //...
8 }
Alternatively, you can use the ES6 Array.from method for a more natural conversion of an array-like object into an array:
1 function obliterate(){
2 //...
3 var victims = Array.from(arguments);
4 victims.forEach(function(victim){
5 console.log(victim + " wiped out of the face of the earth");
6 });
7 //...
8 }
In the specific case of the arguments object ES6 rest syntax offers a much better alternative9 as you’ll find out soon.
Native Arbitrary Arguments with ES6 Rest Parameters
ES6 comes with a native way to handle arbitrary arguments: rest parameters.
All the complexity of using the arguments object in the previous examples can be substitued by rest parameters and handled seamlessly:
1 function obliterate(...victims){
2 victims.forEach(function(victim){
3 console.log(victim + " wiped out of the face of the earth");
4 });
5 console.log('*Everything* has been obliterated, ' +
6 'oh great master of evil and deceit!');
7 }
When using rest parameters, victims becomes an array automatically so there’s no need to perform additional conversions. Indeed, if we use the new obliterate function as we did before it works perfectly:
1 obliterate('batman');
2 // => batman wiped out of the face of the earth
3 // *Everything* has been obliterated, oh great master
4 // of evil and deceit!
5
6 obliterate("John Doe", getPuppy(), 1, new Minion('Willy', 'troll'));
7 /*
8 John Doe wiped off of the face of the earth
9 cute puppy wiped off of the face of the earth
10 1 wiped off of the face of the earth
11 Willy the troll wiped off of the face of the earth
12 *Everything* has been obliterated, oh great master of evil and decei\
13 t!
14 */
Rest paratemers can also be used in combination with normal parameters. For instance, imagine that the obliterate spell had an extra effect on the first enemy it encountered. We could rewrite it like this:
1 function obliterate(unfortunateVictim, ...victims){
2 console.log(unfortunateVictim +
3 " wiped out of the face of EXISTENCE " +
4 "as if it had never existed.... Woooo");
5 victims.forEach(function(victim){
6 console.log(victim + " wiped out of the face of the earth");
7 });
8 console.log('*Everything* has been obliterated, ' +
9 'oh great master of evil and deceit!');
10 }
Upon using this malignant spell the first enemy would be removed from existence completely and utterly:
1 obliterate("John Doe", getPuppy(), 1, new Minion('Willy', 'troll'));
2 /*
3 John Doe wiped out of the face of EXISTENCE as if it had
4 never existed.... Woooo
5 cute puppy wiped off of the face of the earth
6 etc...
7 */
Note how easily the rest parameters capture all the remaining arguments after unfortunateVictim. Beautiful!
You now know different ways to implement a function that takes an arbitrary number of arguments. A function that provides the same API regardless of being called with one or many arguments. But what if you just happen to have an array? What happens if the output of another function is an array of enemies that need to be obliterated? Well, that’s when the ES6 spread operator comes in handy.
The ES6 spread operator, among other things that you’ll learn later in this book, lets you seamlessly call these type of functions using an array as argument:
1 let mortalEnemies = ["John Doe", getPuppy(), 1,
2 new Minion('Willy', 'troll')];
3
4 obliterate(...mortalEnemies);
5 /*
6 John Doe wiped out of the face of EXISTENCE as if
7 it had never existed.... Woooo
8 cute puppy wiped off of the face of the earth
9 etc...
10 */
Note how the spread operator ...mortalEnemies is super similar to rest parameters syntax but performs the opposite operation, instead of gathering arguments into an array, it spreads an array into arguments.
Concluding
Congratulations! You have cleared another obstacle in the path to writing functions with beautiful and thoughtful APIs.
Function parameters and arguments are yet another example of the flexibility and freedom that JavaScript offers. In JavaScript you can call a function with as many argument as you want regardless of the signature of the function itself.
In some ocassions you’ll need to design a function that takes an arbitrary number of arguments. If you are using ES5, you can take advantage of the arguments object. The arguments object is present in every function and gives you access to the arguments with which a function was called. Unfortunately, it is an array-like object and you may need to convert it to an array before you can use it. If you are using ES6 or above, rest parameters offer a great developer experience similar to C# params. You can combine rest parameters with normal parameters and even call one of these special functions with an array using the spread operator.
In the next chapter you’ll find out how to override functions (and methods) in JavaScript.
Exercises
More Useful Function Patterns: Function Overloading
One same API,
to provide similar function,
that's a smart thing,
memorable, familiar, consistent
- Siwelluap
Chieftain of the twisted fangs
Have you Heard About The Marvels Of Overloading?
In the last couple of chapters we learned some useful patterns with functions in JavaScript that helped us achieve defaults and handling arbitrary arguments. We also saw a common thread: The fact that ES6 comes with a lot of new features that make up for past limitations of the language. Features like native defaults and rest parameters that let you solve these old problems in a more concise style.
This chapter will close this section - useful function patterns - with some tips on how you can achieve function overloading in JavaScript.
Function overloading helps you reuse a piece of functionality and provide a unified API in those situations when you have slightly different arguments yet you want to achieve the same thing. Unfortunately, there’s a problem with function overloading in JavaScript.
The Problem with Function Overloading in JavaScript
There’s a slight issue when you attempt to do function overloading in JavaScript like you would in C#. You can’t do it.
Indeed, one does not simply overload functions in JavaScript willy nilly. Imagine a spell to raise a skeleton army:
1 function raiseSkeleton(){
2 console.log('You raise a skeleton!!!');
3 }
And now imagine that you want to overload it to accept an argument mana that will affect how many skeletons can be raised from the dead at once:
1 function raiseSkeleton(mana){
2 console.log('You raise ' + mana + ' skeletons!!!');
3 }
If you now try to execute the raiseSkeleton function with no arguments you would probably expect the first version of the function to be called (just like it would happen in C#). However, what you’ll discover, to your dismay, is that raiseSkeleton has been completely overwritten:
1 raiseSkeleton();
2 // => You raise undefined skeletons!!!
In JavaScript, you cannot override a function by defining a new one with the same name and a different signature. If you try to do so, you’ll just succeed in overwriting your original function with a new implementation.
How Do We Do Function Overloading Then?
Well, as with many things in JavaScript, you’ll need to take advantage of the flexibility and freedom the language gives you to emulate function overloading yourself. In the upcoming sections you’ll learn four different ways in which you can achieve it, each with their own strengths and caveats:
- Inspecting arguments
- Using an options object
- Relying on ES6 defaults
- Taking advantage of polymorphic functions
Function Overloading by Inspecting Arguments
One common pattern for achieving function overloading is to use the arguments object to inspect the arguments that are passed into a function:
1 function raiseSkeletonWithArgumentInspecting(){
2 if (typeof arguments[0] === "number"){
3 raiseSkeletonsInNumber(arguments[0]);
4 } else if (typeof arguments[0] === "string") {
5 raiseSkeletonCreature(arguments[0]);
6 } else {
7 console.log('raise a skeleton');
8 }
9
10 function raiseSkeletonsInNumber(n){
11 console.log('raise ' + n + ' skeletons');
12 }
13 function raiseSkeletonCreature(creature){
14 console.log('raise a skeleton ' + creature);
15 };
16 }
Following this pattern you inspect each argument being passed to the overloaded function(or even the number of arguments) and determine which internal implementation to execute:
1 raiseSkeletonWithArgumentInspecting();
2 // => raise a skeleton
3 raiseSkeletonWithArgumentInspecting(4);
4 // => raise 4 skeletons
5 raiseSkeletonWithArgumentInspecting('king');
6 // => raise skeleton king
This approach can become unwieldy very quickly. As the overloaded functions and their parameters increase in number, the function becomes harder and harder to read, maintain and extend.
At this point you may be thinking: ”…checking the type of the arguments being passed? seriously?!” and I agree with you, that’s why I like to use this next approach instead.
Using an Options Object
A better way to achieve function overloading is to use an options object. This object acts as a container for the different parameters a function can consume:
1 function raiseSkeletonWithOptions(spellOptions){
2 spellOptions = spellOptions || {};
3 var armySize = spellOptions.armySize || 1,
4 creatureType = spellOptions.creatureType || '';
5
6 if (creatureType){
7 console.log('raise a skeleton ' + creatureType);
8 } else {
9 console.log('raise ' + armySize + ' skeletons ' + creatureType);
10 }
11 }
This allows you to call a function with different arguments:
1 raiseSkeletonWithOptions();
2 // => raise a skeleton
3 raiseSkeletonWithOptions({armySize: 4});
4 // => raise 4 skeletons
5 raiseSkeletonWithOptions({creatureType:'king'});
6 // => raise skeleton king
It is not strictly function overloading but it provides the same benefits: It gives you different possibilities in the form of a unified API, and additionally, named arguments and easy extensibility. That is, you can add new options without breaking any existing clients of the function.
Here is an example of both argument inspecting and the options object patterns in the wild, the jQuery ajax function:
1 ajax: function( url, options ) {
2 // If url is an object, simulate pre-1.5 signature
3 if ( typeof url === "object" ) {
4 options = url;
5 url = undefined;
6 }
7
8 // Force options to be an object
9 options = options || {};
10
11 var transport,
12 // URL without anti-cache param
13 cacheURL,
14 // Response headers
15 responseHeadersString,
16 responseHeaders,
17 // timeout handle
18 timeoutTimer,
19 // etc...
20 }
Relying on ES6 Defaults
Although ES6 doesn’t come with classic function overloading, it brings us default arguments which give you better support for function overloading than what we’ve had so far.
If you reflect about it, default arguments are a specialized version of function overloading. A subset of it, if you will, for those cases in which you can use an increasing number of predefined arguments:
1 function castIceCone(mana=5, {direction='in front of you'}={}){
2 console.log(`You spend ${mana} mana and casts a ` +
3 `terrible ice cone ${direction}`);
4 }
5 castIceCone();
6 // => You spend 5 mana and casts a terrible ice cone in front of you
7 castIceCone(10, {direction: 'towards Mordor'});
8 // => You spend 10 mana and casts a terrible ice cone towards Mordor
Taking Advantage of Polymorphic Functions
Yet another interesting pattern for achieving function overloading is to rely on JavaScript great support for functional programming. In the world of functional programming there is the concept of polymorphic functions, that is, functions which exhibit different behaviors based on their arguments.
Let’s illustrate them with an example. Our starting point will be this function that we saw in the inspecting arguments section:
1 function raiseSkeletonWithArgumentInspecting(){
2 if (typeof arguments[0] === "number"){
3 raiseSkeletonsInNumber(arguments[0]);
4 } else if (typeof arguments[0] === "string") {
5 raiseSkeletonCreature(arguments[0]);
6 } else {
7 console.log('raise a skeleton');
8 }
9
10 function raiseSkeletonsInNumber(n){
11 console.log('raise ' + n + ' skeletons');
12 }
13 function raiseSkeletonCreature(creature){
14 console.log('raise a skeleton ' + creature);
15 };
16 }
We will take it and decompose it into smaller functions:
1 function raiseSkeletons(number){
2 if (Number.isInteger(number)){ return `raise ${number} skeletons`;}
3 }
4
5 function raiseSkeletonCreature(creature){
6 if (creature) {return `raise a skeleton ${creature}`;}
7 }
8
9 function raiseSingleSkeleton(){
10 return 'raise a skeleton';
11 }
And now we create an abstraction (functional programming likes abstraction) for a function that executes several other functions in sequence until one returns a valid result. Where a valid result will be any value different from undefined:
1 // This is a higher-order function that returns a new function.
2 // Something like a function factory.
3 // We could reuse it to our heart's content.
4 function dispatch(...fns){
5
6 return function(...args){
7 for(let f of fns){
8 let result = f.apply(null, args);
9 if (exists(result)) return result;
10 }
11 };
12 }
13
14 function exists(value){
15 return value !== undefined
16 }
dispatch lets us create a new function that is a combination of all the previous ones: raiseSkeletons, raiseSkeletonCreature and raiseSingleSkeleton:
1 let raiseSkeletonFunctionally = dispatch(
2 raiseSkeletons,
3 raiseSkeletonCreature,
4 raiseSingleSkeleton);
This new function will behave in different ways based on the arguments it takes. It will delegate any call to each specific raise skeleton function until a suitable result is obtained.
1 console.log(raiseSkeletonFunctionally());
2 // => raise a skeleton
3 console.log(raiseSkeletonFunctionally(4));
4 // => raise 4 skeletons
5 console.log(raiseSkeletonFunctionally('king'));
6 // => raise skeleton king
Note how the last raiseSingleSkeleton is a catch-all function. It will always return a valid result regardless of the arguments being sent to the function. This will ensure that however you call raiseSkeletonFunctionally you’ll always have a default implementation or valid result.
A super duper mega cool thing that you may or may not have noticed is the awesome degree of composability of this approach. If we want to extend this function later on, we can do it without modifying the original function. Take a look at this:
1 function raiseOnSteroids({number=0, type='skeleton'}={}){
2 if(number) {
3 return `raise ${number} ${type}s`;
4 }
5 }
6
7 let raiseAdvanced = dispatch(raiseOnSteroids,
8 raiseSkeletonFunctionally);
We now have a raiseAdvanced function that augments raiseSkeletonFunctionally with the new desired functionality represented by raiseOnSteroids:
1 console.log(raiseAdvanced());
2 // => raise a skeleton
3 console.log(raiseAdvanced(4));
4 // => raise 4 skeletons
5 console.log(raiseAdvanced('king'));
6 // => raise skeleton king
7 console.log(raiseAdvanced({number: 10, type: 'ghoul'}))
8 // => raise 10 ghouls
This is the OCP (Open-Closed Principle)10 in all its glory like you’ve never seen it before. Functional programming is pretty awesome right? We will take a deeper dive into functional programming within the sacred tome of FP later in the series and you’ll get the chance to experiment a lot more with both higher-order functions and function composition alike.
Concluding
Although JavaScript doesn’t support function overloading you can achieve the same behavior by using different patterns: inspecting arguments, using an options object, relying on ES6 defaults or taking advantage of polymorphic functions.
You can use the arguments object and inspect the arguments that are being passed to a function at runtime. You should only use this solution with the simplest of implementations as it becomes unwieldly and hard to maintain as parameters and overloads are added to a function.
Or you can use an options object as a wrapper for parameters. This is both more readable and maintanaible than inspecting arguments, and provides two additional benefits: named arguments and a lot of flexibility to extend the function with new parameters.
ES6 brings improved support for function overloading in some situations with native default arguments.
Finally, you can take advantage of functional programming, compose your functions from smaller ones and use a dispatching mechanism to select which function is used based on the arguments.
Exercises
On the Art of Summoning Servants and Critters, Or Understanding The Basics of JavaScript Objects
Things are ideas,
ideas are abstractions,
abstractions are objects,
objects are things.
That's the secret of JavaScript-mancy
- Branden Iech,
Meditations
An Army of Objects
Hello JavaScriptmancer! It is time to get an introduction to the basics of objects in JavaScript. In this chapter you’ll learn the beauty of the object initializer and the nice improvements ES6 brings to objects. If you think that you already know this stuff, think twice! There is more than one surprise in this chapter and I promise that you’ll learn something new by the end of it.
Let’s get started! We’ll start by concentrating our efforts in the humble object initializer. This will provide a foundation that we can use later when we come to the tome of object-oriented programming in JavaScript and prototypical inheritance.
Objects it is!
Object Initializers (a.k.a. Object Literals)
The simplest way to create an object in JavaScript is to use an object initializer:
1 var critter = {}; // {} is an empty object initializer
You can add properties and methods inside your object initializer to your heart’s content:
1 critter = {
2 position: {x: 0, y: 0},
3 movesTo: function (x, y){
4 console.log(this + ' moves to (' + x + ',' + y + ')');
5 this.position.x = x;
6 this.position.y = y;
7 },
8 toString: function(){
9 return 'critter';
10 },
11 hp: 40
12 }
And, of course, if you call a method within the critter object it behaves as you have come to expect from any good self-respecting method:
1 critter.moveTo(10, 10);
2 // => critter moves to (10,10)
As you saw in the introduction of the book, you can augment any11 object at any time with new properties:
1 critter.damage = 1;
2 critter.attacks = function(target) {
3 console.log(this + ' rabidly attacks ' + target +
4 ' with ' + this.damage + ' damage');
5 target.hp-=this.damage;
6 };
And use these new abilities to great devastation:
1 var rabbit = {hp:10, toString: function(){return 'rabbit';}};
2
3 critter.attacks(rabbit);
4 // => critter rabidly attacks rabbit with 1 damage
Alternatively, you can access any property and method within an object by using the indexing notation via []:
1 critter['attacks'](rabbit);
2 // => critter rabidly attacks rabbit with 1 damage
Although a little bit more verbose, this notation lets you use special characters as names of properties and methods:
1 critter['sounds used when communicating'] = [
2 'beeeeeh', 'grrrrr', 'tjjiiiiii'
3 ];
4 critter.saysSomething = function(){
5 var numberOfSounds = this['sounds used when communicating'].length,
6 randomPick = Math.floor(Math.random()*numberOfSounds);
7
8 console.log(this['sounds used when communicating'][randomPick]);
9 };
10
11 critter.saysSomething();
12 // => beeeeeeh (random pick)
13 critter.saysSomething();
14 // => tjjiiiii (random pick)
As you can see in many of the examples above, you can use the this keyword to reference the object itself and thus access other properties within the same object.
Getters and Setters
Getters and setters are an often overlooked feature within object initializers. You’ll even find fairly seasoned JavaScript developers that don’t know about their existence. They work exactly like C# properties and look like this:
1 var mouse = {
2 strength: 1,
3 dexterity: 1,
4 get damage(){
5 return this.strength*die20() + this.dexterity*die8();
6 },
7 attacks: function(target){
8 console.log(this + ' ravenously attacks ' + target +
9 ' with ' + this.damage + ' damage!');
10 target.hp-=this.damage;
11 },
12 toString: function() { return 'mouse';}
13 }
Notice the strange get damage() function-like thingy? That’s a getter. In this case, it represents the read-only property damage that is calculated from other two properties strength and dexterity.
1 mouse.attacks(rabbit);
2 // => mouse ravenously attacks rabbit with 19 damage!
3 mouse.attacks(rabbit);
4 // => mouse ravenously attacks rabbit with 15 damage!
Getters are extremely useful when you need to define computed properties, that is, properties described in terms of other existing properties. They save you from needing to keep additional and unnecessary state that brings the additional burden of keeping it in sync with the properties it depends on (in this case strength and dexterity).
We can also use a backing field to perform additional steps or validation:
1 var giantBat = {
2 _hp: 1,
3 get hp(){ return this._hp;},
4 set hp(value){
5 if (value < 0) {
6 console.log(this + ' dies :(')
7 this._hp = 0;
8 } else {
9 this._hp = value;
10 }
11 },
12 toString: function(){
13 if (this.hp > 0){
14 return 'giant bat';
15 } else {
16 return 'a dead giant bat';
17 }
18 }
19 };
In this example we ensure that the _hp property of the giant bat cannot go below 0 (because you can’t be deader than dead, unless you are a necromancer that is):
1 mouse.attacks(giantBat);
2 // => "mouse ravenously attacks giant bat with 23 damage!"
3 // => "giant bat dies :("
4 console.log(giantBat.toString());
5 // => a dead giant bat
Method Overloading
Method overloading within object initializers works just like with functions. As we saw in the previous chapter, if you try to overload a method following the same pattern that you are accustomed to in C#:
1 var venomousFrog = {
2 toString: function(){
3 return 'venomous frog';
4 },
5 jumps: function(meters){
6 console.log(this + ' jumps ' + meters + ' meters in the air');
7 },
8 jumps: function(arbitrarily) {
9 console.log( this + ' jumps ' + arbitrarily);
10 }
11 };
You’ll just succeed in overwriting the former jump method with the latter:
1 venomousFrog.jumps(10);
2 // => venomous frog jumps 10
3 // ups we have overwritten a the first jumps method
Instead, use any of the patterns that you saw in the previous chapter to achieve method overloading. For instance, you can inspect the arguments being passed to the jump function:
1 venomousFrog.jumps = function(arg){
2 if (typeof(arg) === 'number'){
3 console.log(this + ' jumps ' + arg + ' meters in the air');
4 } else {
5 console.log( this + ' jumps ' + arg);
6 }
7 };
This provides a naive yet functioning implementation of method overloading:
1 venomousFrog.jumps(10);
2 // => venomous frog jumps 10 meters
3 venomousFrog.jumps('wildly in front of you')
4 // => venomous frong jumps wildly in front of you
Creating Objects With Factories
Creating one-off objects through object initializers can be tedious, particularly whenever you need more than one object of the same “type”. That’s why we often use factories13 to encapsulate object creation:
1 function monster(type, hp){
2 return {
3 type: type,
4 hp: hp || 10,
5 toString: function(){return this.type;},
6 position: {x: 0, y: 0},
7 movesTo: function (x, y){
8 console.log(this + ' moves to (' + x + ',' + y + ')');
9 this.position.x = x;
10 this.position.y = y;
11 }
12 };
13 }
Once defined, we can just use it to instantiate new objects as we wish:
1 var tinySpider = monster('tiny spider', /* hp */ 1);
2 tinySpider.movesTo(1,1);
3 // => tiny spider moves to (1,1)
1 var giantSpider = monster('giant spider', /* hp */ 200);
2 giantSpider.movesTo(10,10);
3 // => giant spider moves to (10,10);
There’s a lot of cool things that you can do with factories in JavaScript. Some of them you’ll discover when you get to tome of OOP where we will see an alternative to classical inheritance in the shape of object composition via mixins. In the meantime let’s take a look at how to achieve data privacy.
Data Privacy in JavaScript
You may have noticed by now that there’s no access modifiers in JavaScript, no private, public nor protected keywords. That’s because every property is public, that is, there is no way to declare a private property by using a mere object initializer. You need to rely on additional patterns with closures to achieve data privacy, and that’s where factories come in handy.
Imagine that we have the previous example of our monster but now we don’t want to reveal how we have implemented positioning. We would prefer to hide that fact from prying eyes and object consumers. If we decide to change it in the future, for a three dimensional representation, polar coordinates or who knows what, it won’t break any clients of the object. This is part of what I call intentional programming, every decision that you make, the interface that you build, the parts that you choose to remain hidden or public, represent your intentions on how a particular object or API should be used. Be mindful and intentional when you write code. Back to the monster:
1 function stealthyMonster(type, hp){
2 var position = {x: 0, y: 0};
3
4 return {
5 type: type,
6 hp: hp || 10,
7 toString: function(){return 'stealthy ' + this.type;},
8 movesTo: function (x, y){
9 console.log(this + ' moves stealthily to (' + x + ',' + y + ')\
10 ');
11 // this function closes over (or encloses) the position
12 // variable position is NOT part of the object itself,
13 // it's a free variable that's why you cannot access it
14 // via this.position
15 position.x = x;
16 position.y = y;
17 }
18 };
19 }
Let’s take a closer look to that example. We have extracted the position property outside of the object initializer and inside a variable within the stealthyMonster scope (remember that functions create scopes in JavaScript). At the same time, we have updated the movesTo function, which creates its own scope, to refer to the position variable within the outer scope effectively creating a closure.
Because position is not part of the object being returned, it is not accessible to clients of the object through the dot notation. Because the movesTo becomes a closure it can access the position variable within the outside scope. In summary, we got ourselves some data privacy:
1 var darkSpider = stealthyMonster('dark spider');
2 console.log(darkSpider.position)
3 // now position is completely private
4 // => undefined
5
6 darkSpider.movesTo(10,10);
7 // => stealthy dark spider moves stealthily to (10,10)
ES6 Improves Object Initializers
ES6 brings some improvements to object initializers that reduce the amount of code needed to create a new object. For instance, with ES6 you can declare methods within objects using shorthand syntax:
1 let sugaryCritter = {
2 position: {x: 0, y: 0},
3 // from movesTo: function(x, y) to...
4 movesTo(x, y){
5 console.log(`${this} moves to (${x},${y})`);
6 this.position.x = x;
7 this.position.y = y;
8 },
9 // from toString: function() to...
10 toString(){
11 return 'sugary ES6 critter';
12 },
13 hp: 40
14 };
15
16 sugaryCritter.movesTo(10, 10);
17 // => sugary ES6 critter moves to (10, 10)
As you can appreciate from the movesTo and toString methods in this example above, using shorthand notation lets you skip the function keyword and collapse the parameters of a function directly after its name.
Additionally you can apply shorthand syntax to object properties. When you write factory functions you’ll often follow a pattern where you initialize object properties based on the arguments passed to the factory function:
1 function simpleMonster(type, hp = 10){
2 return {
3 type: type,
4 hp: hp
5 };
6 }
Where you have a little bit of redundant code in type: type and hp: hp. Property shorthand syntax removes the need to repeat yourself by letting you write the property/value pair only once. So that the previous example turns into a much terser factory method:
1 function simpleMonster(type, hp = 10){
2 return {
3 // with property shorthand we avoid the need to repeat
4 // the name of the variable twice (type: type)
5 type,
6 hp
7 };
8 }
And here you have a complete example where we use both method and property shorthand to get the ultimate sugary monster:
1 function sugaryStealthyMonster(type, hp = 10){
2 let position = {x: 0, y: 0};
3
4 return {
5 // with property shorthand we avoid the need to repeat
6 // the name of the variable twice (type: type)
7 type,
8 hp,
9 toString(){return `stealthy ${this.type}`;},
10 movesTo(x, y){
11 console.log(`${this} moves stealthily to (${x},${y})`);
12 position.x = x;
13 position.y = y;
14 }
15 };
16 }
17
18 let sugaryOoze = sugaryStealthyMonster('sugary Ooze', /*hp*/ 500);
19 sugaryOoze.movesTo(10, 10);
20 // => stealthy sugary Ooze moves stealthily to (10,10)
Finally, with the advent of ES6 you can use any expression as the name of an object property. That is, you are no longer limited to normal names or using the square brackets notation that handles special characters. From ES6 onwards you’ll be able to use any expression and the JavaScript engine will evaluate it as a string (with the exception of ES6 symbols which we’ll see in the next section). Take a look at this:
1 let theArrow = () => 'I am an arrow';
2
3 let crazyMonkey = {
4 // ES5 valid
5 name: 'Kong',
6 ['hates!']: ['mario', 'luigi'],
7
8 // ES6 computed property names
9 [(() => 'loves!')()]: ['bananas'],
10 [sugaryOoze.type]: sugaryOoze.type
11 // crazier yet
12 [theArrow]: `what's going on!?`,
13 }
This example let’s you appreciate how any expression is valid. We’ve used the result of evaluating a function (() => 'loves!')(), a property from another object sugaryOoze.type and even an arrow function theArrow as property names. If you inspect the object itself, you can see how each property has been intrepreted as a string:
1 console.log(crazyMonkey);
2 // => [object Object] {
3 // function theArrow() {
4 // return 'I am an arrow';
5 // }: "what's going on!?",
6 // hates!: ["mario", "luigi"],
7 // loves!: ["bananas"],
8 // name: "Kong",
9 // sugary Ooze: "sugary Ooze"
10 // }
And you can retrieve them with the [](indexing) syntax:
1 console.log(crazyMonkey[theArrow]);
2 // => "what's going on!?"
Use cases for this particular feature? I can only think of some pretty far-fetched edge cases for dynamic creation of objects on-the-fly. That and using symbols as property names wich gracefully brings us to ES6 symbols and how to take advantage of them to simulate data privacy.
ES6 Symbols and Data Privacy
Symbols are a new type in JavaScript. They were conceived to represent constants and to be used as identifiers for object properties. The specification even describes them as the set of all non-string values that may be used as the key of an object property 14. They are immutable and can have a description associated to them.
You can create a symbol using the Symbol function:
1 let anUndescriptiveSymbol = Symbol();
2 console.log(anUndescriptiveSymbol);
3 // => [object Symbol]
4 console.log(typeof anUndescriptiveSymbol);
5 // => symbol
6 console.log(anUndescriptiveSymbol.toString());
7 // => Symbol()
And you can add a description to the symbol by passing it as an argument to the same function. This will be helpful for debugging since the toString method will display that description:
1 // you can add a description to the Symbol
2 // so you can identify a symbol later on
3 let up = Symbol('up');
4 console.log(up.toString());
5 // => Symbol(up)
Each symbol is unique and immutable, so even if we create two symbols with the same description, they’ll remain two completely different symbols:
1 // each symbol is unique and immutable
2 console.log(`Symbol('up') === Symbol('up')??
3 ${Symbol('up') === Symbol('up')}`);
4 // => Symbol('up') === Symbol('up')?? false
ES6 symbols offer us a new approach to data privacy in addition to closures. Properties that use a symbol as name (or key) can only be accessed by a reference to that symbol (the very same symbol used to identify the property). Because of this special characteristic, if you don’t expose a symbol to the outer world you have provided yourself with data privacy. Let’s see how this works in practice:
1 function flyingMonster(type, hp = 10){
2 let position = Symbol('position');
3
4 return {
5 [position]: {x: 0, y: 0},
6 type,
7 hp,
8 toString(){return `stealthy ${this.type}`;},
9 movesTo(x, y){
10 console.log(`${this} flies like the wind from` +
11 `(${this[position].x}, ${this[position].y}) to (${x},${y})`);
12 this[position].x = x;
13 this[position].y = y;
14 }
15 };
16 }
17
18 let pterodactyl = flyingMonster('pterodactyl');
19 pterodactyl.movesTo(10,10);
20 // => stealthy pterodactyl flies like the wind from (0,0) to (10,10)
Since outside of the flyingMoster function we don’t have a reference to the symbol position (it is scoped inside the function), we cannot access the position property:
1 console.log(pterodactyl.position);
2 // => undefined
And because each symbol is unique we cannot access the property using another symbol with the same description:
1 console.log(pterodactyl[Symbol('position')]);
2 // => undefined
If everything ended here the world would be perfect, we could use symbols for data privacy and live happily ever after. However, there’s a drawback: The JavaScript Object prototype provides the getOwnPropertySymbols method that allows you to get the symbols used as properties within any given object. This means that after all this trouble we can access the position property by following this simple procedure:
1 var symbolsUsedInObject = Object.getOwnPropertySymbols(pterodactyl);
2 var position = symbolsUsedInObject[0];
3 console.log(position.toString());
4 // => Symbol(position)
5 // Got ya!
6
7 console.log(pterodactyl[position]);
8 // => {x: 10, y: 10}
9 // ups!
So you can think of symbols as a soft way to implement data privacy, where you give a clearer intent to your code, but where your data is not truly private. This limitation is why I still prefer using closures over Symbols.
Concluding
In this chapter you learned the most straightforward way to work with objects in JavaScript, the object initializer. You learned how to create objects with properties and methods, how to augment existing objects with new properties and how to use getters and setters. We also reviewed how to overload object methods and ease the repetitive creation of objects with factories. We wrapped factories with a pattern for achieving data privacy in JavaScript through the use of closures.
You also learnt about the small improvements that ES6 brings to object initializers with the shorthand notation for both methods and properties. We wrapped the chapter with a review of the new ES6 Symbol type and its usage for attaining a soft version of data privacy.
Exercises
Mysteries of the JavaScript Arcana: JavaScript Quirks Demystified
Beware of any assumptions,
distrust any preconceptions,
forgo your experience,
and think with the mind of a beginner.
- Appa Ojnh
The White Sage
A Couple of Tips About JavaScript Quirks and Gotchas
While JavaScript looks a lot like a C-like language, it does not behave like one in many ways. This, I would say, is the biggest reason why C# developers get so confused when they come to JavaScript.
If you’ve followed the book closely, you may have noticed that I have decided to call these unexpected behaviors the JavaScript Arcana. You have already seen several examples of these shadowy features thus far. Let’s make a quick summary of them:
- Function scope and variable hoisting
- Array-like objects
- Function overloading
We’ll start this chapter by making a short review of the quirks that you’ve already learned (repetition is a great tool for learning). And we’ll continue by diving deeper into these other parts of the JavaScript Arcana:
- The sneaky
thiskeyword - Global scope as a default
- Type coercion madness
- JavaScript strict mode
We will focus particularly in the obscure behavior of the this keyword, our most dangerous foe. I expect that what you will learn in this chapter will save you from unmeasurable frustration in the future.
A Quick Refresher of the JavaScript Arcana 101
In The Basics of JavaScript Functions we saw how JavaScript has function scope. That is, as opposed to C# where every block of code creates a new scope, in JavaScript it is only functions that create new scopes. Every time you declare a variable through the var keyword it is scoped to its containing function. You also learned the concept of hoisting and how the JavaScript runtime moves your variable declarations to the top of a function body. Finally, you discovered how ES6 brings the let and const keywords that give you the ability to declare block-scoped variables and forget about the headaches of hoisting and function-scoped variables.
In Function Patterns: Arbitrary Arguments you learned about the arguments object. It can be accessed within every function to retrieve the arguments being passed to that function at runtime. You saw how the arguments object, although it looks like an array, it is actually what we call an array-like object. Array-like objects can be enumerated, indexed and have a length property but they lack all array methods. You also discovered how to convert these objects to actual arrays using Array.prototype.slice (or Array.from) and how the new ES6 rest operator solves the arguments issue completely.
In Function Patterns: Overloading you learned how you cannot overload JavaScript functions or methods in the same way that you do in C#. Instead, you can use several patterns to achieve the same effect: Argument inspection, options objects, ES6 default arguments or functional programming with polymorphic functions.
Now that we’ve warmed up to JavaScript weirdest features let’s take a look at the behavior of this.
This, Your Most Dangerous Foe
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 or Zie will write this common piece of code unaware of the terrible dangers that lurk just one HTTP call away…
1 function UsersCatalog(){
2 this.users = [];
3 getUsers()
4
5 function getUsers(){
6 $.getJSON('https://api.github.com/users')
7 .success(function updateUsers(users){
8 this.users.push(users);
9 // BOOOOOOOM!!!!!
10 // => Uncaught TypeError:
11 // Cannot read property 'push' of undefined
12 });
13 }
14 }
15 var catalog = new UsersCatalog();
In this code example we are trying to retrieve a collection of users from the GitHub API. We perform an AJAX15 request using jQuery getJSON and if the request is successful the response is passed as an argument to the updateUsers function.
The example throws an exception cannot read property 'push' of undefined which is the JavaScript version of our well known nemesis: The NullReferenceException (we meet again). Essentially, when we evaluate the updateUsers function, the this.users expression takes the value of undefined. When we try to execute this.users.push(users) we’re basically calling the method push on nothing and thus the exception being thrown.
In order to understand why this is happening we need to learn how this works in JavaScript. In the next sections we will do just that. By the end of the chapter, when we have demystified this and become this-xperts, you’ll be able to understand what is the cause of the error.
JavaScript Meets This
So this in JavaScript is weird. Unlike in other languages, the value of this in JavaScript depends on the context in which a function is invoked. Repeat. The behavior of this in JavaScript is not 100% stable nor reliable at all times, it depends on the context in which a function is invoked.
This essentially means that depending on how you call a function, the value of this inside that function will vary. We can distinguish between these four scenarios:
-
thisand objects -
thisunbound -
thisexplicitly -
thisbound
This And Objects
In the most common scenario for an OOP developer we call functions as methods. That is, we call a function that is a property within an object using the dot notation.
If we have a hellHound spawned in the pits of hell with the ferocious ability of breathing fire:
1 // #1. A function invoked in the context of an object (a method)
2 var hellHound = {
3 attackWithFireBreath: function(){
4 console.log(this + " jumps towards you and unleashes " +
5 "his terrible breath of fire! (-3 hp, +fear)");
6 },
7 toString: function (){ return 'Hellhound';}
8 }
When we call its attackWithFireBreath method using the dot notation this will take the value of the object itself:
1 hellHound.attackWithFireBreath();
2 // => Hellhound jumps towards you and unleashes
3 // his terrible breath of fire! (-3 hp, +fear)
4 // 'this' is the hellHound object
Nothing strange here. This is the version of this we know and love from C#. Things get a little bit trickier in the next scenario.
This Unbound
In JavaScript you can do crazy things. Things like invoking a method without the context of the object in which it was originally defined. Since functions are values we can just save the attackWithFireBreath method within a variable:
1 // #2. A function invoked without the context of its object
2 var attackWithFireBreath = hellHound.attackWithFireBreath;
And invoke the function via the newly created variable:
1 attackWithFireBreath();
2 // => [object Window] jumps towards you and unleashes
3 // his terrible breath of fire! (-3 hp, +fear)
Ooops! What did just happen here? this is no longer the hell hound but the Window object. You may be asking yourself: What? And here comes the weird part that you need to remember: Whenever you invoke a function without an object as context the this automatically becomes the Window object.
The Window16 object in JavaScript represents the browser window and contains the document object model (also known as DOM) an object representation of the elements within a website.
As a cool exercise, you can now take that free function and add it to another object zandalf different from the original:
1 // we could add the same method to another object:
2 var zandalf = {
3 toString: function(){return 'zandalf';}
4 };
5 zandalf.attackWithFireBreath = attackWithFireBreath;
Then call it as a method with the dot notation:
1 zandalf.attackWithFireBreath();
2 // => zandalf jumps towards you and unleashes
3 // his terrible breath of fire! (-3 hp, +fear)
4 // => 'this' is the jaime object
And again, when we invoke the original function in the context of an object, even when it is another one different from the original, this takes the value of that object.
Let’s make a summary of what you’ve seen up until now:
- Call a function in the context of an object and
thiswill take the value of the object - Call a function without context and
thiswill take the value of theWindowobject. Unless you are in strict mode in which case it will take the value ofundefined.
This Explicitly
All functions in JavaScript descend from the Function prototype. This prototype provides two helpful methods that allow you to explicitly set the context in which to execute a function: call and apply.
Take the attackWithFireBreath function from the last example. This time, instead of calling it directly, we use its call method and pass the object zandalf as an argument:
1 attackWithFireBreath.call(zandalf);
2 // => zandalf...
3 // => 'this' is zandalf
The object zandalf becomes the context of the function and thus the value of this. Likewise, if we call the apply method on the same function and pass an object hellHound as argument:
1 attackWithFireBreath.apply(hellHound);
2 // => hell hound...
3 // => 'this' is hellHound
We can verify how the object hellHound becomes the context of the function and the value of this.
But, what happens if the original function has paremeters? Worry not! Both call and apply take additional arguments that are passed along to the original function. Take this function attackManyWithFireBreath that unleashes a terrible breath of fire on many unfortunate targets:
1 function attackManyWithFireBreath(){
2 var targets = Array.prototype.slice.call(arguments, 0);
3 console.log(this + " jumps towards " + targets.join(', ') +
4 " and unleashes his terrible breath of fire! (-3 hp, +fear)");
5 }
The call method let’s you specify a list of arguments separated by commas in addition to the value of this:
1 attackManyWithFireBreath.call(hellHound, 'you', 'me', 'the milkman');
2 // => Hellhound jumps towards you, me, the milkman and unleashes
3 // his terrible breath of fire! (-3 hp, +fear)
Likewise, apply takes an array of arguments:
1 attackManyWithFireBreath.apply(hellHound, ['me', 'you', 'irene']);
2 // => Hellhound jumps towards me, you, irene and
3 // unleashes his terrible breath of fire! (-3 hp, +fear)
And that’s how you can set the value of this explicitly. Let’s recapitulate what we’ve learned so far:
- Call a function in the context of an object and
thiswill take the value of the object - Call a function without context and
thiswill take the value of theWindowobject. Unless you are in strict mode in which case it will take the value ofundefined. - Call a function using
callandapplypassing the context explicitly as an argument andthiswill take the value of whatever you pass in.
This Bound
As of ES5, the Function prototype also provides a very interesting method called bind. bind lets you create new functions that always have a fixed context, that is, a fixed value for this 17.
Let’s use bind to set a fixed value for this in our original attackWithFireBreath function. bind will return a new function attackBound that will have this with a value of our choosing. In this case, it will be hellHound:
1 // As of ES5 we can bind the context of execution of a function
2 // FOR EVER
3 attackBound = attackWithFireBreath.bind(hellHound);
After using bind, the value of this is bound to the hellHound object even if you are not using the dot notation:
1 attackBound();
2 // => Hellhound jumps towards you and unleashes
3 // his terrible breath of fire! (-3 hp, +fear)
4 // `this` is Hellhound even though I am not using the dot notation
Moreover, if you assign the attackBound method to another object and call it using the dot notation, the attackBound method is executed in the context of the original object hellHound. That is, after binding a function to a context with bind, the context will remain the same even after assigning the function to another object:
1 // the function is bound even if I give the function to another obje\
2 ct
3 zandalf.attackBound = attackBound;
4
5 zandalf.attackBound();
6 // => Hellhound ...
7 // `this` is Hellhound even though I am using dot notation
8 // with another object
Once a function is bound it is not possible to un-bound it nor re-bind it to another object:
1 // You cannot rebind a function that is bound
2 var attackReBound = attackBound.bind(zandalf);
3
4 attackReBound();
5 // => Hellhound ...
6
7 attackBound();
8 // => hellHound ...
But you can always use the original unbound function to create new bound versions through subsequent calls to bind with different contexts:
1 // But you can still bind the original
2 var attackRebound = attackWithFireBreath.bind(zandalf);
3 attackRebound();
4 // => zandalf...
Concluding This
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
Windowobject orundefined(strict mode) if a function is invoked by itself - Be whichever object we pass as argument to
callorapply - Be whichever object we pass as argument to
bind.
If now that you are a this-xpert we go back to the original example you will be able to spot the problem at once. Since the updateUsers function is a callback, it is not invoked in the context of the UsersCatalog object. Callbacks are invoked as normal functions, and thus in the context of the Window object (or undefined in in strict mode). Because of this, the value of this within updateUsers wouldn’t be catalog but undefined18.
Because this is not the catalog object, it doesn’t have a users property and thus the resulting cannot read property of undefined error:
1 function UsersCatalog(type){
2 this.users = [];
3 getUsers()
4
5 function getUsers(){
6 $.getJSON('https://api.github.com/users')
7 .success(function(users){
8 this.users.push(users);
9 // BOOOOOOOM!!!!!
10 // => Uncaught TypeError:
11 // Cannot read property 'push' of undefined
12 // 'this' in this context is the jqXHR object
13 // not our original object
14 });
15 }
16 }
17 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 when it refers to the UsersCatalog object and use it within the closure function as depicted below (a very common pattern in JavaScript):
1 function UsersCatalogWithClosure(){
2 "use strict";
3 var self = this;
4
5 self.users = [];
6 getUsers()
7
8 function getUsers(){
9 $.getJSON('https://api.github.com/users')
10 .success(function(users){
11 self.users.push(users);
12 console.log('success!');
13 });
14 }
15 }
16 var catalog = new UsersCatalogWithClosure();
Or you can take advantage of bind and ensure that the function that you use as callback is bound to the object that you want:
1 //#2. Using bind
2 function UsersCatalogWithBind(){
3 "use strict";
4
5 this.users = [];
6 getUsers.bind(this)();
7
8 function getUsers(){
9 $.getJSON('https://api.github.com/users')
10 .success(updateUsers.bind(this));
11 }
12
13 function updateUsers(users){
14 this.users.push(users);
15 console.log('success with bind!');
16 }
17 }
18 var catalog = new UsersCatalogWithBind();
Later within the book, you’ll see how ES6 arrow functions can also lend you a hand in this type of scenario.
Global Scope by Default and Namespacing in JavaScript
As you will come to appreciate by the end of the book, JavaScript has a minimalistic design. It has a limited number of primitive constructs that can be used and composed to achieve higher level abstractions and other constructs that are native to other languages. One of these constructs are namespaces.
Since we do not have the concept of namespaces, variables that are declared in a JavaScript file are part of the global scope where they are visible and accessible to every JavaScript file within your application. Yey! Party!
1 var dice = "d12";
2 dice;
3 // => d12
4 window.dice
5 // => d12
6 // ups... we are in the global scope/namespace
The problems with global variables are well known: they tightly couple different components of your application and they can cause name collisions. Imagine that you have several JavaScript files declaring variables with the same names but performing different tasks. Or imagine importing third party libraries that could overwrite your own variables. Chaos and destruction!! Because of these problems we want to completely avoid the use of global variables, yet we lack support for namespaces in JavaScript… What to do?
We can use objects to emulate the construct of namespaces. A commonly used pattern is depicted below where we use what we call an IIFE (immediately invoked function expression) to create/augment a namespace:
1 // IIFE - we invoke the function expression as soon as we declare it
2 (function(armory){
3 // the armory object acts as a namespace
4 // we can add properties to it
5 // these would constitute the API for
6 // the 'armory' module/namespace
7 armory.sword = {damage: 10, speed: 15};
8 armory.axe = {damage: 15, speed: 8};
9 armory.mace = {damage: 16, speed: 7};
10 armory.dagger = {damage: 5, speed: 20};
11
12 // additionally you could declare private variables and
13 // functions as well
14
15 // either augment or create the armory namespace
16 }(window.armory = window.armory || {} ));
17
18 console.log(armory.sword.damage);
19 // => 10
An immediately-invoked function expression is just that, a function expression that you invoke immediately. By virtue of being a function it creates a new scope where you can safely have your variables and avoid name collisions with the outside world. If you were to declare a variable with the same name of an existing variable in an outer scope, the new variable would just shadow the outer variable.
By immediately invoking the function you can extend the window.armory object with whichever properties you desire, creating a sort of public API for the armory object that becomes a namespace or module. A container where you can place properties and functions and expose them as services for the rest of your application.
We will come back to namespacing and higher level code organization in JavaScript within the tome on JavaScript modules.
Type Coercion Madness
In the basic ingredients of javascript-mancy you learned a little bit about type coercion in JavaScript. You learn how JavaScript provides the == and != abstract equality operators that let you perform loose equality between values and the === and !== operators that perform strict equality.
By using the first set of operators JavaScript will try to coerce the types being compared to a matching type before performing the comparison, whilst the second set of operators expect a matching type. You also learned how type coercion creates the concept of falsey and truthy by assigning true and false to different values and types when being converted to boolean.
I thought it would be interesting for you to learn a little bit more about this JavaScript feature and about its possible pitfalls.
JavaScript was designed to be an accessible language19, a language that even a layman, someone with no prior programming experience could use to create interactive websites. A welcoming language that would help anyone to write their own web applications and solve their own problems. You can see this vision clearly in many of the features of JavaScript, even in some of the most controversial ones. If you think about it from this perspective, it doesn’t feel so weird that the following statement evaluates to true:
1 > 42 == '42'
2 // => true
For is not 42 equal to '42'? Don’t both refer to the same number? Does it really matter that they have different types? And so we have implicit conversion of types.
In my experience, taking advantage of type coercion usually results in more terse code:
1 // as opposed to (troll !== null && troll !== undefined)
2 > if (troll) {
3 // do stuff
4 }
Taking advantage of the strict equality usually results in more correct, less bug-prone code:
1 > if (troll !== null && troll !== undefined){
2 // do stuff
3 }
In the first case the condition will be satisfied as long as troll has a truthy value: It could be an object, an array, a string, a number different than 0. In the second case, the condition will be satisfied whenever troll is not null nor undefined (so even it troll is equal to 0 as opposed to the previous example). Expressiveness or correctness, choose the one that you prefer.
The truthy and falsey values for the most common types are as follow (note how we use the !! to explicitly convert every value to booleans). Both arrays and objects are truthy, even when they are empty:
1 > !![1,2,3]
2 // => true
3 > !![]
4 // => true
5 > !!{message: 'hello world'}
6 // => true
7 > !!{}
8 // => true
A non-empty string is truthy while an empty string is falsey:
1 > !!"hellooooo"
2 // => true
3 > !!""
4 // => false
Numbers are truthy but for 0 that is falsey:
1 > !!42
2 // => true
3 > !!0
4 // => false
undefined and null are always falsey:
1 > !!undefined
2 // => false
3 > !!null
4 // => false
Using JavaScript in Strict Mode
From ES5 onwards you can use strict mode to get a better experience with JavaScript. One of the main goals of strict mode is to prevent you from falling into common JavaScript pitfalls by making the JavaScript runtime more proactive in throwing errors instead of causing silent ones or unwanted effects.
Take the example of the value of this in callbacks. Instead of setting the value of this to the Window object, when you use strict mode the value of this becomes undefined. This little improvement prevents you from accessing the Window object or extending it by mistake, and will alert you with an error as soon as you try to do it. Short feedback loops and failing fast are sure recipes for success.
Other improvements that come with strict mode are:
- trying to create a variable without declaring it (with
var,letorconst) will throw an error. Without strict mode it will add a property to theWindowobject. - trying to assign a variable to NaN, or to a read-only or non-writable property within an object throws an exception
- trying to delete non-deletable properties within an object throws an exception
- trying to have duplicated names as arguments throws a syntax error
- and more explicit errors that will help you spot bugs faster
Additionally with strict mode enabled the JavaScript runtime is free to make certain assumptions and perform optimizations that will make your code run faster. If you want to learn more about the nitty-gritty of strict mode I recommend that you take a look at the MDN (Mozilla Developer Network), the best JavaScript resource in the web.
Enabling Strict Mode
You can enable strict mode by writing 'strict mode'; at the top of a JavaScript file. This will enable strict mode for the whole file:
1 'strict mode';
2 // my code ...
3 var pouch = {};
Alternatively, you can use the strict mode declaration at the top of a function. This will result in the strict mode only being applied within that function:
1 (function(){
2 'strict mode';
3 // my code ...
4 var bag = {};
5
6 }());
Wrapping your strict mode declarations inside a function will prevent the strict mode from being applied to code that may not be prepared to handle strict mode. This can happen when concatenating strict mode scripts with non-strict mode scripts like external third party libraries outside of your control.
ES6 modules always use strict mode semantics.
Concluding
In this chapter you learned about the weirdest bits of JavaScript, the mysterious JavaScript Arcana. You started the chapter by reviewing parts of the JavaScript Arcana that you read about in previous chapters: function scope and variable hoisting, array-like objects and function overloading.
You continued taking a look at the sneaky this keyword, and understood how its value depends on the context in which a function is executed:
- If you invoke a function as a method using the dot notation, the
thisvalue will be the object that holds that method. - If you call a function directly the value of
thiswill be theWindowobject (orundefinedin strict mode). - If you call a function using either
call,applyorbind, the value ofthiswill be set to the object that you pass as argument to either of these functions. - You can use
bindto create a new version of a function that is bound to a specific object. That is, in that new funtionthisbecomes the object for all eternity.
You saw how JavaScript assumes global scope by default and how you can achieve a similar solution to namespaces by using objects to represent them and organize your code. You examined the concept of IIFE (Immediately Invoked Function Expression) and how you can use it to create an isolated scope to declare your variables and add them to a namespace object.
After that you reviewed type coercion in JavaScript to finally wrap the chapter examining strict mode, a more restricted version of JavaScript that attempts to help you find bugs faster by failing more loudly.
Exercises
A Guide to Strings, Finding the Right Words and Proper Spell Intonation
Klaatu...
verata...
n...
Necktie.
Nectar. Nickel. Noodle.
- Ash
Epic Hero of Ages
Find the Right Words You Must
The use of words, text and text manipulation is commonplace in applications today. Often times applications will provide some sort of user interface as a way to allow user interaction. This UI will contain a myriad of textual information in various forms like labels, tooltips, help texts, text-based content, etc. Even applications that don’t expose a user interface will often log information to the filesystem or to analytics services for troubleshooting and monitoring.
JavaScript, like many other languages, has a primitive type that catters to all your text representation and manipulation needs, the string. In this chapter you’ll learn all you need to know about strings in JavaScript and the exciting new features that come with ES6: template literals and tags.
Let’s start with the basics first!
The Basic Of Strings
You create a string by either using single ' or double quotes ".
1 // you can create a string using double quotes
2 > typeof "Klaatu... verata... n... Necktie. Nectar. Nickel. Noodle."
3 // => string
4
5 // or using single quotes
6 > typeof 'Klaatu... verata... n... Necktie. Nectar. Nickel. Noodle.'
7 // => string
You will often use a single quoted ' string to include " double quotes inside the string and vice versa:
1 // you'll often use a ' to escape "
2 > "Ya ain't gonna need that fairy dust!"
3 // => ya ain't gonna need that fairy dust!
4
5 // and vice versa
6 > 'it was, in essence, a sophisticated heat beam which we called a "\
7 laser".'
8 // => it was, in essence, a sophisticated heat
9 // beam which we call a "laser".
You can concatenate two strings using the + operator:
1 > "Three rings " + "for the elves"
2 // => three rings for the elves
The + operator is often used to inject values within a string and thus create text based on data:
1 > var conan = {toString: function() {return 'Conan, the cimmerian';}}
2 > conan + " was a famous hero of a past age"
3 // => Conan, the cimmerian was a famous hero of a past age
You can also create multiline strings using the same operator:
1 > "There are few men with more blood on their hands than me. " +
2 "None, that I know of. " +
3 "The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
4 // => "There are few men with more blood on their hands than me.
5 // None, that I know of. The Bloody-Nine they call me, my
6 // enemies, and there’s a lot of ’em"
Or, alternatively, with a backslash at the end of each line \:
1 > "There are few men with more blood on their hands than me.\
2 None, that I know of.\
3 The Bloody-Nine they call me, my enemies, and there’s a lot of ’em"
4 // => "There are few men with more blood on their hands than me.
5 // None, that I know of. The Bloody-Nine they call me, my
6 // enemies, and there’s a lot of ’em"
Additionally, you can insert new lines using the newline character ‘\n’:
1 > "There are few men with more blood on their hands than me.\n None,\
2 that I know of.\n The Bloody-Nine they call me, my enemies, and the\
3 re’s a lot of ’em"
4 // => "There are few men with more blood on their hands than me.
5 // None, that I know of.
6 // The Bloody-Nine they call me, my enemies, and there’s a
7 // lot of ’em"
As you may have deduced from the previous example, JavaScript uses the backslash `\’ as escape character:
1 > "\""
2 // => "
3 > '\''
4 // => '
Now that you’ve got a better grasp of strings in JavaScript, let’s take a look at the different operations you can perform on them.
Are Strings Arrays of Characters?
JavaScript strings are not arrays of characters, they behave more like array-like objects. They have a length property, they can be enumerated and indexed but they lack most of the methods of an array. They are also immutable.
1 var justDoIt = 'Just DO IT!';
2
3 // they have a length property
4 console.log('length of justDoIt: ' + justDoIt.length);
5 // => length of justDoIt: 11
6
7 // they can be enumerated
8 for (var c in justDoIt) console.log(justDoIt[c]);
9 // => J
10 // => u
11 // => etc
12
13 // they don't have array methods
14 console.log(justDoIt.forEach)
15 // => undefined
Even though strings are not arrays, you can use some of the array functions on a string by borrowing them from Array.prototype. For instance, you can traverse each character in a string using forEach:
1 > Array.prototype.forEach.call(justDoIt,
2 function(c){console.log(c);})
3 // => J
4 // => u
5 // => etc...
Or inject an arbitrary string between each character with join:
1 Array.prototype.join.call(justDoIt, '--')
2 // => J--u--s--t-- --D--O-- --I--T--!
However, if we try to use reverse it throws an error:
1 Array.prototype.reverse.call(justDoIt);
2 // BOOM!
3 // TypeError: cannot assign to read only property '0'....
The error message gives us some hints as to why reverse doesn’t work: The implementation of reverse is trying to do an in-place string reversal. Because strings are immutable, attempting to replace the first character in a string with the last one causes an error and therefore the "cannot assign to read only property '0'".
Remember, you can use any array methods on strings as long as they don’t attempt to mutate the original string.
Performing Operations with strings
In addition to these array methods, the string type also provides its own series of methods to help you perform the most common text operations.
You can concatenate strings with concat just like you did with the + operator:
1 > String.prototype.concat('hello my nemesis', 'we meet again')
2 // => hello my nemesis we meet again
3
4 > justDoIt.concat(' - Shia Labeaouf')
5 // => Just DO IT! - Shia Labeaouf
You can obtain an uppercase or lowercase version of an existing string using toUpperCase and toLowerCase:
1 console.log(justDoIt.toUpperCase());
2 // => JUST DO IT!
3
4 console.log(justDoIt.toLowerCase());
5 // => just do it!
You can extract a character at a specific position with chartAt:
1 > justDoIt.charAt(0)
2 // => j
The indexOf method returns the position of the first occurrence of a piece of text within a string.
1 > justDoIt.indexOf('DO')
2 // => 5
You’ll often see it used to find out whether a piece of text exists in a string by comparing it to -1:
1 // indexOf returns `-1` when it can't find the piece of text
2 > justDoIt.indexOf('DO') !== -1
3 // => true
4 > justDoIt.indexOf('Sand castle') !== -1
5 // => false
Alternatively you can use the search method. It is an enhanced version of indexOf that allows you to specify what you are looking for using a regular expression:
1 > justDoIt.search(/DO/)
2 // => 5
match is, in turn, an enhanced version of search that lets you find multiple matches within a string according to a regular expression of your choice:
1 > justDoIt.match(/DO/)
2 // => ["DO"]
3
4 > justDoIt.match(/DO.*/)
5 // => ["DO IT!"]
The replace method lets you replace a piece of text with another one of your own choosing:
1 > justDoIt.replace('DO IT!', 'DANCE!')
2 // => Just DANCE!
Since replace also allows for regular expressions, you can match all occurrences of a substring and replace them at once. Just use the g flag (which stands for global):
1 > 'a dragon is a creature that can breathe fire'
2 .replace(/a /g, 'the ')
3 // => the dragon is the creature that can breathe fire
The substr and substring methods let you extract bits of text from an array by specifying indexes. The former expects the start index and the length of the substring whilst the latter expects the starting and ending indexes:
1 // String.prototype.substr(startIndex, length)
2 > 'a dragon is a creature that can breathe fire'.substr(2, 6)
3 // => dragon
4
5 // String.prototype.substring(startIndex, endIndex)
6 > 'a dragon is a creature that can breathe fire'.substring(2, 6)
7 // => drag
You can split a string in several pieces using the split method. The resulting pieces of splitting the string are returned as items of an array:
1 > 'a dragon is a creature that can breathe fire'.split(' ');
2 // => ["a", "dragon", "is", "a", "creature", "that",
3 // "can", "breathe", "fire"]
The split and join methods make it dead easy to convert a string into an array and vice versa. Using them will allow you to take advantage of both the string and array methods without limitations. You have a string and want to use the Array.prototype.map method? Convert it into an array via split, perform whichever operations you need and then use join to get your string back.
1 > 'a dragon is a creature that can breathe fire'.split(' ')
2 .join(' ');
3 // => 'a dragon is a creature that can breathe fire'
New String Features in ES6
ES6 brings a lot of exciting new features to strings:
- Several new helpful methods like
startsWithandendsWith. - A complete overhaul of how we define strings in JavaScript with Template Literals20. Template Literals provide a much better support for string interpolation, multiline strings, HTML-friendly strings and the ability to create reusable string formatters called tags.
ES6 Brings Some New String Methods
After reading the previous sections you may have missed three methods you often use in C#: Contains, StartsWith and EndsWith. Well, worry no more because ES6 brings all these new methods to JavaScript strings:
The startsWith and endsWith methods work just like in C#. The first one verifies whether a string starts with a given substring and the latter checks whether or not a string ends with a given piece of text:
1 > 'thunder and lightning!'.startsWith('thunder')
2 // => true
3 > 'thunder and lightning!'.endsWith('lightning!')
4 // => true
The includes method performs the same function as C# Contains by checking whether or not a piece of text is contained within a string:
1 > 'thunder and lightning!'.includes('thunder')
2 // => true
3 > 'thunder and lightning!'.includes('lightning!')
4 // => true
5 > 'thunder and lightning!'.includes('and')
6 // => true
Note how using the includes method provides a much better developer experience and readable code than the indexOf method from previous sections.
Finally, ES6 brings the repeat method that allows you to create a new string by repeating an existing string a specific number of times:
1 > 'NaN'.repeat(10) + ' BatMan!'
2 // => NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN BatMan!
The Awesomeness of ES6 Template Literals
ES6 template literals provide a new and very powerful way of working with strings. You can create a string using a template literal by wrapping some text between backticks:
1 > `Rain fire and destruction upon thy enemies!`
2 // => Rain fire an destruction upon thy enemies!
Template literals let you use both single and double quotes freely without the need to escape them:
1 > `Rain fire n' destruction upon thy "enemies"!`
2 //=> Rain fire n' destruction upon thy "enemies"!
One of the greatest strengths of template literals is that you can inject values in a very straightforward and readable fashion. By using the notation ${target} inside the template literal you can include the value of the variable target in the resulting string. This is also known as string interpolation:
1 let target = 'Sauron', spell = 'balefire'
2 console.log(`Blast ${target} with ${spell}!`)
3 // => blast Sauron with balefire!
4
5 // prior to ES6 we would've needed to write:
6 // 'blast' + target + 'with' + spell
You can include any variable that is accessible within the scope where the template literal is declared. Say goodbye to concatenating strings and variables with the + operator. Bye and farewell!
Additionally, you are not limited to using variables when doing string interpolation. You can use any valid JavaScript expression. For instance, you could call a function:
1 function calculateDamage(modifier){
2 return Math.round(modifier*Math.random())
3 }
4 console.log(`Blast ${target} with ${spell} making
5 ${calculateDamage(10)} damage`)
6 // => Blast Sauron with balefire making 4 damage
Or perform arithmetics:
1 console.log(`1 + 1 is not ${1+1} ===> SYNERGY!!!`);
2 // => 1 + 1 is not 2 ===> SYNERGY!!!
Another great improvement from template literals over vanilla strings are multiline strings. With template literals, if you want to have a multiline string, you just write a multiline string. It’s that easy:
1 let multiline = `I start in this line,
2 and then I go to the next,
3 because there are few things in life,
4 that I like best`
5 console.log(multiline);
6 // => I start on this line,
7 // and then I go to the next...
Tags
Tags are a very interesting feature of template literals. They allow you to customize how a specific template literal gets parsed into a string.
To apply a tag you prepend its name to the template literal:
1 // appreciate how the orcSpeech tag appears before
2 // the string template
3 let clothes = 'boots';
4 let orcLikesBoots = orcSpeech`I like those ${clothes} that you're we\
5 aring!`
In this example we have created a tag orcSpeech to parse any piece of speech into the way orcs speak (who I’ve heard speak numerous times). When we evaluate the resulting string we can verify how the tag has transformed the original text into garbled orc speech:
1 console.log(orcLikesBoots);
2 // => I like thossse bootsss that you'rre wearring!
How did that happen? Well, a tag is merely a function and what you see above is the result of calling that function with the string literal as input.
More specifically, a tag function takes each string literal of a template and each substitution (the ${} tokens) and returns the parsed string after composing literals and substitution together:
1 function orcSpeech(literals, ...substitutions){
2 // do your magic
3 // return parsed string
4 }
In the previous example the literals and substitutions would be:
1 literals => "I like those", " that you're wearing"
2 substitutions => ${clothes}
The implementation of the orcSpeech tag function could look like this:
1 function orcSpeech(literals, ...substitutions){
2 console.log(literals); // => ['I like those ',
3 // ' that you're wearing']
4 console.log(substitutions); // => ['boots']
5
6 let phrase = literals[0];
7 substitutions.forEach(function(s, idx){
8 phrase += `${s}${literals[idx+1]}`
9 });
10
11 return phrase.replace(/s/g, 'sss').replace(/r/g, 'rr')
12 }
Where we compose literals and substitutions and then replace s and r with sss and rr respectively giving the original sentence that rough touch characteristic of orcs, goblins and other creatures of darkness.
1 console.log(orcLikesBoots);
2 // => I like thossse bootsss that you'rre wearring!
When the tag is applied to a template literal like you saw above, the effect is the same as that of calling the tag function with the literals and substitutions. So this down here would be the same:
1 let orcLikesBoots =
2 orcSpeech`I like those ${clothes} that you're wearing!`
As calling orcSpeech as a function:
1 let orcLikesBoots =
2 orcSpeech(['I like those ', " that you're wearing!"], 'boots');
In addition to what you’ve seen thus far, tags also give you the possibility to access the raw string literals. Having access to raw literals lets you customize even how you parse special characters such as end of line /n, tabs /t, etc.
We can illustrate this with an example of a hypothetical orcSpeechRaw tag:
1 function orcSpeechRaw(literals, ...substitutions){
2 console.log(literals.raw); // => ['I like those ',
3 // ' that you're wearing']
4 console.log(substitutions); // => ['boots']
5
6 let phrase = literals.raw[0];
7 substitutions.forEach(function(s, idx){
8 phrase += `${s}${literals.raw[idx+1]}`
9 });
10
11 return phrase.replace(/s/g, 'sss').replace(/r/g, 'rr')
12 }
The literals array exposes a raw property that contains the same information than the literals array but in raw format. If you take a look at the output of the non-raw tag orcSpeech:
1 console.log(
2 orcSpeech`I like those ${clothes}\n\n that you're \twearing!`)
3 // => ["I like those ", "
4 //
5 // that you're wearing!"]
6 // ['boots']
7 // I like thossse bootsss
8 //
9 // that you'rre wearring
You’ll be able to appreciate how the special characters have been transformed into whitespace. If you then take a look at the output of the raw tag orcSpeechRaw:
1 console.log(
2 orcSpeechRaw`I like those ${clothes}\n\n that you're \twearing!`)
3
4 // => ["I like those ", "\n\n that you're \twearing!"]
5 // ["boots"]
6 // "I like thossse bootsss\n\n that you'rre \twearring!"
You’ll see how the special characters are available in the literals.raw array.
In summary, tags give you the opportunity of creating reusable formatting functions or even small text manipulation DSL’s (Domain Specific Languages) for your web applications. For instance, an interesting application of tagged template literals could be building your own HTML templating engine:
1 let inventory = [
2 {name: 'rusty sword', price: 2},
3 {name: 'health potion', price: 10},
4 {name: 'medallion of Valor', price: 300}
5 ];
6
7 console.log(ul`
8 ${inventory.map(function(item){
9 return li`${item.name}: ${item.price} silvers`
10 })}`)
11 // => "<ul>
12 // <li>rusty sword: 2 silvers</li>
13 // <li>health potion: 10 silvers</li>
14 // <li>medallion of Valor: 300 silvers</li>
15 // </ul>"
Where we would create a ul and li tags that would be an extension over the more generic html tag:
1 function html(literals, ...substitutions){
2 let phrase = literals[0];
3 substitutions.forEach(function(s, idx){
4
5 // if array convert to string
6 if (Array.isArray(s)) s = s.join('\n');
7
8 phrase += `${s}${literals[idx+1]}`;
9 // you could also add some special characters processing
10 // for parsing non HTML compliant characters like &
11 });
12 return phrase;
13 }
14
15 function ul(literals, ...substitutions){
16 return `<ul>${html(literals, ...substitutions)}</ul>`;
17 }
18
19 function li(literals, ...substitutions){
20 return `<li>${html(literals, ...substitutions)}</li>`;
21 }
The code when applying the templating engine could be simplified further if we used ES6 arrow functions which we’ll cover in the next chapter. But there’s nothing preventing us from taking a sneak-peek right?
1 ul`${inventory.map(item => li`${item.name}:${item.price} coins`)}`)
Beautiful!
String Cheatsheet
Basics
| Basics | description |
|---|---|
'a string', "a string"
|
Create a string |
"Y'all", 'the "laser"` |
Escape single and double quotes |
"You are: " + status |
Concatenate strings and values using +
|
"lalalala too long" + |
Concatenate for multiline strings |
"lala" |
|
"lalalala too long\ |
Create multiline strings using \ at the end of each row |
lala" |
|
"this is\n a new line" |
Special characters start with \
|
String Methods
| String Methods | description |
|---|---|
concat(str1, str2, ...) |
Concatenate strings |
- "this is".concat(" excellent")
|
- "this is excellent"
|
toUpperCase() |
Uppercase all characters in a string |
- "abracadabra".toUpperCase()
|
- "ABRACADABRA"
|
toLowerCase() |
Lowercase all characters in a string |
- "SHAZAM!".toLowerCase()
|
- "shazam"
|
charAt(position) |
Get character at given position |
- "Just DO IT!".charAt(0)
|
- "J"
|
indexOf(string) |
Gets position of the first occurrence of a string |
- "JUST DO IT!".indexOf("DO")
|
- 5
|
search(regExp) |
Gets position of the first occurrence of a regular expression |
- "JUST DO IT!".search(/DO/)
|
|
match(regExp) |
Find strings matching a regex |
- "JUST DO IT!".search(/DO.*/)
|
- ['DO', 'DO IT']
|
| `replace(str or regex, str) | Find and replace using a string or regex |
- "JUST DO IT!".replace('DO IT!', 'DANCE!')
|
- "JUST DANCE!"
|
split(separator) |
Separate string into an array of items using a separator |
- "JUST DO IT".split(" ")
|
- ["JUST", "DO", "IT"]
|
ES6 String Methods
| String Methods | description |
|---|---|
startsWith(str) |
Check whether a string starts with some text |
- "JUST DO IT".startsWith("JUST")
|
|
endsWith(str) |
Check whether a string ends with some text |
- "JUST DO IT".endsWith("IT")
|
|
includes(str) |
Check whether a string contains some text |
- "JUST DO IT".includes("DO")
|
|
repeat(times) |
Create a new string by repeating the current string N times |
- "Na".repeat(3)
|
- "NaNaNa"
|
ES6 Template Literals
| Template Literals | description |
|---|---|
`new literal` |
Create template literal |
- "new literal"
|
|
`I have ${coins} coins` |
Variable interpolation |
- "I have 10 coints"
|
|
`I have ${1+1} coins` |
Evaluate expressions |
- "I have 2 coins"
|
|
li`purse` |
Use of tags |
- "<li>purse</li>"
|
Concluding
And we got to the end! In this chapter you learned a ton about strings in JavaScript, you saw how strings are a primitive type which behave in a very similar way to C# strings. They are immutable and performing operations on them or changing them in some fashion results in a new string being created.
Strings behave like array-like objects and although they don’t have array methods, you can apply the non-destructive array methods on them by virtue of using call and apply. In addition to this, strings have a lot of methods that you can use to perform operations like concatenation, matching, searching, extracting and replacing pieces of text.
ES6 brings new string methods like startsWith and endsWith but more importantly it comes with a new way to work with strings in the form of template literals. Template literals are a great improvement over vanilla strings because they let you inject values within strings in a very straightforward fashion and make creating multiline strings seamless. Tags are a great companion to template literals that let you customize how a template literal is parsed into a string opening an endless world of possibilities when it comes to text manipulation in JavaScript.
Exercises
Upgrading Your Everyday JavaScript Magic With ES6 - Destructuring
There's always a better way
to solve a problem
You just haven't found it...
...yet
- Torvik Knivsa
Alchemist
Welcome to the Future! ECMAScript 6
Welcome back JavaScriptmancer! So far in this first part of the book you’ve seen several great ES6 features:
- In The Basics Of JavaScript Functions you learned about the new
letandconstkeyword that let you declare block scoped variables. - In Useful Function Patterns: Default Parameters you saw how ES6 brings default parameters to JavaScript. They have a very similar syntax to C# and let you use any valid expression as a default value, even objects and functions.
- In Useful Function Patterns: Multiple Arguments you found out about rest parameters. They work just like C#
paramsand let you define functions that take an arbitrary number of arguments. - In On the Art of Summoning Servants and Critters, Or Understanding The Basics of JavaScript Objects you discovered how ES6 simplifies object initializers with the shorthand syntax (both for properties and methods). You also learned about ES6 symbols and how they can help you with data privacy.
- In A Guide to Strings, Finding the Right Words and Proper Spell Intonation you dived into ES6 Template Literals and learned how they revolutionize the way we work with strings in JavaScript. You also took a look at tags and the new string methods.
In this and the upcoming chapters we will introduce other great ES6 features that you can use in your everyday JavaScript: destructuring, arrow functions and the spread operator. It’s time to upgrade your JavaScript wizardry to ES6!
Destructure All The Things!
ES6 comes with a very handy new feature called destructuring. Destructuring lets you extract parts of information from within objects and other data structures in a very natural and concise manner.
Destructuring Objects
In the simplest of use cases, you can initialize variables from properties of objects using the following syntax:
1 let pouch = {coins: 10};
2 let {coins} = pouch;
This is equivalent to:
1 let coins = pouch.coins;
Using destructuring in a real world example is not very different. Imagine that you have a cimmerian barbarian that is yearning to improve his JavaScript skills. Let’s call him conan:
1 let conan = {
2 firstName: 'Conan',
3 lastName: 'the barbarian',
4 height: 178,
5 weight: 90,
6 email: 'conan.thecimmerian@akilonia.com',
7 toString() {
8 return this.firstName;
9 }
10 };
We can create a javascriptmancyCourse object that allows anyone, not just barbarians, to learn some javascript by using a helpful signUp method:
1 let javascriptmancyCourse = {
2 signUp(person){
3 let {firstName, lastName} = conan;
4 console.log(`Thank you ${firstName}, ${lastName}!!
5 You've successfully signed up to our very special JavaScript course!
6 Welcome and prepare to learn some JavaScript!`);
7 }};
8
9 javascriptmancyCourse.signUp(conan);
10 // => Thank you Conan, the barbarian!!
11 // You've succesfully signed up to our very special
12 // JavaScriptmancy course!
13 // Welcome and prepare to learn some JavaScript!
The let {firstName, lastName} = conan lets us extract the information necessary from the conan object and have it ready for processing in a terse single statement. If you appreciate writing beautiful code it doesn’t get better than this.
And there’s more! You are not limited to using variables that have exactly the same names than the properties within the original object. You can take advantage of a slightly more advanced destructuring syntax to map an object property to a different variable:
1 let { lastName:title } = conan;
This is equivalent to:
1 let title = conan.lastName;
So that lastName is the name of the origin property and title is the name of the destination variable. Additionally, if you try to extract a property that doesn’t exist in the source object, the newly created variable will be undefined:
1 // let pouch = {coins: 10};
2 let {bills} = pouch;
3 console.log(bills);
4 // => undefined
To prevent this from happening you can use default values in tandem with destructuring syntax. With this powerful combination, if an object doesn’t have a given property, your variable still gets a default value instead of undefined.
1 // let pouch = {coins: 10};
2 let {bills=10} = pouch;
Using defaults with destructuring as depicted above will ensure that you’ll never be poor and enjoy those illusory 10 bills even when they are not in your pouch. Magic!
Yet another mighty feature of destructuring is the ability to extract properties that are deep within an object. Imagine that you have one of those useful bags that have infinite pockets and you want to reach for your tobacco pouch:
1 let bag = {
2 leftPocket: {
3 tobaccoPouch: ['pipe', 'tobacco']
4 },
5 rightPocket: [pouch],
6 interior: ['10 pieces of dried meat', 'toilet paper', 'leprechau\
7 n']
8 };
Well you can create a new tobbacoPouch variable using destructuring. It’s this easy:
1 let {leftPocket: {tobbacoPouch}} = bag;
2
3 console.log(`Let's see what I've got in my tobaccoPouch:
4 ${tobaccoPouch}`);
5 // => Let's see what I've got in my tobaccoPouch: pipe,tobacco
You can read this like go into the leftPocket grab the tobaccoPouch and put it in its own separate variable.
Again if the property you are trying to extract doesn’t exist you’ll get undefined:
1 let {leftPocket: {secretPouch}} = bag;
2 console.log(`Let's see what I've got in my secret pouch:
3 ${secretPouch}`);
4 // => Let's see what I've got in my secret pouch: undefined
But beware, because if there is a missing property in the object graph on the way to the specific property you want, the destructuring will result in a SyntaxError:
1 let {centralPocket: {superSecretPouch}} = bag;
2 // => SyntaxError:
3 // Cannot read property 'superSecretPouch' of undefined
In this previous example the centralPocket property doesn’t exist in the bag object. This means that the JavaScript runtime cannot traverse the object to get to the superSecretPouch and thus you get the SyntaxError as a result.
And now that we’re talking about errors and problems, let’s take a look at something that you cannot do with destructuring, something that has bitten me repeatedly: Extracting a property into an existing variable. No, this here won’t work:
1 // let pouch = {coins: 10};
2 let money = 0;
3 {money} = pouch;
Yes, I know… But don’t be sad. The happy news is that you can also use all you’ve learned thus far with arrays! Wiii! Let’s destructure!
Destructuring Arrays
Destructuring arrays is just as easy as destructuring objects. Instead of using curly braces {} though, you’ll use the more familiar array square brackets []:
1 let [one, two, three] = ['goblin', 'ghoul', 'ghost', 'white walker'];
2 console.log(`one is ${one}, two is ${two}, three is ${three}`)
3 // => one is goblin, two is ghoul, three is ghost
In this example you see how we extract the first three elements of the array and place them in three distinct variables: one, two and three. The fourth element in the array, the scary white walker remains unreferenced by any variable.
You can also jump places within the array:
1 let [firstMonster, , , fourthMonster] =
2 ['goblin', 'ghoul', 'ghost', 'white walker'];
3 console.log(`the first monster is ${firstMonster}, the fourth is
4 ${fourthMonster}`)
5 // => one is goblin, two is ghoul, three is ghost
Destructuring arrays comes very handy when you want to get the first element of an array in a very readable and intuitive fashion:
1 let [first] = ['goblin', 'ghoul', 'ghost', 'white walker'];
2 console.log(`first is ${first}`)
3 // => first is goblin
Which you can combine with the rest operator like this:
1 let [first, ...rest] = ['goblin', 'ghoul', 'ghost', 'white walker'];
2 console.log(`first is ${first} and then go all the rest: ${rest}`)
3 // => first is goblin and then go all the rest ghoul, ghost,
4 // white walker
Unfortunately this trick doesn’t work for the last element of the array because the rest operator is greedy. It wants to extract all items in the array and therefore [...initialOnes, last] wouldn’t do the job.
You could be a super crafty fox and do the following:
1 let [last] = Array
2 .from(['goblin', 'ghoul', 'ghost', 'white walker'])
3 .reverse();
4 console.log(`last is ${last}`);
5 // => last is whiteWalker
Beautiful21! Another use case for array destructuring is to swap the values of two variables:
1 console.log(`first is ${first}, last is ${last}`);
2 // => first is goblin, last is white walker
3
4 [first, last] = [last, first];
5
6 console.log(`but wait! Now first is ${first}, last is ${last}`)
7 // => but wait! Now first is white walker, last is goblin
Finally, you can enjoy the versatile defaults when performing array destructuring as well:
1 let [aMonster, anotherMonster, yetAnotherMonster='cucumber']
2 = ['goblin', 'ghoul'];
3 console.log(`We've got a monster that is a ${aMonster},
4 another that is ${anotherMonster}, and yet another one
5 that is a ${yetAnotherMonster}`);
6 // => We've got a monster that is a goblin, another that
7 // is ghoul, and yet another one that is a cucumber
Because we try to extract three elements from an array that only has two the yetAnotherMonster variable would get a value of undefined. The use of a default prevents that and ensures that the variable has a safe value of cucumber. Because nothing speaks of safety like a cucumber.
Destructuring Function Arguments
In Useful Function Patterns: Default Parameters you briefly saw how destructuring can be useful when used within the parameter list of a function.
1 // With destructuring we can unpack the direction from
2 // the incoming object and use it right away
3 let randalf = {
4 toString(){ return 'Randalf the Mighty'; },
5 castIceCone(mana, {direction}){
6 console.log(`${this} spends ${mana} mana
7 and casts a terrible ice cone ${direction}`);
8 }
9 };
10
11 let options = { direction: 'towards Mordor'}
12 randalf.castIceCone(10, options);
13 // => Randalf the Mighty spends 10 mana and
14 // casts a terrible ice cone towards Mordor
The principle is the same but the destructuring process happens in a more indirect fashion. One piece of the destructuring is the object being passed as an argument to a method, in this case options:
1 randalf.castIceCone(10, options);
And the other bit is the destructuring syntax with the specific variables as part of the method signature {direction}:
1 castIceCone(mana, {direction}){
Just like normal destructuring it also works with arrays:
1 function castMiniIceCone(mana, [target, ...others]){
2 var caster = this || 'God almighty';
3 console.log(`${caster} spends ${mana} mana
4 and casts a super teeny tiny ice cone that only reaches
5 ${target} but misses ${others} because it is so tiny and cute`);
6 }
7 randalf.castMiniIceCone = castMiniIceCone;
8 randalf.castMiniIceCone(10, ['giant', 'troll', 'death knight']);
9 // => Randalf the Mighty spends 10 mana
10 // and casts a super teeny tiny ice cone that only reaches
11 // giant but misses troll,death knight because it is so
12 // tiny and cute
And you can use any of the features you’ve learned in this section for both object and array parameters: defaults, nested properties, jumping over array items, etc.
Concluding
In this chapter we did a brief review of all the ES6 features that you’ve learned so far: let, const for block scoped variables, default parameters, rest parameters which work like C# params, shorthand syntax for object initializers, symbols, template literals, tags and the new string methods.
We also dived deeper into destructuring and learned how you can use this new feature to easily extract properties from objects and items from arrays. You discovered that you can use destructuring within the arguments of a function and how to combine destructuring and default values when the property you are trying to extract doesn’t exist within an object or array.
Exercises
Upgrading Your Everyday JavaScript Magic With ES6 - Arrow Functions
Speak less.
That way there's less chance
that you'll say something stupid.
Think about that,
when you craft your next spell.
- Kyeich Chir
Guardian of the word
Behold! The Arrow Function!
Arrow functions are one of my favorite features in ES6. They give you a beautiful and terse way to write JavaScript functions which is reminiscent of C# lambda expressions.
Here you have a vanilla JavaScript function expression:
1 let createWater = function (mana){
2 return `${mana} liters of water`;
3 }
And here you have an equivalent version as an arrow function:
1 let createWater = mana => `${mana} liters of water`;
If you call any of these functions you’ll get the same result:
1 console.log(createWater(10));
2 // => 10 liters of water
Let’s examine the arrow function in closer quarters. You may have noticed that it doesn’t have any return statement. And that’s because in its simplest incarnation, the arrow function has an implicit return that returns whichever expression is to the right of the fat arrow =>. Note that this notation is only valid when an arrow function has a single statement.
Like in C#, you’ll often see arrow functions used in conjunction with array methods such as filter (the JavaScript version of LINQ’s Where):
1 let monsters = ['orc chieftain', 'orc grunt', 'small orc', 'goblin'];
2 let orcs = monsters
3 .filter(m => m.includes('orc'));
4 console.log(orcs);
5 // => ["orc chieftain", "orc grunt", "small orc"]
You can define arrow functions with any arbitrary number of arguments. For instance, you can have no arguments at all:
1 let helloMiddleEarth = () => "hello Middle Earth!";
2
3 console.log(helloMiddleEarth());
4 // => hello Middle Earth!
Or one:
1 let frodo = {
2 toString(){ return 'Frodo'},
3 destroyTheOneRing() {
4 console.log(`${this} throws the one ring into the entrails of Mo\
5 unt Doom`);
6 },
7 hideFrom(enemy, how) {
8 console.log(`${this} hides from the ${enemy} ${how}`);
9 }
10 };
11
12 let destroyDaRing = (hobbit) => hobbit.destroyTheOneRing();
13
14 destroyDaRing(frodo);
15 // => Frodo throws the one ring into the entrails of Mount Doom
Two:
1 let nazgul = {
2 toString(){ return 'scary nazgul';}
3 };
4 let useElvenCloak = (hobbit, enemy)
5 => hobbit.hideFrom(enemy, 'with an elven cloak');
6
7 useElvenCloak(frodo, nazgul);
8 // => Frodo hides from the scary nazgul with an elven cloak
Or as many arguments as you want using the rest syntax:
1 useElvenCloak = (hobbit, ...enemies)
2 => hobbit.hideFrom(enemies, 'with an elven cloak');
3 useElvenCloak(frodo, nazgul, 'orc', 'troll');
4 // => Frodo hides from the scary nazgul,orc,troll with an elven cloak
Because they are just functions you can also use defaults:
1 destroyDaRing = (hobbit=frodo) => hobbit.destroyTheOneRing();
2 destroyDaRing();
3 // => Frodo throws the one ring into the entrails of Mount Doom
And destructuring:
1 let companyOfTheRing = {
2 smartestHobbit: frodo,
3 wizard: 'Gandalf',
4 ranger: 'Aragorn',
5 // etc
6 };
7 destroyDaRing =
8 ({smartestHobbit}) => smartestHobbit.destroyTheOneRing();
9
10 destroyDaRing(companyOfTheRing);
11 // => Frodo throws the one ring into the entrails of Mount Doom
If the body of your arrow function has more than one statement then you’ll need to wrap it inside curly braces just like you would do in C#:
1 let eatRation = (hobbit, rations) => {
2 let ration = rations.shift();
3 if (ration) {
4 hobbit.hp += ration.hp;
5 console.log(`${hobbit} eats ${ration} and ` +
6 `recovers ${ration.hp} hp`);
7 } else {
8 console.log(`There are no rations left! We're all gonna die!!`);
9 }
10 }
11
12 let rations = [{
13 name: 'sandwich',
14 hp: 5,
15 toString(){ return this.name;}
16 }];
17
18 eatRation(frodo, rations);
19 // => Frodo eats sandwich and recovers 5 hp
Additionally, when you have more than one statement you’ll need to return a value explicitly:
1 let carveWood = (wood, shape) => {
2 console.log(`You carve a piece of ${wood} into a ${shape}`);
3 return {name: shape, material: wood};
4 }
5 let pipe = carveWood('oak', 'pipe');
6 // => You carve a piece of oak into a pipe
An arrow function can also return an object via the object initializer syntax. When doing so, you’ll need to wrap it inside parentheses. That way the JavaScript runtime will be able to understand that it is an object and not a block of code:
1 let createHealthPotion = () => ({
2 name: 'potion of health',
3 hp: 10,
4 toString(){
5 return `${this.name} (+${this.hp}hp)`;
6 }});
7 let healthPotion = createHealthPotion();
8 console.log(healthPotion.toString());
9 // => potion of Health (+10 hp)
In summary, arrow functions are awesome. Using arrow functions you’ll be able to write much terser code and still get to use features like destructuring or defaults. But as functions themselves they are a little bit special, and when I say a little I mean a lot.
Arrow Functions Arcana
Indeed, though they seem like regular functions, arrow functions have their quirks:
- They don’t have
this - They don’t have an
argumentsobject - You cannot use
bind,applyandcallto set the context in which they are evaluated - You cannot use
newnorsuper
This may be surprising but if you take a look at the ECMA-262 specification22, that is, JavaScript’s own specification, you’ll read the following:
14.2.16 Arrow Functions - Runtime Semantics: Evaluation
An ArrowFunction does not define local bindings for
arguments,super,this, ornew.target. Any reference toarguments,super,this, ornew.targetwithin an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.
But what does it exactly mean for arrow functions not to have their own version of this nor arguments?
It means that when you refer to this or arguments within an arrow function you are actually referring to this or arguments in the enclosing environment. Let’s clarify this with an example.
Let’s say that we have gollum that wants to be pleasant to you just before he stabs you in the back and steals your wedding ring. If we use normal functions to define his greetings:
1 let gollum = {
2 name: 'Golum! Golum!',
3 toString(){ return `${this.name}!!!`;},
4 saysHi(){
5 console.log(`Hi! I am ${this}`);
6 setTimeout(function(){
7 console.log(`${this} stabs you in the back and
8 steals your wedding ring while saying 'My Preciouuuuuus'`)
9 },/*waitPeriodInMilliseconds*/ 500);
10 }
11 };
And then call the function saysHi:
1 // call it in the context of the gollum object
2 gollum.saysHi();
3 // => Hi! I am Gollum! Gollum!!!!
4 // => "[object Window] stabs you in the back and
5 // steals your wedding ring while saying 'My Preciouuuuuus'"
As we expected, gollum happily salutes us. Then, after a short while, he returns and stabs us in the back. The only problem being that it is no longer gollum but the Window object. This is nothing new. We learned about this strange behavior of this in Mysteries of the JavaScript Arcana. But what happens if we use an arrow function instead of a normal function?
1 // what happens if we use an arrow function instead?
2 let gollumWithArrowFunctions = {
3 name: 'Golum! Golum!',
4 toString(){ return `${this.name}!!!`;},
5 saysHi(){
6 console.log(`Hi! I am ${this}`);
7 setTimeout(() =>
8 console.log(`${this} stabs you in the back and
9 steals your wedding ring while saying 'My Preciouuuuuus'`)
10 ,/*waitPeriodInMilliseconds*/ 500);
11 }
12 };
Notice how we have rewritten the saysHi function to use an arrow function within setTimeout instead of a normal function. If we call it:
1 gollumWithArrowFunctions.saysHi();
2 // => Hi! I am Gollum! Gollum!!!!
3 // => Golum! Golum!!!! stabs you in the back and
4 // steals your wedding ring while saying 'My Preciouuuuuus'
The arrow function guarantees that the right version of this is used. What is happening? Because the arrow function doesn’t have its own version of this it accesses the this defined by the saysHi method (effectively behaving like a closure). Because saysHi was called using the dot notation gollumWithArrowFunctions.saysHi then the object itself is the value of this and thus everything works yey! And we die an ignominious death at Gollum’s hands. No!
What are the consequences of this? Well, the most exciting consequence of an arrow function not having its own this is that it makes them more resistant to the this problems you saw in Mysteries of the JavaScript Arcana (they are more resistant but not bullet proof as you’ll see later in this chapter).
Let’s bring this concept home with another example, the same one we used in Mysteries of the JavaScript Arcana:
1 function UsersCatalogJQuery(){
2 "use strict";
3 var self = this;
4
5 this.users = [];
6 getUsers()
7
8 function getUsers(){
9 $.getJSON('https://api.github.com/users')
10 .success(function updateUsers(users){
11 // console.log(users);
12 // console.log(this);
13 try {
14 this.users.push(users);
15 } catch(e) {
16 console.log(e.message);
17 }
18 // BOOOOOOOM!!!!!
19 // => Uncaught TypeError:
20 // Cannot read property 'push' of undefined
21 // 'this' in this context is the jqXHR object
22 // not our original object
23 // that's why we usually use a closure here instead:
24 // self.products = products;
25 });
26 }
27 }
28 var catalog = new UsersCatalogJQuery();
Again, if you take advantage of arrow functions and substitute the updateUsers function expression for an arrow function you’ll solve the problem. And in a more elegant way than binding the function explicitly (with bind) or using a closure (var self = this). You can appreciate that elegance in this example below:
1 function UsersCatalogJQueryArrowFunction(){
2 "use strict";
3 this.users = [];
4 this.getUsers = function getUsers(){
5 $.getJSON('https://api.github.com/users')
6 .success(users => this.users.push(users)); // arrow function
7 // this is mostly equivalent to:
8 // .success(function(users){
9 // return this.users.push(users);}.bind(this))
10 };
11
12 this.getUsers();
13 }
14 var catalog = new UsersCatalogJQueryArrowFunction();
Arrow Functions And This Gotchas
Now that we’ve learned about the good parts of the arrow function and how it can help us write terser code and avoid some problems with the this keyword let’s take a look at its darker sides: when an arrow function doesn’t behave like a function.
Let’s start with the not having this and when it can become a problem.
Beware of Using Arrow Functions With Object Literals
My first thought when I learned about arrow functions was: Awesome! Now I can use arrow functions everywhere! And so I wrote this:
1 let raistlin = {
2 name: 'Raistlin',
3 toString(){ return this.name;},
4 deathRay: () =>
5 console.log(`${this} casts a terrible ray of deaaaath!`)
6 }
To my surprise, when I tried to have raistlin cast an evil death ray this is what happened:
1 raistlin.deathRay();
2 // => [object Window] casts a terrible ray of deaaaath!
The arrow function, which we had thought impervious to this problems before, now reveals us the classic this issue.
What is happening here? Well, if you remember from the previous section arrow functions don’t have this. Because of that, when you try to access this inside an arrow function you’re referring to the this of the outer scope. In the case of an object literal, the this from the outer scope is no other than the Window object. Again, the rules we learned about this in Mysteries of the JavaScript Arcana don’t apply to arrow functions and calling an arrow function using the dot notation doesn’t evaluate it in the context of the object. So remember, be wary of using arrow functions as properties of object literals.
You Can’t Bind Arrow Functions
You cannot use bind with arrow functions. If you are brave enough to try to bind an arrow function to an object you’ll be sorely disappointed because it won’t work.
Let’s illustrate it with an example:
1 let saruman = {
2 name: 'Saruman, the White',
3 toString(){ return this.name;},
4 raiseUrukhai(){
5 console.log(`${this} raises a Urukhai from the pits ` +
6 ` of Isengard`);
7 return {name: 'Uruk', hp: 500, strength: 18};
8 },
9 telekineticStaffAttack: () =>
10 console.log(`${this} uses his staff to throw
11 you across the room "telekinetically" speaking`)
12 }
Behold saruman! Another epic javascriptmancer. He has a couple of methods that show his magic prowess. One raiseUrukhai is a regular function, the other telekineticStaffAttack uses an arrow function. If we call these methods using the dot notation:
1 saruman.raiseUrukhai();
2 // => Saruman, the White raises a Urukhai from the pits of Isengard
3
4 saruman.telekineticStaffAttack();
5 // => [object Window] uses his staff to throw
6 // you across the room "telekinetically" speaking
7 // this would be undefined instead of Window if we used
8 // strict mode
Again, just like we saw in the previous section, when we call telekineticStaffAttack method the this gets evaluated as Window. But let’s say that we want to solve it as we are accustomed to by using bind.
If we use bind to bind these two methods to a different object:
1 // if we try to bind these two methods to a new object
2 let boromir = {name: 'Boromir of Gondor',
3 toString(){return this.name;}};
4 let raiseUrukhaiBound = saruman.raiseUrukhai.bind(boromir);
5 raiseUrukhaiBound();
6 // => Boromir of Gondor raises a Urukhai from the pits of Isengard
We can appreciate how we can bind a normal function but when we try to bind an arrow function nothing happens:
1 let telekineticStaffAttackBound =
2 saruman.telekineticStaffAttack.bind(boromir);
3
4 telekineticStaffAttackBound();
5 // => undefined uses his staff to throw
6 // you across the room "telekinetically" speaking
7 // didn't work, not telekinetic staff attack for Boromir
Since an arrow function doesn’t have this it makes no sense to bind it.
Even though arrow functions are not the same as bound functions, once an arrow function is declared and encloses its nearest this it pretty much behaves in the same way as a bound function. Let’s illustrate this idea with some code:
1 // this is a constructor function
2 let Warg = function(name, size){
3 this.name = name;
4 this.size = size;
5 this.name = '${name}, the ${size} warg`;
6 // wargs don't bark, they wark
7 this.wark = () => console.log(`${name} warks!: Wark! Wark!`);
8 this.jump = (function(){
9 console.log(`${name} jumps around`);
10 }).bind(this);
11 }
12
13 // here we are creating a new object using the new operator
14 let willyTheWarg = new Warg('willy', 'litte');
In this example we are using a constructor function and the new keyword to instantiate a fiery warg. Even though we haven’t seen any of these concepts yet because I am reserving them for the OOP section of the series, they are still the best way to exemplify the similar behavior of arrow functions and bound functions (I hope you’ll forgive me).
Essentially you use the new keyword to instantiate new objects via constructor functions. When you apply the new keyword on any function the JavaScript runtime instantiates an object {}, sets it as the this value of the function, then evaluates the function and finally returns it. This is useful because it is the this value that the wark method is going to enclose and safeguard for the rest of the program execution.
After creating willyTheWarg we got ourselves an arrow function wark and a bound function jump. If we execute any of them we will be able to appreciate how this refers to the warg itself:
1 // this is an arrow function
2 willyTheWarg.wark();
3 // => willy, the litte warg warks!: Wark! Wark!
4
5 // and this is the bound function
6 willyTheWarg.jump();
7 // => willy jumps around
This is the expected behavior, but what happens if we are mean and take these functions away from willyTheWarg?
Well the bound function, as we learned in Mysteries of the JavaScript Arcana, will still have willyTheWarg as its context:
1 let jump = willyTheWarg.jump;
2 jump();
3 // => willy, the litte warg warks!: Wark! Wark!
4
5 let goblin = {jump: jump};
6 goblin.jump();
7 // => willy, the litte warg warks!: Wark! Wark!
And the arrow function behaves in exact the same way. Instead of being explicitely bound to willyTheWarg it is implicitly bound by the closure over the this variable:
1 let wark = willyTheWarg.wark;
2 wark();
3 // => willy, the litte warg warks!: Wark! Wark!
4
5 goblin.wark = wark;
6 goblin.wark();
7 // => willy, the litte warg warks!: Wark! Wark!
This similar behavior and the fact that neither bound nor arrow functions can be bound (re-bound in the case of the bound function) makes both types of function practically identical in this situation. The only difference being that bound functions don’t need a closure, you can just bind a normal function to whatever object you want by just calling the bind method. Arrow functions, on the other hand, can be seen to be implicitly bound to their enclosing context by virtue of the closure.
You Can’t Use Apply or Call on Arrow Functions
In addition to bind, you cannot use call nor apply on an arrow function to change its context. If you remember Mysteries of the JavaScript Arcana, you can use call and apply to explicitly set the context in which a function is executed, that is, the value of this:
1 let caragor = {toString(){return 'scary caragor';}}
2 let howl = function({times}){
3 console.log(`${this} howls to the moon ${times} times!`);
4 }
5 // a normal function let's you set its context explicitly via apply \
6 or call
7 howl.apply(caragor, [{times: 3}]);
8 // => scary caragor howls to the moon 3 times!
9 howl.call(caragor, {times: 4});
10 // => scary caragor howls to the moon 4 times!
But if you try to use either apply or call with an arrow function, the context that you pass as argument will be completely ignored:
1 // an *arrow function* completely ignores the value of `this` passed\
2 as argument
3 willyTheWarg.wark.apply(caragor);
4 // => willy, the litte warg warks!: Wark! Wark!
5 willyTheWarg.wark.call(caragor);
6 // => willy, the litte warg warks!: Wark! Wark!
In the example above you can easily appreciate how instead of scary caragor the ${this} within the wark arrow function is evaluated as willy, the little. This demostrates how arrow functions ignore the context when called with either call or apply.
Arrow Functions Don’t Have Arguments Object
Another interesting feature of arrow functions is that they don’t have arguments object. Just like with this if you attempt to access the arguments object within an arrow function you’ll access the arguments of the enclosing environment.
If you remember More Useful Function Patterns - Multiple Arguments every function in JavaScript has a arguments object that you can use to access which arguments where passed to a function. So if you have a normal function that logs the arguments object:
1 function rememberWhatISaid(){
2 console.log(`you said: ${Array.from(arguments).join(', ')}`);
3 }
You can easily demonstrate how the arguments object collects those arguments being passed to the function:
1 rememberWhatISaid('hello', 'you', 'there');
2 // => you said: hello, you, there
3 rememberWhatISaid('supercalifragilisticusespialidosus')
4 // => you said: supercalifragilisticusespialidosus
Not so with arrow functions:
1 let forgetWhatISaid = () => {
2 console.log(`I am going to forget that you said: ${arguments}`);
3 }
4 forgetWhatISaid('I said Wazzaaaaa');
5 // => error ReferenceError: arguments is not defined
The arguments variable is not defined and thus we get a ReferenceError. Let’s define it and see what happens:
1 let arguments = ['trying something crazy'];
2 let forgetWhatISaid = () => {
3 console.log(`I am going to forget that you said: ${arguments}`);
4 }
5 forgetWhatISaid('I said Wazzaaaaa');
6 // => I am going to forget that you said: trying something crazy
We can also make the same experiment wrapping an arrow function inside another function:
1 let createMemoryWisp(){
2 return () => console.log(`*MemoryWisp*: You said...
3 ${Array.from(arguments).join(', ')}`);
4 }
5 let wispRememberThis = createMemoryWisp(1, 2, 3, 4, 'banana!');
6 wispRememberThis('important password', '123456789');
7 // => *MemoryWisp*: You said... 1, 2, 3, 4, banana!
So as you can see in both these examples, arrow functions don’t have their own arguments object and use the arguments object of their enclosing environment. But What if we want to send an arbitrary number or arguments to an arrow function? Well, in that case you should use the rest operator:
1 function createMemoryWispWithRest(){
2 return (...thingsToRemember) =>
3 console.log(`*MemoryWisp*: You said... ${thingsToRemember.jo\
4 in(', ')}`);
5 }
6 let wispRememberThisAgain = createMemoryWispWithRest();
7 wispRememberThisAgain('important password', '123456789');
8 // => *MemoryWisp*: You said... important password, 123456789
In summary, whenever you use arguments inside an arrow function you’ll be accessing the enclosing environment’s arguments object. So if you want to send multiple arbitrary arguments to an arrow function use the rest operator.
Arrow Functions and the New and Super Operators
The new and super operators are two operators that we will see in depth in the OOP section of the series. The new operator lets you create new instances of objects when applied to any function which will then act as a constructor. The super keyword is new in ES6 and lets you access methods in parent classes within an inheritance chain.
In much the same way as with bind, call and apply, you cannot use new nor super with an arrow function.
Concluding
In this chapter you learned about ES6 arrow functions which resemble lambdas in C#. Arrow functions let you use a terser syntax than the normal function syntax and help you avoid problems with the this keyword by using the this value of their enclosing environment.
Arrow functions are a little bit special in what regards to this since they are the only functions in JavaScript that don’t have their own this value. Because of this characteristic you cannot use bind, call or apply to specify the context in which an arrow function will be evaluated. In a similar fashion you cannot use the new and super operators with an arrow function.
Additionally, arrow functions don’t have their own arguments object, if you try to access arguments inside an arrow function you’ll access the arguments object within the enclosing function. This means that if you want for an arrow function to take an arbitrary number of arguments you’ll need to use the rest syntax.
Exercises
Upgrading Your Everyday JavaScript Magic With ES6 - The Spread Operator
Learn to recognize beauty in code,
it will make the task of coding
a pleasure in itself,
it will make you appreciate code
in a whole different way.
- Zazongel emjia
Bard and Poet
Ready To Spread Your Wings?
In More Useful Function Patterns: Multiple Arguments you learned about rest parameters, a new ES6 feature, that lets you define functions with an arbitrary number of arguments just like params in C#.
The spread operator works sort of in an opposite way to the rest operator. Where the rest operator takes a variable number of arguments and packs them into an array, the spread operator takes and array and expands it into its compounding items.
In this chapter you’ll see several recipes that will help you write better code by taking advantage of the spread operator like, for instance, concatenating arrays.
Use the Spread Operator to Seamlessly Concatenate Arrays
You can use the spread operator to easily concatenate arrays with each other. Let’s say that we want to collect our most terrible enemies for later reference. We have an array knownFoesLevel1 and another array newFoes with newly acquired enemies (because you can never have enough enemies):
1 let knownFoesLevel1 = ['rat', 'rabbit']
2 let newFoes = ['globin', 'ghoul'];
Since it’s easier to manage one collection than two, we want to merge these two collections into one single array. Where you would have used the concat method in ES5:
1 let knownFoesLevel2 = knownFoesLevel1.concat(newFoes);
2 console.log(knownFoesLevel2);
3 // => ["rat", "rabbit", "globin", "ghoul"]
In ES6 you can use the spread operator to achieve the same result with a much clearer syntax:
1 let knownFoesLevel2WithSpread = [...knownFoesLevel1, ...newFoes];
2 console.log(knownFoesLevel2WithSpread);
3 // => ["rat", "rabbit", "globin", "ghoul"]
You can even mix arrays and singular items:
1 let undead = ['zombie', 'banshee', 'vampire', 'skeleton'];
2 let knownFoesLevel3 = [...knownFoesLevel2, 'troll', 'orc',
3 ...undead];
4 console.log(knownFoesLevel3);
5 // => ["rat", "rabbit", "globin", "ghoul", "troll",
6 // "orc", "zombie", "banshee", "vampire", "skeleton"]
Easily Use Apply With the Spread Operator
Another useful use case of the spread operator is as an alternative syntax to Function.prototype.apply.
In Mysteries of the JavaScript Arcana you learned about how you can use the apply function to explicitly set the context (this) in which a function is executed. You also learned how apply expects an array of arguments as second parameter and how when the function is finally invoked each element within the array is passed as a separate argument to the original function.
Well the spread operator let’s you call an arbitrary function with an array of arguments in a better way than apply does.
Let’s say that you are working on a spell to command your minions with random actions because being too predictive is boring and you appreciate the wild factor. You express these random actions as arrays: ['minion1', 'action', 'minion2']
1 let action = ['hobbit', 'attacks', 'rabbit'];
Now let’s say that you have a function of your own device where you want actions to be done viciously (looks like you are in a foul mood today):
1 function performActionViciously(agent1, action, agent2){
2 console.log(`${agent1} ${action} ${agent2} viciously`);
3 }
Because the action is expressed as an array but the performActionViciously function expects a separate series of arguments you need a way to adapt these two disparate elements.
Prior to ES6 you would have used the apply function:
1 performActionViciously.apply(/* this */ null, action);
2 // => hobbit attacks rabbit viciously
Where you would need to fill in the context in which the function will be executed for the apply method to work (that is, the value of this).
With ES6 you can use the spread operator to easily perform an action:
1 // let action = ['hobbit', 'attacks', 'rabbit'];
2 performActionViciously(...action);
3 // => hobbit attacks rabbit viciously
No need to set the context in which the function is executed and the resulting code is much concise with the omission of apply.
Now you may be asking yourself: Why don’t I make the performActionViciously function take an array as argument and forget all this spread operator nonsense? Well, you could do that. But what happens when you have no control over the function being called?
Take console.log. Imagine that, instead of performing these actions viciously, you just want to log them. Because console.log takes an arbitrary number of arguments and you have an array, you need some way to adapt the array to the expected signature. Again, prior to ES6 you would use apply:
1 // console.log expects something like this
2 // console.log(a1, a2, a3, a4, etc)
3 console.log.apply(/* this */ console.log, action);
4 // => 'hobbit', 'attacks', 'rabbit'
With ES6 and the spread operator you can simplify the code sample above greatly:
1 console.log(...action);
2 // => 'hobbit', 'attacks', 'rabbit'
Another example in which the spread operator comes handy is when we want to extend an existing array with another array. In the olden days we would have written:
1 let anotherAction = ['jaime', 'cleans', 'the dishes'];
2 let moreThingsToClean = ['the toilet', 'the hut', 'the stables'];
3 Array.prototype.push.apply(anotherAction, moreThingsToClean);
4 console.log(anotherAction);
5 // => ['jaime', 'cleans', 'the dishes', 'the toilet',
6 // 'the hut', 'the stables'];
With the spread operator it’s as easy as:
1 anotherAction.push(...moreThingsToClean);
In summary, do you have some variable as an array and need to apply it to a function that takes separate arguments? Use the spread operator.
Converting Array-likes and Collections Into Arrays
Another interesting application of the spread operator is to convert array-like objects into arrays.
If you remember More Useful Function Patterns: Multiple Arguments, array-like objects are a special type of object that can be indexed, enumerated, has a length property but doesn’t have any of the methods of an array. Some examples of array-like objects are the arguments object inside functions or the list of DOM23 nodes that result when using document.querySelector.
Let’s imagine that we have a web-based user interface, a form, to help us create minions based on some characteristics that we can type manually (for even wizards can benefit from web interfaces). It could look like this:
1 <form action="post" id="minion">
2 <label for="name">Name:</label>
3 <input type="text" name="name" value="Orc">
4
5 <label for="class">Class:</label>
6 <input type="text" name="class" value="Warrior">
7
8 <label for="strength">Strength:</label>
9 <input type="number" name="strength" value="18">
10
11 <button>Save</button>
12 </form>
When you click on the Save button we want to store these values and create a new minion that will serve us for eternity. So we add an event handler saveMinion that will be called when the form is submitted:
1 // select the form element with the id of minion
2 let form = document.querySelector('form#minion');
3
4 // when submitting the form we will call the saveMinion function
5 form.addEventListener('submit', saveMinion);
In the example above we use the document.querySelector method to select the form element that represents the actual form on the web page. After that, we call the addEventListener method to register a saveMinion event handler for the submit event of the form. Whenever the user clicks on the Save button, the form will be submitted and the saveMinion method will be called.
The next step would be to extract the values from the inputs above. How can we go about that? Well, we can select all the inputs within the form and extract the values that we or another wizards have typed in.
1 function saveMinion(e){
2 let inputs = form.querySelectorAll('input'),
3 values = [];
4
5 for (let i = 0; i < inputs.length; i++) {
6 values.push(inputs[i].value);
7 }
8
9 console.log(values);
10 // => ["Orc", "Warrior", "18"]
11
12 // TODO: createMinion(values);
13
14 // this just prevents the form from being submitted via AJAX
15 e.preventDefault();
16 }
So we use the form.querySelectorAll('input') method to select all input elements within the form. This method returns an array-like object of nodes. Because it has a length property we can use a simple for loop and a new array values to collect the values. After that we can create our brand new minion with the extracted values.
But, is there a better way to collect these values? What about converting the inputs array-like object to an array and using the helpful array methods instead of the for loop? Spread operator to the rescue!
1 function saveMinionWithSpread(e){
2 let values = [...form.querySelectorAll('input')]
3 .map(i => i.value);
4
5 console.log(values);
6 // => ["Orc", "Warrior", "18"]
7
8 // TODO: createMinion(values);
9
10 // this just prevents the form from being submitted via AJAX
11 e.preventDefault();
12 }
By converting the array-like to an array using the spread operator we can use array functions such as map and write more beautiful code! map works just like LINQ’s Select and let’s you perform transformations on each item of a collection. In this case we just transform a collection of elements into values. Awesome right?
In addition to array-like objects you can use the spread operator to convert any iterable object to an array. For instance a Set (a collection of unique items):
1 // You can also convert any iterable into an array using spread
2 let exits = new Set(['north', 'south', 'east', 'west']);
3
4 console.log(exits);
5 // => [object Set]
6
7 console.log([...exits]);
8 // => ['north', 'south', 'east', 'west];
Or a Map (like a C# Dictionary):
1 let box = new Map();
2 box.set('jewels', ['emerald', 'ruby']);
3 box.set('gold coins', 100);
4
5 console.log(box);
6 // => [object Map]
7
8 console.log([...box])
9 // => [["jewels", ["emerald", "ruby"]], ["gold coins", 100]]
10 // in this case we get an array of key-value pairs
Spread Lets You Combine New and Apply
The spread operator also lets you combine the new operator with the ability to apply arguments to a function. That is, the ability to instantiate objects using a constructor function while, at the same time, adapting an array of arguments into a constructor function that expects separate arguments.
Let’s continue the example from the previous section where we extracted the characteristics of our minion from an HTML form. To refresh your memory the form looked like this:
1 <form action="post" id="minion">
2 <label for="name">Name:</label>
3 <input type="text" name="name" value="Orc">
4
5 <label for="class">Class:</label>
6 <input type="text" name="class" value="Warrior">
7
8 <label for="strength">Strength:</label>
9 <input type="number" name="strength" value="18">
10
11 <button>Save</button>
12 </form>
The next natural step would be to create a new minion using those characteristics and the following constructor function:
1 function Minion(name, minionClass, strength){
2 this.name = name;
3 this.minionClass = minionClass;
4 this.strength = strength;
5 this.toString = function(){
6 return `I am ${name} and I am a ${minionClass}`;
7 }
8 }
If we were to use pure ES5 we would need to unwrap the values before we use them:
1 var newMinion = new Minion(values[0], values[1], values[2]);
With ES6 we can combine new with the spread operator to get this beautiful piece of code:
1 let newMinion = new Minion(...values);
The full code example could look like this:
1 // add event handler for the form submit event
2 form.addEventListener('submit', saveMinionForReal);
3
4 function saveMinionForReal(e){
5 let values = [... form.querySelectorAll('input')]
6 .map(i => i.value);
7 console.log(values);
8 // => ["Orc", "Warrior", "18"]
9
10 // create minion with the values
11 let newMinion = new Minion(...values);
12 console.log(`Raise and live my minion: ${newMinion}!!!`)
13 // => Raise and live my minion: I am Orc and I am a Warrior!!!
14
15 // saveNewMinion(newMinion);
16
17 e.preventDefault();
18 }
In the example above we first extract the values from the form and then we use them to create a newMinion object by applying both the new and the spread operators at once.
Concluding
In this chapter you learned about the ES6 spread operator and how it works in sort of the opposite way to the rest operator. Instead of grouping separate items into an array, the spread operator expands arrays into separate items.
You learned how you can use it in many scenarios usually resulting in more readable code: to easily concatenate arrays, as a substitute for apply, to convert array-like objects and even other iterables to arrays and, finally, to combine the new operator with apply.
This chapter wraps the first tome of JavaScriptmancy! Great job JavaScriptmancer! Time to spread your wings and dive into the mysteries of data structures in JavaScript!