A JavaScript Crash Course from a Apex Dev's Point of View - Part 4

At TrailheaDX ‘19, I was fortunate enough to do a talk on learning JavaScript fundamentals from an Apex developer’s point of view. This series covers the topics from that talk

We (hopefully) demystifyied the this keyword in the last post. Now we’ll switch gears and focus on debugging differences between Apex and JavaScript, and how it can change your thoughts on how to debug your code.

Reactive Debugging

In Apex, generally you are doing what I like to call “reactive” debugging. Using a System.debug and System.assert calls, you generally run your code and examine the results after the fact. The Apex Replay Debugger sort of simulates interactive debugging, but even with that tool you are looking at something that has already happened.

I often see Apex developers do the same thing when working with JavaScript using console.log. For example, this is a simple function that accepts an array of contacts and returns an array of the contacts’ full names:

function getFullNames(contacts) {
    console.log(contacts);
    return contacts.map( (x) => {
        x.FirstName + ' ' + x.LastName;
    });
}

What I will generally see is a developer use the console.log function, run the code, look at the console, and repeat. Debugging like that can be helpful, but it’s only a fraction of what you can do in JavaScript. Since front-end JavaScript is running on your machine as opposed to a cloud server, you have more powerful debugging tools at your disposal.

Interactive Debugging.

Let’s take the scenario when you have to debug a front-end JavaScript issue in production, so adding console.log calls to your source isn’t really a feasible option. With JavaScript, the Chrome browser gives you tools to interact with your code while it is running without having to modify the source code.

First you need to enable Debug Mode for Lightning Components. By doing so, JavaScript won’t be minified, making it easier to read and interact with.

//unminified
function getFullNames(contacts) {
    console.log(contacts);
    return contacts.map( (x) => {
        x.FirstName + ' ' + x.LastName;
    });
}

//minified
function getFullNames(e){return console.log(e),e.map(a=>{a.FirstName,a.LastName})}

Then, open up the Developer Tools in Chrome and go the Source tab. When you click the line numbers, you can add a breakpoint. When the code runs, the browser will actually freeze on at the line. You can see the values of variables and you when you are done you can press the play button in the top right to continue running.

Debugger screenshot

You can also interact with the variables and even change them in the Console tab.

Console screenshot

This is just some of the basic stuff you can do, so make sure to check out the Chrome Dev Tools documentation for more cool features.

What’s fantastic about this is that this makes it easier to interact with production code. You don’t have to deploy anything to debug, you can just interact with it directly in the browser. This is really useful for those scenarios where something works in a sandbox but not in production - you can debug closer to the actual problems as they are actively happening.

That concludes this crash course in JavaScript development. I hope that provides you with a foundation to continue leveraging the best of JavaScript on the Salesforce platform and you find yourself a little more fluent when reading others’ JavaScript code.

A JavaScript Crash Course from a Apex Dev's Point of View - Part 3

At TrailheaDX ‘19, I was fortunate enough to do a talk on learning JavaScript fundamentals from an Apex developer’s point of view. This series covers the topics from that talk

We focused on first class functions in the last post, which should help set the stage for demystifying the this keyword.

What’s this?

In Apex, the this keyword is fairly straightforward: it refers to the current instance of an Object. Take this Person class as an example:

public class Person {
    String name;

    public String getName() {
        return this.name;
    }
}

When you instantiate a Person object, the method getName uses this.name to return that instance’s value for the property name.

If you treat the this keyword in JavaScript similarly you will quickly run into some unexpected behavior. Remember, functions in JavaScript are first class. So while a function may be the value of a property in an object, it doesn’t necessarily belong to that object. For example, consider this object:

let person = {
    name : 'Sean',
    getName : function() {
        return this.name;
    }
}

The person JavaScript object has a property called getName and it would seem that if you called that function, it should return the name property on that object (i.e. the value Sean). Depending on how the function was called, that would be true. But what if you assigned the value of getName to another variable, like so?

let nameGetter = person.getName;

Now the variable nameGetter has been essentially been removed from the object person. What does the this keyword in nameGetter refer to now? If you think of JavaScript like Apex, you would be tempted to say the this keyword still refers to the person object, but that’s not the case.

Generally, this keyword is often said to be bound to the “call site”, meaning that its values depends on the context in which the function is used. Just by looking at a function that contains the this keyword is not enough to tell you what it is bound to. It is not necessarily the object it is used in, but it can be!

This really frustrated me, because I wanted the this keyword to work like it does in Apex. But if you can let go of that notion of what you think it should be, you can than embrace it for what it can do. To do that, let’s walk through the rules of how the this keyword is bound.

(Credit for the this hierarchy that follows goes to You Don’t Know JS, an amazing book that really helped me understand JavaScript more!)

Default Binding

Let’s start with “default binding”, which is basically what this falls back on when all of the other rules that follow don’t apply. In short, the default binding is the global scope. For example:

function identify() {
    console.log(this.name);
}

var name = 'Sean'

identify(); //Outputs 'Sean'

The variable name is in the global scope. So when you call the identify function, none of the other rules for binding the this keyword apply (you’ll understand why later as you read on), so it is bound to the global scope and thus the log the string Sean.

However, when strict mode is in use, which it will be in LWC, then the global scope is not eligible to be bound to this, so you will get an error instead.

function identify() {
    'use strict`
    console.log(this.name);
}

var name = 'Sean'

identify(); //TypeError: Cannot read property 'name' of undefined

“new” Binding

Let’s start at the top of the this binding hierarchy. When you call a function with the new keyword, you are invoking a constructor call of that function. When you call a function with the new keyword, that function behaves differently:

  • The function creates an empty object
  • Any references to the this keyword in the function will now refer to that object during it’s execution
  • At the end of the function, unless something is explicitly returned by the function, that newly created object will be returned (even if you don’t have a return statement in your function)

Consider the following function named Person. It has a capital “P”, but that does not affect it’s behavior as a constructor. Instead, it typically signals that it is a constructor function that can and should be called with the new keyword, but using a lowercase p would work as well.

function Person(name) {
    this.name = name;
}

When we call this function with the new keyword, we can assign the object it creates to a variable.

let programmerSean = new Person('Sean');
console.log(programmerSean.name) //Outputs 'Sean'

Because we used the new keyword to invoke the Person function, the variable programmerSean is an object with a property name, that contains the string value Sean. What if we don’t use the new keyword?

let programmerJoe = new Person('Joe');
console.log(programmerJoe.name) //TypeError: Cannot read property 'name' of undefined.
console.log(name) //Outputs 'Joe' because the `this` keyword fell back on the global scope!

Without the new keyword the Person function did NOT return an Object, so the variable programmerJoe is actually undefined. Also the this keyword could not use new binding, so it defaulted to the global scope. As a result, there is now a name property on the global scope that holds the string value Joe!

Explicit Binding

In JavaScript, you can also explicitly set what you want the this keyword to be bound to using the functions call, apply and bind.

Consider the following function and object:

function greet(greetings) {
    console.log(greetings, this.name);
}

let sean = { name : 'Sean' }

The call function can be called on a function and accepts one to many parameters. The first parameter is the object that you want this to be bound to. And rest of the parameters are the parameters that you want to pass the function you are invoking. Using call on a function immediately invokes it. For example:

greet.call(sean, 'Hello', 'My Friend') //Outputs 'Hello My Friend Sean'

The apply function works the same, but accepts only 2 parameters. The first parameter is the same, it’s the object you want this to be bound to. The second parameter, however, is an array that holds all of the parameters you want to pass the function you are invoking. For example:

greet.call(sean, ['So Long', 'Farewell']) //Outputs 'So Long Farewell Sean'

The bind function works differently in that it does not invoke the function. When you call bind on a function, you pass it one parameter, which will be the object you want the this keyword bound to. bind then returns that function with this already bound, which can you assign to a variable. For example:

let boundFunction = greet.bind(sean);
boundFunction('Good Morning'); //Outputs 'Good Morning Sean'

In short, use call and apply when you want to invoke a function with a specified this keyword and use bind when you want to specify the this keyword, but don’t want to invoke the function until later.

Implicit Binding

When a object has a function as a property, when you call that function from that object, the this keyword is bound to that bound. This is known as implicit binding. Here is the person object that we looked at earlier that contained a function as a property:

let person = {
    name : 'Sean',
    getName : function() {
        return this.name;
    }
}

When you call person.getName(), due to implicit binding the this keyword refers to the person object and thus returns the string value Sean. As we illustrated earlier, you can also lose this implicit binding if you call the function outside of the context of the object.

let nameGetter = person.getName;
nameGetter(); //returns nothing

You will often run into this in LWC. Consider this example component:

import { LightningElement, track } from 'lwc'
export default class JobTracker extends LightningElement {
    @track
    jobRunning = false;

    handleClick() {
        this.jobRunning = true
    }
}

At first class, it looks like JavaScript class are treating the this keyword similarly to how it works in Apex. It looks like this is mean to represent that instance of the class, which is why the handleClick function can access the jobRunning property. In actuality, this only works because of implicit binding. Let’s rewrite this component to illustrate how the this keyword does not necessarily refer to the class.

import { LightningElement, track } from 'lwc'
export default class JobTracker extends LightningElement {
    @track
    jobRunning = false;

    handleClick() {
        markJobRunning();

        function markJobRunning() {
            this.jobRunning = true
        }
    }
}

We have now created a nested function called markJobRunning within the handleClick function. markJobRunning is not called with the new keyword, so new binding is not in effect. Explicit binding is also not being used and we can tell that implicit binding is not being used because markJobRunning is not being called from an object. So that means that default binding is in effect. Of course, since LWC runs in strict mode, that means this is going to undefined, so we’ll get an error saying Cannot read property jobRunning of undefined.

Binding Hierarchy

To review, use the following hierarchy to determine what this is bound to.

  • Is the function being called with new?
    • The this keyword refers to the “new” object constructed by the function
  • If not, is the function being called with call or apply, or instantiated with bind?
    • The this keyword refers to the specified object
  • If not, is the function being called off of an object?
    • The this keyword refers to the object the function was called off of
  • If not, default binding is in effect
    • The this keyword refers to the global scope, or is undefined if strict mode is enforce.

Arrow functions

There is of course an exception to all of this! Arrow functions are not just shorthand for writing functions. The this keyword actually behaves differently when you use an arrow function. For those who are unfamiliar here is the same function written with the function keyword and as arrow function:

let oldFunction = function(x) { console.log(x) }
let arrowFunction = x => console.log(x)

I love the shortened syntax, but if you have the this keyword in a function that means that two different syntaxes are not interchangeable. To illustrate, let’s look at an example that demonstrates that problem that arrow functions attempt to solve. Consider our Person constructor function again.

function Person(name) {
    this.name = name;
    this.delayedGreet = function() {
        setTimeout(
            function() {
                console.log("Hi, my name is ', this.name);
            },
            100
        )
    }
}

let sean = new Person('Sean');
sean.delayedGreet() // Eventually outputs "Hi, my name is undefined"

So our Person function constructs an object with a name property and a delayedGreet property that calls setTimeout with a callback function that logs out a greeting using the name property of the this keyword. However, after calling the Person constructor, when we call delayedGreet, it outputs Hi, my name is undefined instead of the expected Hi, my name is Sean. What’s happening here? What does the this keyword in this.name refer to?

At first glance you might think that new binding should be in effect. However when the delayedGreet property is created, the callback passed to the setTimeout function is not being invoked yet, so new binding IS NOT in effect. When that callback is eventually invoked, the this keyword is bound to however the setTimeout function binds it, so this.name ends up being undefined.

Arrow functions attempt to fix this confusion. Here’s the same method written with an arrow function:

function Person(name) {
    this.name = name;
    this.delayedGreet = function() {
        setTimeout(
            () => console.log("Hi, my name is ', this.name),
            100
        )
    }
}

let sean = new Person('Sean');
sean.delayedGreet() // Eventually outputs "Hi, my name is Sean"

The same function but written with an arrow function and now it works! What’s happening here? Well, arrow functions use “lexical scope” for defining the this keyword. In other words, whatever this is bound to when the function was instantiated is what this will be bound to. In our example, the this keyword will use new binding so that it is bound to the same object that was created during the constructor.

Are you confused? You should be! In my opinion, this “fix” just creates more mental overhead. It’s another exception you have to keep track of when you already have to deal with a somewhat confusing hierarchy. It’s important to know what’s going on here because some developers do like this “solution” that arrow functions provide. I, however, instead recommend using explicit binding.

function Person(name) {
    this.name = name;
    this.delayedGreet = function() {
        setTimeout(
            function() {
                console.log("Hi, my name is ', this.name);
            }.bind(this), //Explicitly bind this to the "new" binding"
            100
        )
    }
}

let sean = new Person('Sean');
sean.delayedGreet() // Eventually outputs "Hi, my name is Sean"

By using the bind method, I am explicitly setting what I want the this keyword to be so that it is very clear as to what I intend. My advice? If you have to refer to the this keyword, avoid using an arrow function.

Hopefully you now have a better understanding of how the this keyword works, which can make reading JavaScript much less intimidating. In our next post, we’ll wrap up with some debugging options that you have in JavaScript that you previously did not have in Apex.

A JavaScript Crash Course from a Apex Dev's Point of View - Part 2

At TrailheaDX ‘19, I was fortunate enough to do a talk on learning JavaScript fundamentals from an Apex developer’s point of view. This series covers the topics from that talk

In the last post, we covered some basic differences in JavaScript and Apex, focusing on dynamic typing and scope. In this post, we’ll focus on first class functions in JavaScript, a feature that I believe is a fundamental difference in how you write code compared to Apex.

First Class Functions

Unlike Apex, functions are “first class” in JavaScript. What does that mean? It means that functions can be:

  • Passed as arguments to other functions, which are generally referred to as “callbacks”
  • Used as return values from functions
  • Assigned to variables
  • Stored as attributes in data structures.

In Apex, methods can only be passed around when encapsulated in an object, and then called from that object.

Here is an example of first class functions in action:

function fetchAction() {
    return function() { console.log('you called?') }
}

function doTheThing(f) {
    f();
}

let anAction = fetchAction();
doTheThing(anAction); //Outputs: "you called?"

The first function fetchAction returns a simple function that logs the string you called?. The second function doTheThing accepts a callback and executes it. So we can call fetchAction, assign the return value to the variable anAction, and then pass that to doTheThing, which executes that action, logging the string you called?. As you can see, function values do NOT have the parenthesis. Adding the parenthesis to a function invokes the function.

You will often see this in LWC with imperative Apex, which imported as Promises. You can chain Promises with the then and catch functions, both of which accept a callback as parameters. When you call an Apex method in LWC, after the method completes, the then function executes the callback passed to it. If an exception is thrown, the catch method executes the callback passed to it.

For example:

import {LightningElement, track} from 'lwc';
import submitRecords from '@salesforce/apex/MyController.submitRecords';

export default class MyComponent extends LightningElement {
    @track allRecords
    
    submit() {
        submitRecords({ records: allRecords })
            .then(function(){
                alert('Success')
            })
            .catch(function(){
                alert('Something went wrong...')
            })
    }
}

When the submit function is called in our component, that executes the imperative Apex method submitRecords. If that method resolves successfully, the then function executes a callback function that alerts the string Success. If an exception is thrown, the catch function executes a callback function that alerts the string Something went wrong. Since functions are first class, we are able to declare the callbacks parameters as inline functions. But you could also declare the callbacks outside of the functions like so:

import {LightningElement, track} from 'lwc';
import submitRecords from '@salesforce/apex/MyController.submitRecords';

export default class MyComponent extends LightningElement {
    @track allRecords
    
    submit() {
        submitRecords({ records: allRecords })
            .then(handleSuccess)
            .catch(handleError)

        function handleSuccess(){
            alert('Success')
        }

        function handleError(){
            alert('Something went wrong...')
        }
    }
}

In this example, the behavior is the same, but this time the functions are declared outside of the parameters and then we pass handleSuccess and handleError to then and catch respectively. Remember, when referring to functions as values, do not add the parenthesis. handleSuccess is the function itself while handleSuccess() will call the function and pass the value of whatever is returned by that function.

Let’s walk through a more practical example to help illustrate how this affects the way we write code. Given an array of contacts with their names and the group they each belong to, I want to get a concatenated string of all the contacts that belong the group “Avengers” so that I can print them on a document. Here is the list:

let contacts = [
    { groupName : 'Avengers',       name : 'Steve Rogers' },
    { groupName : 'Avengers',       name : 'Tony Stark' },
    { groupName : 'Avengers',       name : 'Natasha Romanov' },
    { groupName : 'Justice League', name : 'Diana Prince' },
    { groupName : 'Justice League', name : 'Bruce Wayne' },
    { groupName : 'X-Men',          name : 'Charles Xavier' },
]

If I were to build this in JavaScript with an Apex mindset, you might do something like this:

  • Initialize a blank string
  • Iterate through all the contacts
  • If the contact belongs to a desired group, concatenate their name to the string
function concat(contacts) {
    let concatenatedNames = '';
    for (let i = 0; contacts.length; i++){
        if (contacts[i].groupName === 'Avengers') {
            concatenatedNames = concatenatedNames + '|' + contacts[i].nae;
        }
    }

    return concatenatedNames
}

There’s nothing inherently wrong with this code; it does the job. But you typically wouldn’t see it written like this. The Array object has functions that allow you to iterate over the items in an array while taking advantage fo first class functions.

  • filter - iterates over each value in a collection and passes each item to a callback for processing
    • If the callback returns a “truthy” value, then the item will be added to a new array
    • Otherwise it does not add the item to the new array.
    • When complete, the new filtered array is returned
  • map - iterates over each value in a collection and passes each item to a callback for processing.
    • Whatever is returned by that callback is added to new array
    • When complete, the new array is returned
  • reduce - iterates over each value in a collection and passes each item and an aggregator to a callback for processing.
    • Whatever is returned by the callback becomes the “aggregator” object. This aggregator is passed to the next iteration of the callback
    • When complete, the aggregator is returned
    • Essentially, you are “reducing” an array into an object

Let’s do the same concatenation, but this time leveraging these functions:

function concat(contacts) {
    return contacts
        .filter( function(contact) { return contact.groupName === 'Avengers' } )
        .map( function(contact) { return contact.Name }, [] )
        .reduce (function(concatenatedNames, name) { return concatenatedNames + '|' + name; }, '')
}

filter builds an array of contacts that are in the “Avengers”. Since the return value of filter is an array, we can chain the map function to it, which will build an array of just the name strings from the contacts. map also returns an array, so we chain reduce to it, which will builds our concatenated string. To be more succinct, here’s same method with arrow functions.

function concat(contacts) {
    return contacts
        .filter( c => c.groupName === 'Avengers' )
        .map( c => c.Name )
        .reduce( (concatenatedNames, n) => concatenatedNames + '|' + n, '')
}

This is a common pattern you’ll see in JavaScript: call some function to get some data and use callbacks to process that data. Because functions are first class in JavaScript, execution tends to favor this pattern of passing around utility functions that are chained to produce an end result as opposed to using instance methods that manage an object’s state.

I actually don’t know if a for loop is faster or slower than using filter, map and reduce, but I do know that using filter, map and reduce is more prevalent. Remember, part of writing code is making it more readable to other developers. As the saying goes, when in JavaScript, do as the JavaScriptors do!

That covers the basics of first class functions and how they can change your approach to writing code in JavaScript vs. Apex. In the next post, we’ll focus on demystifying the this keyword identifier and how it is drastically different from the this keyword in Apex.