002 JavaScript -
002 JavaScript -
com/content/learn/trails/learn-to-work-with-javascript
JavaScript was primarily a way to make web-pages somewhat interactive, and to execute
rudimentary logic without a server trip. Server-side frameworks were extremely popular,
including the Salesforce framework Visualforce. As testimony to this popularity, there are
millions of Visualforce pages live in Salesforce today.
NOW (as of February 2024) JavaScript is well standardized around the ECMAScript standard,
and top browser manufacturers are generally good about adopting new features. Web
applications today have rich user interfaces running modern JavaScript in the browser.
Instead of server-side frameworks, modern web applications tend to be client-side rendered.
In Salesforce, this is done with the Lightning Component Framework. But writing JavaScript is
still new for many developers. If you’ve worked mostly with Visualforce pages and Apex
controllers, you might need a leg up to really get how JavaScript works so you can better
understand your components.
The JavaScript runtime is an engine that interprets JavaScript code. It can be part of a
browser, or other runtime environment like a server. The defining feature of the JavaScript
engine is a single-threaded runtime represented by the stack below. Work being done in the
stack owns the thread and must complete its synchronous logic before it hands back control
of the thread.
The runtime is a busy place. New work can come in at any time from a number of
origins, including the user (UI events) and web APIs (such as geolocation or device
motion). Since there’s only one thread, there is a queue where work awaits its turn to
use the thread.
When the stack is empty, the event loop takes work that is waiting to be done from
the queue and moves it into the stack.
*JavaScript is built according to the ECMAScript standard and it’s constantly changing and
being updated.
All variables are pointers. Assignment is the act of pointing that variable to something in
memory. Mutability is whether or not a variable can be reassigned once it has initially been
assigned something. Using var or let creates mutable pointers, whereas const is
immutable.
In the first example the * operator can only be used for math operations, using the
string "3" to a number. The outcome is 27. In the second, the + operator sees the
string "3" making it a mathematical operator . This use 9 to the string "9" with an
outcome of the string "93" .
5) For Boolean comparison the best practice is to use === and !==
When an expression expects a Boolean value, the following values are always treated
as false.
• false (of course)
7) this Is Tricky: this has well-defined rules, but what this points to can change
even within the same function. For instance, in an Apex class you might
see:
In this example Apex class, this only ever refers to the current instance of MyApexClass . In
JavaScript, what this points to is not determined by where a function is defined, but rather
where that function is called. More on this later.
8) Functions Are Values: In JavaScript, everything is an object. This goes for functions
too. And like other objects, functions can be assigned to variables, passed into
parameters of other functions, and used the same way you can any other object.
Likewise, while a script is executing it can also reach back to the browser to do things like
modify the web page, interact with the local environment, or interact with other web
resources. To make these interactions work, the browser surfaces APIs. In fact, a large part of
what people think of as client-side JavaScript is actually these APIs
Web APIs:
While over half of browser internet traffic comes from Google Chrome, roughly another 30
percent comes from five other browsers. This underscores the importance of the web
standards for these APIs, so that JavaScript can be written once and run anywhere.
For example: You have a list of notes on a web page with a form. You fill out some
fields on the form, click a button to save that data, and a new item is added to the
list. For fun, let’s say also that you will cache this new record locally to speed up
future requests involving that record.
This simple use case would interact with the following browser APIs
• Fetch API (to save the record)
• DOM API (to add a new item to an HTML list)
• Client-Data Storage (to cache the data locally)
When a page is requested and then received by a browser, the browser analyze the HTML
and creates a description, or a model, of that page. This model is used to draw the page in
the browser’s viewport. It’s also surfaced to JavaScript through the DOM.
Think of the DOM as a tree. It starts at the root of the browser’s display functionality: the
window. From there, the page is encapsulated in window.document , with the page’s body
in window.document.body . Then the tree fans out to every bit of content represented on the
page. Most web pages have a very complex tree with many nodes finally ending in leaf
nodes as the most granular pieces of the
hierarchy.
As an API, the DOM is vast, and lets you touch every part of this tree. It also has a
number of methods to optimize interaction with the DOM. Take a look at this
simple example on jsbin.com. It includes:
• An input field
• An Add Item button
• An unordered list
Each time you click the button, the code reaches into the input field, reads its value,
converts it to a text node, creates a new li element, inserts the text node into
the li node, and finally sticks the new li -and-text node into the ul .
As you can see, just this simple example requires the developer to perform a bunch of
manual steps. And it doesn’t even store any data on a server, or interact with a server in any
other way. For this reason, JavaScript libraries (reactjs, jQuery) and frameworks (Angular,
vuejs) have become the standard for interactive pages. Such frameworks abstract away and
simplify DOM interactions, and often automatically apply polyfills (A polyfill - allows web
developers to use an API regardless of whether or not it is supported by a browser, and usually with
minimal overhead. Typically they first check if a browser supports an API, and use it if available,
otherwise using their own implementation). for missing features. The Lightning Component
Framework is no different.
The DOM API is rich and flexible. Using relatively simple JavaScript it is easy to make
changes to the looks, behaviors, and actions invoked by the UI.
But there is a pitfall. The DOM model makes it difficult to encapsulate pieces of the
UI and protect them from accidental (or purposeful and malicious) changes.
For this reason, the Shadow DOM standard was developed. Shadow DOM creates a
boundary around a particular part of UI functionality. The boundary prevents a
parent from changing the elements or CSS of a child. It also forces any events
propagated across the boundary to rescope their targets, preventing the parent from
reaching across the shadow DOM boundary.
If you want the UI to change when the user interacts with it:
Lightning Web Components make it effortless to render data into UI. For example,
the code above uses the JavaScript property text to store a hard coded string. In
the <lightning-formatted-text> tag, the value attribute is bound to the
JavaScript text property, rendering the data in the UI.
But what if you want the UI to change when the user interacts with it? We can
extend this example to do just that. https://youtu.be/5KRjiCArONM
In a Lightning web component, JavaScript class properties are reactive. This means
when a property’s value is assigned (or reassigned), and the property is used in a
template, the component's DOM re-renders and displays the new value.
Manual DOM manipulation shouldn't be your first strategy when building a Lightning
web component, but more sophisticated components may require you to do so. This
is a more advanced topic, but it’s worth scratching the surface of how this works
before signing off on the DOM.
Conditional Rendering
When writing Lightning web components, you can explicitly define sections of the UI
that are rendered only when certain conditions are met. You can do this by using
the lwc:if , lwc:elseif , and lwc:else directives with nested template tags.
In this code sample, the text Peek-a-boo! appears when the show property has a
value of true . When show is set to false , the text I’m hiding! appears. Clicking the
button toggles the value of the show property.
• Objects don’t have classes the way an Apex, Java, or C# developer might think
of them.
• Each object inherits from another object.
• Objects are mutable.
• Objects get their own variable context when they’re created.
Creating Objects:
Syntactically speaking, there are several ways to create an object in JavaScript. But no
matter how you create an object, it’s actually abstracting an underlying API
called Object.create() .
In some instances there’s good reason to use Object.create() directly, but we won’t
cover that here. Instead, let’s look at more common ways to create objects.
Object literal notation is declarative in nature. The object bike in this example has three
members: the gears and currentGear properties and the changeGear function. To reference
those members once the object has been created, use dot notation.
Literal objects are great for one-off objects. But if you want to create two or more objects of
the same type, they are not practical. For that, you need repeatable logic to create new
objects.
Syntax-wise, object literal notation and constructors are pretty different. But in each
case you still end up with a new object created in memory, with the variable bike as
a pointer to that object. With the constructor, you can make lots of Bike objects with
the same properties and functions.
If you understood from the bike examples above that there are two possible types of
members in an object—properties and functions—you would be correct.
• Primitives
• Objects
• Arrays
At the time of the writing of this module, there are seven primitive types in
JavaScript: string, number, Boolean, null , undefined , symbol, and bigint.
Primitive types are immutable. When a variable is a primitive type, it’s passed by
value when assigned. That is to say, each time a primitive is assigned, a copy of the
value is made and assigned to the new variable.
Pretty much anything that isn’t a primitive in JavaScript is an object. In object literal
notation, object properties are denoted by curly brackets.
Functions have their own unit in this module, so we won’t talk about them here, but
based on the above, let’s take another pass at defining a more complex bike object
using object literal notation.
Referencing Properties by Bracket Syntax - Referencing an object member is most commonly done
with dot notation: bike.frontGearIndex
In dot notation, there are strict rules for the names of properties. However, JavaScript also
allows for another syntax called bracket notation. The members above would be referenced
as follows in bracket notation.
Bracket notation has two benefits. You can name your property or function anything you
want, and because it’s a string, you can pass a property or function name through a variable
and call it.
Object Mutability:
Objects in JavaScript are mutable, which means that if you want to modify the shape
of an object, you can.
Let’s take the bike object we created. We could, for instance, add a new property or
function. Even though you may not have access to the code where the object is
initially defined, you can modify the shape of your object once it’s in memory. The
important point, though, is that only one instance of the object changes. Let’s look
back at our Bike constructor:
Despite not having classes as defined by classical languages, JavaScript still has an
inheritance model, called prototype inheritance.
Traditionally in JavaScript objects share the same prototype by sharing the same
constructor function. Remember the Bike constructor. We assign
the changeGear function to something called prototype .
This way every object created from Bike inherits the changeGear function.
The class keyword in JavaScript is a nice bit of syntactic sugar to address the
complexities of prototype inheritance using constructor functions. Under the covers,
the engine is still using Object.create and there is still no class (in the object-oriented
sense), just that in-memory prototype object that is the actual source of inheritance.
The good news is that it does read a lot more like code from Java or C#, with a few
JavaScript-specific things to take into account.
An important feature is that functions and attributes automatically belong to the prototype
chain without having to directly reference Object.prototype. This also simplifies creating
multilevel prototype inheritance.
Lightning Web Components and Objects
To make something happen in a web page, functions get assigned to these events
as event handlers.
- DOM events and other events related to the browser environment are not actually
part of the core JavaScript language, rather they are APIs that are implemented for
JavaScript in the browser.
In JavaScript functions are essentially special objects. As objects, they are first-class
members of JavaScript. They can be assigned as the values of variables, passed into
other functions as parameters, and returned from functions.
There are two essential phases in the life of a function: definition and invocation.
When function is declared, its definition is loaded into memory. A pointer is then
assigned to it in the form of a variable name, parameter name, or an object property.
It should be no surprise, however, that there are several different syntaxes to do this.
Function Declaration
In this code sample, function is followed by the name of the function, with
parameters enclosed in the parentheses.
This works fine, but there’s some implicit stuff happening. First of all, the function
name becomes the variable name. It also implicitly assigns the variable to the
enclosing context. Finally, you can call this function before it is declared, such as
below where calculateGearRatio is invoked the line before the declaration.
Function Expressions
Functions in Lightning web components most often come in the form of methods
that are members of the class exported by the component’s JavaScript module.
These functions can be event handlers or other functions invoked downstream from
there.
The HTML template references handler functions in a way that resembles static HTML
binding, but in fact it's different. Because the template is compiled into a JavaScript
artifact, the static-looking binding is in fact just a syntactic convention the framework
uses for invoking addEventListener sometime in the lifecycle of your component.
When JavaScript is executed without any containing object that you write as a
developer, it runs in a global object. For this reason, functions invoked there are said
to be running in the global context, which means that accessing this will point
there.
In a browser, the global context is the window object. You can test this easily by
running the following in your browser developer tools.
When we attempt to assign to this.aValue with the new context, the mutable nature of
JavaScript objects comes into play. A new uninitialized aValue property is added to this .
Performing math on an uninitialized variable fails, thus the NaN value. But we can see
that aValue exists on window , and is indeed a number.
In the increment example, as long as the increment function is invoked using obj with
the dot notation, this points to obj . Or, generally speaking, when calling a function
as someObject.function() the thing to the left of the dot is the context in which that
function is invoked.
Think about the Bike example. The Bike constructor defines several properties with
the this reference. It also has functions assigned to its prototype that
reference this .
When we invoke any of the functions they are now members of the bike object, so
they use that as the containing context.
Closures:
If you load this HTML page in your browser, you find that the alert pop up displays
first and then blocks the display of the HTML. This is because the alert() function
halts execution of the JavaScript thread until the user dismisses it. In short, when
JavaScript blocks your browser, it is never a good user experience.
The good news is, apart from a few legacy bits that linger like the alert() function
above, JavaScript is an asynchronous language.
Let’s revisit events and functions. Previously we looked at some HTML and JavaScript like
this.
In this example we added handleClick as an event handler to the click event emitted
by the button.
When the event fires, all that happens is a new message is added to the queue. No
event has the ability to take over the thread. Each event fired must get in the queue
and wait its turn to run.
One way to illustrate this is by using the setTimeout function. In this example,
invoking setTimeout , we pass an event handler and a timer in milliseconds. When the
timer is up it fires, adding the event handler to the queue.
Another common mistake is to think the timer is an exact predictor of when the event
handler will fire, but it isn’t necessarily. The event handler still has to wait its turn in the
queue. By measuring time, we can see this in action.
The time is set to one second, and it runs pretty close to that. But clearly there is
some variance in how quickly the function can be added to the queue and then run
each time it’s called.
Now that we’ve seen some examples of asynchronous calls, we can look at some
common asynchronous patterns and constructs.
Callback Pattern:
Promising Stuff:
Promises developed as libraries to handle asynchronous code in a way that made it easier to
reason about when your code succeeded or failed. They also contain built-in mechanisms to
chain one call after the other. Competing libraries eventually standardized in the browser as
the Promise object. Let’s morph bike one
more time.
First, the updated changeGearAsync function takes in the data we pass it and returns a
new Promise object. We pass in a single argument: a callback function that itself has
two functions passed to it, resolve and reject .
If the callback function itself returns an instance of Promise , that’s when things get
exciting. You can simply chain those two promise functions together. For instance, if
we want to change both the front and rear gears.
Async/Await
Before signing off, it’s worth mentioning some newer additions to the asynchronous
arsenal: the async and await operators. These build on promises, allowing them to
be used in a way that much more closely resembles synchronous JavaScript.
Lightning Web Components and Asynchronous JavaScript - Interacting with Salesforce:
Lightning Web Components enables the developer to make use of both promise-
based asynchronous functionality and the async/await functionality.
There are several features implemented in Lightning Web Components that use
asynchronous JavaScript. Most of these revolve around interaction with the server.
One example is the way an Apex method can be invoked imperatively in a Lightning
Web Component.
When we call import findContacts… this is standard module syntax to include the
functionality of another module in this component. We surface the findContacts Apex
method here as a JS function of the same name.
When we invoke it in the handleSearch() function, the parameters for the Apex
method are passed as a literal object, and then we see the familiar promise-based
syntax of a then and a catch function.
{JavaScript history in theory} - All of a sudden developers realized that JavaScript was more
than just a scripting language to play around with. Developers began to see it as something
that they can use to put together complex web applications.
Not all major web and mobile browsers support the latest ECMAScript release. Even though
there are ways around browser limitations using transpilers and polyfills, these introduce
more complexity and can cause performance issues.
The good news though - browsers are finally catching up, and many of the ES6 features that
were once available only with the use of a transpiler such as Babel or Traceur are now good
to go in the latest versions of major browsers, such as Google Chrome. YaY!~
Variables declared with the var keyword are said to be in the function scope. This
means that a variable would only exist within the scope of the function in which it
was declared. Or the nearest parent function, if it's a nested function.
That makes sense. And it still makes sense when you consider global scope, in which
a variable is declared outside of a function. Take, for example, the following code.
You should see both 1 and 2 in the console. First 2 and then 1. Because of function
scope, 2 is printed when the function is called. And because of global scope, 1 is
displayed the second time, when the variable is written to the log outside the
function.
Most developers get this, and everything is great until they encounter code
containing an if statement, such as this example.
Executing that code results in the number 2 appearing twice, because the fact that
the var keyword does not support block scope. A block is any code within curly
braces. Block scoping ensures that any variables defined within those braces don't
become global variables. They instead have local scope. Having this type of control
over how variables are scoped helps you prevent unexpected behaviour in your code.
Hoisting occurs when the JavaScript interpreter makes two passes at your code. In the first
pass, variable and function declarations are “hoisted” to the top of the code. And in the
second pass they are evaluated and assignments are made. If only we had a nickel for every
time that poorly understood behavior has caused a bug.
many JavaScript developers now use let almost exclusively. And we suggest you do too.
Using block-scoped variables is not only less error-prone, but makes it easier for other
developers to know how a variable was meant to be scoped.
And There's Also Const
Useful when you need to declare a variable that cannot be redeclared or reassigned.
Essentially, it's read-only.
However, there are a couple of things to be aware of when using the const keyword.
Since const values cannot be reassigned, they must be initialized at the time they are
declared. If you tried to execute the following code, you would get an error telling
you either unexpected token or that it was missing an initializer in
the const declaration.
• Constants are not immutable. This means that it is possible to modify the
properties of objects or arrays assigned with const . For example, if you
declare an object such as this:
*** Don't forget that when dealing with objects or arrays, only the object itself cannot
be reassigned. Properties within that object or array can be changed.
The brackets on the left side of the assignment are part of the new destructuring
syntax. So, this code is the same thing as saying, “Give me four variables named one,
two, three, and four, and assign the first value in the numbers array to variable one,
the second value to variable two, and so on.” Shorter, sweeter, great.
But now let's say that you want to get just the third value, and you are not really
interested in assigning variables to the first two elements. You can still use this
syntax:
A Better Error Message
Two Uses for the Same Thing? ... Three dots Operator
The three dots ( ... ) operator has two uses. As the rest operator, it is used to gather up all
the remaining arguments into an array. But as the spread operator, it allows an iterable such
as an array or string to be expanded in places where zero or more arguments or elements
are expected, or an object to be expanded in places where zero or more key-value pairs are
expected. It is just either expanding or collapsing that iterable. Arrays can be spread into
objects but objects can't be spread into arrays.
In an attempt to make JavaScript at least appear to work like a class-based language, ES6
introduced the class keyword. Using this new syntax means that the Animal class can now
be defined like this:
What is important to know is that even though the class keyword is used, the underlying
object created is still a function. Executing the following code would show “function” and not
“class” in the console as the type.
Instantiating and using methods from the class works almost the same as it did for
constructor functions. Using the new keyword was optional for ES5 constructor
functions, but now it’s required. If you try to leave off the new keyword when working
with classes, a TypeError will be thrown.
Another difference is that you can call a function that has yet to be declared. Classes
do not allow this sort of thing. A class can only be accessed after its definition is
evaluated.
• Classes can contain the following kinds of members: Constructor, Static Methods,
Prototype Methods, Getters and Setters.
Assume you needed to create a subclass named Child that extends the functionality
available in the Parent class. It might look something like this: (Copy below the previous code)
The extends keyword in the Child class definition tells you it's a derived class.
Also notice the use of the super keyword, which allows you to reference the parent
constructor and the method definitions from the base class. Whenever you see
the super keyword, you know you are in a derived class and referring to the base
class.
Tell Me More
• Although commas are used to separate method definitions in objects, they are
not allowed in classes.
• Classes can also be defined using expressions:
ES6 introduced a long-overdue native module system. But it was separate from all the other
ES6 functionality and for a long time, no major browsers supported it. However, that's finally
changed and now most browsers allow you to load ES6 modules with
the type="module" attribute on the HTML5 script tag.
Example
Different Ways to Use Modules - But what if you wanted to rename the functions and
variables and use different names? You can do this using an alias!
*If you try to use the msg1 variable name you get a reference error
Check this:
If you export a variable and then try to change the value in the imported module, you get an
error. Essentially, it's read-only.
The thing to remember here is that you are not allowed to reassign the exported value. It can
only be changed from inside the module it was exported from.
• Modules always execute in strict mode, which means that variables need to be
declared.
• They only get executed once, which is right when they are loaded.
• Import statements get hoisted, which means that all dependencies will
execute right when the module is loaded.
• To access an exported variable that was imported as a single object – Use the
keyword to assign an alias AND reference the alias and property name.
Using callback functions was one of the ways you did this in JavaScript. A callback is just a
function that executes after another function has finished executing. For example, if you were
to run the following code in PlayCode and then look at the Console, you would see the
message, "1st Call".
it would take 1 second or 1000 milliseconds for that to happen since
the doSomething function uses the browsers built-in setTimeout method to simulate a
delayed response. This kind of code works fine and most developers can understand it. But
what happens if you want to call this function multiple times, but only after the previous call
finishes? And what happens if one of those callback functions fails?
The pyramid of doom is typically used to identify asynchronous code that is deeply nested,
which tends to result in a pyramid-looking shape. Basically it's code built from several
dependent asynchronous functions that can all potentially have errors. Trust us, code like this
can get real messy, real quick. – AVOID THIS!
A promise is just that: a promise to return something at a later time. Either the thing you
wanted is returned, or an error. ES6 introduced promises natively to JavaScript in the form of
a Promise object and they are based on the Promises/A+ open standard and offer many
advantages to traditional callback functions. The most important advantage is how easy it is
to chain asynchronous functions together.
Let's start by rewriting the doSomething function so that it uses ES6 promises.
The actual doSomething function, now returns a new Promise object. And instead of calling
the callback , the function now calls resolve .
The cool thing here is how the doSomething function is called. We can now use
the then method to specify what gets called only after the first function completes. Running
this code should result in three messages displayed in the Console, "1st Call" followed by
"2nd Call" and then "3rd Call".
Notice that we added the catch method to the very end of the call and if triggered, the error
object is returned. To print out the error message, we need to specify the message property
name.
• calling the error function at the bottom of the chain using the catch method is a best
practice since it catches all errors produced from the chain.
Another Way to Await and See - async functions
The ES2016+ release introduced async functions and a different way of calling native
promises. The structure of the promise remains the same, but what changes is how the
promise is called. The call is wrapped in a function that uses the async and await keywords.
What is returned is a promise object that contains either the resolved or rejected value. For
the doSomething function, the call looks like this:
Tell Me More
• The Promise object includes four methods that you may want to check out for
more advanced promise scenarios. They include:
o Promise.all(iterable)—Returns promise only after all the promises in
the iterable have resolved or any are rejected.
Behavior-Driven Development
You then use a behavior-driven tool to create a test script. The script should contain
natural language sentences that essentially describe what the test is supposed to do.
It should also contain assertions to see whether the test is working.
One of the more popular behavior-driven tools is the easy to use – Jasmine.
In Jasmine you use a describe function to create a test suite, but you're really just
creating a JavaScript function. Inside of that function, use the it function to specify
one or more specs (or tests), which are also just functions. Inside of the spec function,
put assertions to check whether the test worked.
Jasmine includes global functions ( beforeEach and afterEach ) that can be used to execute
code before and after each of your test specs have run. This is useful for when you have set
up and tear down code that requires a lot of resources, like logging in and out as a certain
user. There are also beforeAll and afterAll functions, but these are run only once for each
describe block they are part of.
he installation page for Jasmine provides a few different ways to install it. You can
also run it stand-alone by downloading the latest version from the GitHub repo and
then copying and unzipping the entire contents of the lib folder to your JavaScript
project directory.
Once you have written your JavaScript test suite, you can reference this, along with
the necessary Jasmine framework files from an HTML page to run the tests in your
browser.
To see how it works, let's assume you want to create a Jasmine test suite to test the
Parent/Child class code you saw in the Working with Classes unit. In case you don't
remember, the code for the Parent class looked like this:
Once you've downloaded and extracted the Jasmine lib folder, create a new folder on
your local machine. Copy the lib folder you extracted into the folder you just created.
This is also where you place the JavaScript code you want to test, which in this case is
the parent and child classes.
You can add the JavaScript code for the Jasmine test suite to a separate file. It might
look something like this:
The only thing left to do is to create a simple index.html file that loads all the required
Jasmine files, along with your JavaScript files. That markup code might look something like
the this: