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.

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

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

I’ve been a developer for about 8 years now and while most of that time has been focused on working on the Salesforce platform, I’ve also had the opportunity to work on Golang, Ruby, and of course, JavaScript. Just like the languages we use to speak with each other, part of successfully using a programming language is to embrace the nuances that are commonly followed by other users of that language. In other words:

Stop developing in JavaScript as if you were writing Apex.

During this series, we’ll cover some topics that I believe provide a strong foundation to getting started with JavaScript by comparing its features to analogous features from Apex.

JavaScript is a Dynamically Typed Language, Apex is Static

In a statically typed language like Apex, variables must be declared with their type and thus those variables can only reference their declared type. Here’s a simple Apex method for example:

Integer increment(Integer x) {
    return x + 1;
}

Because of static typing, the method increment can only accept an Integer as a parameter and can only return an Integer. This provides a level of protection at compile time as there is a stricter contract as to what your method will accept and return, leaving you with less surprises at runtime.

In a dynamically typed language like JavaScript, variable types are defined at runtime. Here’s the same method as a JavaScript function:

function increment(x) {
    return x + 1;
}

The parameter x can be anything! A string, an integer, or some kind of arbitrary object. You also do not necessarily know what it will return just by looking it. You lose that compile time protection, but you also get more flexibility. For instance, dynamic typing makes defining arbitrary data easier. Here’s a function that returns an arbitrary data structure that is declared inline:

function returnData() {
    return {
        a : 'some',
        b : 'random',
        c : {
            d : 'nested data
        }
    }
}

I can’t count the number of times while writing Apex where I just need to process some data and return it in some random structure. Here is the equivalent of that data structure in Apex:

class RandomData {
    String a;
    String b;
    NestedData c;
}

class NestedData {
    String d;
}

class RandomData returnData() {
    RandomData rand = new RandomData();
    NestedData nest = new NestedData();

    rand.a = 'some';
    rand.b = 'random';
    rand.c = nest;
    nest.d = 'nested data';

    return rand;
}

Because Apex is statically typed, you would need to define objects that follow the data structure you want. You get some type safety here but if this was just some private helper method in a class, that type safety is probably outweighed by the extra code that you have to write (and thus maintain!) This is a pretty contrived example, but you have probably seen this when making wrapper classes for SObjects when you wanted to encapsulate an SObject in addition to other data that does not necessarily exist as a field on that SObject. For example:

class OpportunityWrapper() {
    Opportunity opp;
    Boolean shouldDisplay;
}

Sure that’s only four lines, but where should those four lines live? In the controller it is used in? In its own file? What if you have to add more properties? What if those properties aren’t necessarily related? You can see how you can start to spiral out of control, all over some data structure.

The increased flexibility of dynamic typing, however, can lead to some unexpected results. Consider this concatenate function that accepts two parameters and logs their concatenation as a string.

function concatenate(a, b){
    console.log(a + b);
}

If you called function with the strings “hello” and “world”, you’ll see the expected output “helloworld” with no spaces. If you called function with the integers “1” and “2”, you would expect the output “12”, but it would actually output “3”. The function just adds the parameters together and adding two integers together behaves differently from adding two strings together. To fix this, add an empty string to the concatenation so the parameters are coerced into strings. Here’s the fixed version of function:

function concatenate(a, b){
    console.log(a + '' + b);
}

Now passing the integers “1” and “2” to the function will output the expected “12”. So when you are writing JavaScript, make sure to be more defensive when handling variable types to prevent unexpected results.

Scoping

In short, scope refers to visibility of variables within your code. JavaScript and Apex have some differences when handling scope.

Apex is pretty straightforward with block level scope. You can think of a block as anything between a pair of curly braces. Scope flows outward in Apex. When a variable is used in a block, the compiler searches within that block for its definition. If the variable is not defined in there, the compiler searches outside of that block to see if it is a static or instance property in that class. If it still can find it, then it checks the global scope (which contains the definition for tokens like Test and Schema). If that fail, then you get a compilation error.

This outward flow of scope also explains why in Apex you are able to have some level of duplicate variable names without issue. For example, in this Apex method, the variable i is defined twice: once within the blockScope method and once as an instance variable. The blockScope method uses the i that is declared within the block of the method as opposed to the instance variable defined outside of the scope.

private Integer i = 0;

void blockScope() {
    Integer i = 10;
    System.debug(i);
}

blockScope(); //outputs 10;

In JavaScript, scope depends on how you declare your variables. You are probably most familiar with using the var keyword to declare variables, which provide function level scope.

For example, consider this function where the variable greeting is defined within an if block. The greeting variable is still visible outside of the block due to the function level scoping.

function greet() {
    if(true) {
        var greeting = 'hello!';
    }

    console.log(greeting) //Outputs 'hello!'
}

If you forget to use the var keyword, JavaScript will actually put that variable on global scope (i.e. the window object).

function greet() {
    if(true) {
        greeting = 'hello!';
    }

    console.log(greeting) //Outputs 'hello!'
}

Fortunately, if you write you code with use strict, then strict mode will prevent this from happening and will throw an error

function greet() {
    'use strict'
    if(true) {
        greeting = 'hello!'; //Throws "ReferenceError: greeting is not defined in scope"
    }

    console.log(greeting) 
}

Lightning Locker actually enforces strict mode everywhere so you don’t have to specify use strict, but it is important to understand the mechanism behind this scoping.

ES6 introduced the two new keywords let and const for variable declaration which provide block level scope like in Apex. let is used for variables that you want to be able to reassign values to while const variable values cannot be reassigned. In the following function, the variables greeting1 and greeting2 are not accessible outside of the if block;

function greet() {
    'use strict'
    if(true) {
        let greeting1 = 'hello';
        const greeting2 = 'world!';
    }

    console.log(greeting1) //Throws "Uncaught ReferenceError: greeting1 is not defined"
    console.log(greeting2) //Throws "Uncaught ReferenceError: greeting2 is not defined"
}

In general, I tend to default to const if the variable won’t be reassigned, then I use let if I need to change the variable and only using var when I need function level scope, though I don’t run into many scenarios where that is necessary.

These are some very basic differences between JavaScript and Apex. In the next part, we’ll dive deeper by covering first class functions and how that changes the way you approach writing code in JavaScript compared to Apex.