Tuesday, March 10, 2020

Unemployed Again and the Javascript Fat Arrow

As is often the case when I become involuntarily unemployed I find myself with free time on my hands.  I was recently working as contractor for Hilton Hotels indirectly for a recruitment agency known as Apex. What I learned from this experience is you should read the contract. In this case it simply said if I want to quit I must give them a two week notice but if they no longer require my services they can just tell me that day. While they weren't that draconian in this instance (they told me Thursday that the next day Friday would be my last) I still don't view this as an equitable exchange and so in the future I will be sure to amend the contract one way or the other or just not take the job.

But I digress. Having free time on my hands allows me to catch up on the new stuff I am normally missing out on because when I am working full time I very rarely have free time on my hands and that which I do is typically consumed by my wife. For some reason this woman wants to constantly be around me and consume my time with small talk and other trivial stuff so unfortunately when I am working I have little to no time to catch up on what's new and exciting with my favorite tools.

Such is the case with Javascript because even though I have been using Javascript productively since around 1999, it seems I am not aware of the foundational concepts on which it is predicated and the way those concepts have been expanded and used. It is one thing to know a language such that you can produce working code. It is far different to have an academic understanding of the language. I learned this several years ago when I befriended a professor at the local university. He was teaching Python and while I had been using Python every day it was apparent he knew a lot more about Python than I did even though he had never written a line of Python that was used in a production environment.

Anyway, back to the topic at hand, the 'fat arrow'. While some might say the fat arrow is basically syntactic sugar for function declarations this would be doing a great disservice to the fat arrow. In fact it differs from standard Javascript function declarations in a variety of ways.

A) Syntactical Differences
First, the syntactic sugar.
function someFunc() {          // decalre a function with no arguments
const someFunc = () => {       // declare a function with no arguments

function someFunc(props){  // decalre a function with a single argument
const someFunc = (props) => {  // declare a function with a single argument
const someFunc = props => {    // also declare a function with a single argument

// and finally we have this parameter free syntax
const things = [{name:'thing1', price:'10'}, {name:'thing2', price:'20'}];
console.log(things.map(prices => prices.price));
B) Use as a Constructor
Another difference between functions defined using the fat arrow and standard Javascript functions is that functions defined using the fat arrow may not be used as constructors.

C) Use as a Generator
Functions defined using the fat arrow syntax can’t be used as generators. Using the yield keyword in a function defined using the fat arrow will throw an error.


D) Implicit Return Value
When using normal functions there is no implicit return value. You must use the return statement to return a value from a function defined using the standard function syntax. Functions defined using the fat arrow syntax CAN POTENTIALLY have an implicit return value.
const someFunc = () => 'boo'
someFunc()      // returns 'boo'

For an arrow function to have an implicit return an expression is required. But in JavaScript, many language constructs are not expressions but are statements. In fact, for a function defined using the fat arrow if it has a statement in its body it must be defined using curly braces and as soon as you have curly braces surrounding your function body, returns are no longer implicit – for either statements or expressions.

const broken = () => { 'who cares' }
broken()       // returns undefined
const thisWorks = () => { return 'boo who' }
thisWorks()     // returns 'boo who'

Our old pal the ternary operator allows an expression, and as was previously mentioned an expression can be returned.

function ternary() { return true ? 'Yup' : 'Nope' }
ternary()     // returns 'Yup'


E) Return Values
If you want to return objects from an arrow function, you need to wrap them in parentheses.

const details = name => ({ firstName: name }); // will return an object
const details = name => { firstName: name };   // will return undefined

Not so using the funnction syntax to define a function.

function syntax1(){
  let someObj = {first:'Joe', last:'Blow'};
  return(someObj);
}
function syntax2(){
  let someObj = {first:'Joe', last:'Blow'};
  return someObj;
}
console.log(syntax1()); // prints { first: 'Joe', last: 'Blow' }
console.log(syntax2()); // also prints { first: 'Joe', last: 'Blow' }

F) This is Now Lexically Scoped
Finally, the meaning of 'this' is different for a function defined using the function syntax versus a function defined using the fat arrow. Instead of trying to describe what 'lexically scoped' means I'll simply show you an example I saw somewhere which I copied (sorry to the author but I forgot the link and I can't seem to find it again). Without just showing you the code though I'll try to explain briefly using my own experience.

Before the fat arrow, 'this' referred to the thing you were running, so to get the desired behavior in a function that was passed to the timer I would have to use the trick of using a closure to define a variable (some like myself would call this variable 'self' or 'that') and then we would get the desired behavior. Being the chucklehead I am I had no idea Javascript functions had a default function called 'bind()'. While the closure trick still works with normal functions it is no longer necessary if you use the fat arrow to define your function. The following code which you can simply copy and paste into a file on your desktop which you then just double click on to see it in action in your default browser should demonstrate the concept better than a bunch of words.

<html>
  <head>
    <script>
// globally defined this.i
this.i = 100;
var counterA = new CounterA();
var counterB = new CounterB();
var counterC = new CounterC();
var counterD = new CounterD();
// bad example
function CounterA() {
  // CounterA's `this` instance (!! gets ignored here)
  this.i = 0;
  setInterval(function () {
    // `this` refers to global object, not to CounterA's `this`
    // therefore starts counting with 100, not with 0 (local this.i)
    this.i++;
    document.getElementById("counterA").innerHTML = this.i;
  }, 500);
}
// manually binding that = this
function CounterB() {
  this.i = 0;
  var that = this;
  setInterval(function() {
    that.i++;
    document.getElementById("counterB").innerHTML = that.i;
  }, 500);
}
// using .bind(this)
function CounterC() {
  this.i = 0;
  setInterval(function() {
    this.i++;
    document.getElementById("counterC").innerHTML = this.i;
  }.bind(this), 500);
}
// fat arrow function
function CounterD() {
  this.i = 0;
  setInterval(() => {
    this.i++;
    document.getElementById("counterD").innerHTML = this.i;
  }, 500);
}
    </script>
  <body>
    Bad Example. This code is actually broken:<span id="counterA">0</span>
    Using a closure: <span id="counterB">0</span>
    Using 'bind':<span id="counterC">0</span>
    Using the fat arrow: <span id="counterD">0</span>
</html>


So now you know everything I know about the fat arrow.

No comments: