Introduction to javascript - part 2
Hello again, this is the second part of my ongoing series about javascript. I hope it's to some use to you. Let's get straight to the point, here's what we're covering :
Arrays
An array is what you will commonly use if you want to store a collection of data that you want to keep numerically indexed.
You can declare an array simply by :
var arr = ['a', 'b', 'c', 'd'] // declaring an array is as easy as wrapping a collection of elements
// in brackets and separating them by commas
console.log(arr[0], arr[1])
// -> a b
arr = ['a', 2, true, null, undefined, {object: true}] // being a dynamic language, javascript allows
// arrays to hold different data types, including complex ones such as objects and other arrays
Arrays are fun to work with because of the functional nature of the language. We'll see how easy how to manipulate them in a moment.
Functional programming
According to Eric Elliot, an influencing voice in the community of javascript, functional programming is one of the two pillars of the language (the other one being prototypal inheritance, a more advanced topic we might visit in the future).
Personally, I consider it the most useful feature of the language as it makes manipulating complex data types so much easier. First, let's take a look at the concept : basically, it all comes down to the fact that you can pass functions as arguments to other functions.
Let's say you have a function that calculates the sum of two numbers :
function log(){
console.log('sorry i\'m late')
}
You can have another function that accepts a function as a parameter for internal use, let's pick
one from the built-in functions, setTimeout
which takes two parameters :
- a function to call at a later time
- time in milliseconds before executing it
setTimeout(log, 5000)
// 5 seconds later...
// -> sorry i'm late
You can see how setTimeout
is using the log
function just like any other parameter.
Things can be even more interesting because we can have functions passed to our own functions and execute them whenever and however we want, e.g we can pass custom arguments to them.
We'll be making our own version of the built-in function Array.prototype.forEach
which takes a function
as an argument and applies it to each element in an array.
// we'll rewrite our log function so that it handles one parameter
function log(element){
console.log(element)
}
// our custom forEach function
function each(arr, func){
var i = 0
for(i = 0; i < arr.length; i++){
func(arr[i])
}
}
// let's test this out
var anArray = ['some', 'random', 'data', 0, 1, 2]
each(anArray, log)
// -> some random data 0 1 2
Now let's explore another feature of functional programming which is chaining functions. For this example we'll use string built-in functions.
var s = 'javascript is a functional language'
console.log(
s.toUpperCase() // convert to uppercase -> JAVASCRIPT IS A FUNCTIONAL LANGUAGE
.substr(16, 10) // take only 10 character after index 16 -> FUNCTIONAL
.replace(/i/ig, '1') // replace i characters with 1 -> FUNCT1ONAL
.replace(/o/ig, '0') // replace o characters with 0 -> FUNCT10NAL
)
// -> FUNCT10NAL
If you're wondering what's with /o/ig, it simply says : match the character 'o' or 'O'(i: ignore case) multiple times(g: global match)
Back to our example, the chaining mechanism is possible due to the fact that every function in the chain returns the result back to be processed by the next element.
Back to Arrays
Now that you have an idea about what functional programming is all about, let's revisit arrays from a new perspective.
If you type Array.prototype.
in your developer console and run through the list of buil-in functions
offered to manipulate arrays, you'll find few very interesting ones that handle array manipulation
from a special point of view which is that of a functional programming language.
The list includes concat
, every
, fill
, filter
, find
, forEach
, map
, reduce
, some
...
For starters, Array.prototype.concat
returns a new array that is result of the concatenation of the
current array and the first parameter which is an array:
var arr1 = [0, 1, 2],
arr2 = ['a', 'b', 'c'],
arr3 = arr1.concat(arr2)
console.log(arr3)
// -> [0, 1, 2, "a", "b", "c"]
Next up, we've got Array.prototype.every
and Array.prototype.some
, they both take one required
parameter which is a callback function to run on every element of the target array and they return a boolean.
However, every
returns true when and only when the callback function returns a truthy value every time
and some
returns true if the callback functions returns at least one truthy value :
function isPair(el){
return el % 2 === 0
}
function isNull(el){
return el == null // undefined and null are treated equally when not using ===
}
var arr = [2, 0, 1, 5, 8]
console.log(arr.every(el=> !isNull(el))) // no element is null
// -> true
console.log(arr.some(isPair)) // at least one element is pair
// -> true
console.log(arr.some(el=> !isPair(el))) // at least one element is impair
// -> true
Another helpful array method is Array.prototype.map
which simply maps an array of N elements to
a new array of N elements using a callback function:
var ids = {
'R': 'reading',
'W': 'writing',
'P': 'playing video games',
'G': 'playing guitar',
'S': 'swimming'
}
var student = {
name: 'ghsamm',
hobbies: 'RWP'
}
console.log(student.name + ' likes ' +
student.hobbies.split('') // ['R', 'W', 'P']
.map(el=> ids[el]) // ['reading', 'writing', 'playing video games']
.join(', ')) // 'reading, writing, playing video games'
// -> ghsamm likes reading, writing, playing video games
My personal favorite array method is Array.prototype.reduce
because it allows you to reduce a whole
array to any kind of value you want. It's a little harder to grasp than its siblings.
// here is its signature
arr.reduce(callback, initialValue)
// and this is the callback's signature
function callback(previousValue, currentValue){}
For the callback, the currentValue
parameter is always going to be the current value in the array.
previousValue
however is the meat of the trick because it's initial value is the one passed
to reduce but for the following callback invocations, it's value is whatever the callback function
returned last time.
The final result is the last value of previousValue
, aka. the last returned value from callback
.
Let's see some code:
// we'll be calculating the sum of the array
var arr = [57, 1, 153, 0, -2, 99]
function sum(prev, cur){
return prev + cur // each time the sum function returns whatever
// the previous sum is + the current element of the array
}
console.log(arr.reduce(sum, 0)) // of course, the initial value of the sum is 0
This is a simple example but reduce
can be extremely powerful when dealing with arrays.
Moving on...
Objects
Object creation and manipulation in javascript is easier than any other language that I know of and it had become the inspiration for the most used data transfer protocol on the web : JSON(JavaScript Object Notation) for its simplicity and universality.
var student = {
name: {
first: 'samh',
last: 'ghanmi'
},
age: 21,
interests: [
'javascript',
'node.js',
'git'
],
sayHi: function(){
console.log('Hi, I am ' + this.name.first + ', I like ' + this.interests.join(', '))
}
}
student.sayHi()
// -> Hi, I am samh, I like javascript, node.js, git
The data structure is fairly obvious; objects are key:value
pairs, with the value being one of the
following data types: string, number, boolean, array, function or object.
Just like primitive types, objects are not committed to any sort of schema like in most other languages, so it is very normal in javascript to monkey-patch (aka. decorate) your objects as needed.
Here's a beginner implementation of the idea of adding properties to an existing object:
var student = {
name: 'ghsamm'
}
function logName(obj){
console.log(obj.name)
}
// utility function
function override(object, key, cb){
object[key] = function(){
cb(object)
}
}
// now we can add the logName function as a 'method' to the original object
override(student, 'logName', logName)
student.sayName()
// -> ghsamm
Of course as I said this is not a practical example but it should be more than enough to make you grasp the concept of monkey-patching, a unique feature in the language.
An important thing to notice here is that although it is possible to monkey-patch buil-in objects (the Object.prototype for example which is the inherited prototype of all objects), it is not recommended to try it because it can result in conflicts with future language specs.
That being said, there are ways to do it wisely like for example when a new language feature is 'polyfilled' to run on older browsers.
That's it for today, my next article will cover typescript, the language that adds types to javascript for development-time static-analysis error checking. Stay tuned.