Sunday 31 March 2019

Some JavaScript Goodies

Time for an entry with some JavaScript discoveries that I've done recently.

Reading some article I came across some code that I could not understand, something like:

class Person{
 [doSomething](){
 
 }
 
 //more code here
}

Odd, right? Well, that [doSomething] is a symbol, there was a previous declaration in the code like this: const doSomething = Symbol('doSomething'); so all in all this is a way to create private members. Typescript is not using this technique at all, the private methods in TypeScript are only at compile time, while this mechanism makes them private at runtime. Anyway, I don't see myself using it, but at least it's good to know.

The most common use of push and concat in JavaScript is: push to add a single element to an existing array, concat to merge 2 arrays into a new array. But you can use concat also to add a single element (still creating a new array). This detail is really useful to write the most concise function to get unique elements in one array that I can think of:

function getUniqueItems(arr){
 return arr.reduce((difItems, it) => difItems.indexOf(it) == -1 ? difItems.concat(it) : difItems
 , []);  
}

let ar = [1, 4, 5, 4, 1, 3, 2, 3, 2];
console.log(getUniqueItems(ar));
//[ 1, 4, 5, 3, 2 ]

An interesting point is that thanks to the rest/spread operator (...) we can now easily merge an array into an existing one:

let ar = [1, 2];
let ar2 = [3, 4];
ar.push(...ar2);
console.log(ar);
//[ 1, 2, 3, 4 ]

A while ago I posted about the (at that time new) rest/spread operator. Reading these nice snippets I've found many interesting uses of them. One thing that I have to thank to the ... operator is that it's spared me one of those recurrent dubious moments that made me go back to the documentation again and again to be refreshed about the difference between Function.prototype.call and Function.prototype.apply:

the fundamental difference is that call() accepts an argument list, while apply() accepts a single array of arguments.

With the ... operator we no longer need apply, we can just use call:

let args = [arg1, arg2];

//rather than:
fn.apply(self, args);

//we can just use:
fn.call(self, ...args);

This led me to wonder how does the compiler spread the array and pass it as arguments? The inverse, rest, is very simple, wherever the operator shows up the compiler will add code to push the elements into an array, that's simple, but how does the compiler/interpreter spread that array into an argument list and pass it over to the function? We don't have anything like that in C# or Java, they work the opposite way. Well, I guess all the magic has to be in the Activation Object. A JavaScript function when invoked (activated) ends up with an execution context that contains in its Activation Object, the arguments and local variables. So I guess the compiler just pushes the elements of the array that it's going to spread into this activation object.

No comments:

Post a Comment