Salesforce Certification Exam Notes - JavaScript Developer I
JavaScript Developer I consists of two parts, the multiple-choice exam and the Lightning Web Copmonents Specialist Superbadge. This note focuses on the multiple-choice exam.
Variables, Types, and Collections
Variable, Scope, Hoisting
Variables can be defined using var, const, and let. var declarations are hoisted to the top of their scope, whereas const and let are not.
Scope types include global scope, function scope, and block scope. Hoisting occurs in global and function scopes.
The minimum scope for const and let is block scope.
Variable hoisting lifts the variable declarations but not their initializations.
Data types
stringnumberbooleannullundefinedBigIntobjectSymbol
The typeof operator in JavaScript is used to determine the type of a given operand. It returns a string that indicates the type of the unevaluated operand. Note some pecularities including:
typeof null=>"object"typeof undefined=>"undefined"typeof function(){}=>"function"
Type conversions can occur explicitly or implicitly.
- Explicitly:
String(),Number(),Boolean() - Implicitly:
+,ifconditions,==
Any variable can be truthy or falsy. Falsy values include the following 8 values. All other values are considered truthy.
0-00nnullundefined""NaNfalse
Variables can be compared using == or === operators. == performs type coercion, while === does not. Special cases exist, such as +0 === -0 => true, NaN === NaN => false. Object.is() can be used to determine whether a value equals NaN.
Strings
Some frequently used string methods.
toLowerCase()toUpperCase()trim()trimStart()trimEnd()search()slice()split()startsWith()substr(start, length)substring(start, end)replace(regexp|substr, newstr)indexOf(substr)includes(valueToFind[, fromIndex])split(separator)
Template literals `test string ${expression}`
JSON serialization can be done using JSON.stringify and JSON.parse. Be aware that undefined values are ommited. For example, {a:undefined} => {}, [undefined] => [null]
Arrays
Array methods.
[].push(),[].pop(),[].shift(),[].unshift(),[].reverse()[].indexOf(),[].lastIndexOf()[].join()[].splice(start[, deleteCount[, item1, item2 ...]]), return deleted item[].slice([start[, end]]), return sliced arrayconcatmap(callback with return)every,filter,somesort(), convert elements into strings and compare using Unicodesort(compareFunction(a,b)), if return less than 0, a first, if return greater than 0, b first, leave unchangedreduce(callback(accumulator, currentValue, index, array))reduceRight
Objects, Functions, and Classes
”use strict”
Using "use strict" at the top of a JavaScript file will enable strict mode, which catches common coding mistakes and throws exceptions. It prevents actions like undeclared variables, deleting properties from objects, duplication of property names in an object.
"use strict" applies to the current scope, function scope, and global scope.
Loops
for loop, forEach, for-of, for-in (loop over the object attributes), while, do while
this
this refers to the object representing the current execution context. In global scope, it refers to the global object.
Changing the context of this
call, apply, bind are special methods in JavaScript for changing the context of this when a function is called. They belong to Function prototype, so every function in JavaScript automatically has access to them.
apply and call are commonly used for immediate invocation of a function with a custom this. apply expects the arguments as an array. call expects individual arguments to be passed directly.
function greet(greeting, name) {
console.log(greeting + ", " + name + "! My name is " + this.name);
}
const person = { name: "Alice" };
greet.call(person, "Hello", "Bob"); // "Hello, Bob! My name is Alice"
greet.apply(person, ["Hi", "Bob"]); // "Hi, Bob! My name is Alice"
bind is useful when you want to delay the function call but have this set to a specific context, such as when using callbacks or event listeners.
const person = { name: "Alice" };
function sayHello(greeting) {
console.log(greeting + ", " + this.name);
}
const boundGreet = sayHello.bind(person, "Hello");
boundGreet(); // "Hello, Alice"
Example question: var arr = [[1,2],[3,4,5]]; How to get [1,2,3,4,5]?
[].concat(...arr);[].concat.apply([],arr);[].concat.apply(arr,[]);
Arrow functions
Arrow functions are a concise way to write function expressions. They do not have their own this context, instead, they inherit the this value from the enclosing scope. Arrow functions cannot access the global variable arguments.
Closure
A closure allows a function to retain access to variables in its outer scope, even after the outer function has finished executing.
Example question: function outerfunc(){ var x = 1; return function(){return x;}}, why inner function can access x?
Rest parameters, spread syntax
Both rest parameters and spread syntax use the ... syntax. They serve different purposes in JavaScript.
Rest parameters is used in function definitions to collect all remaining arguments into a single array. It can only be used in function parameters.
function sum(arg1, ...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
Spread syntax is used to expand an array or object into individual elements. It can be used in function calls, array literals, and object literals.
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4, 5];
const person = { name: "Alice", age: 30 };
const newPerson = { ...person, city: "New York" };
Object built-in methods
Object.assign() copies the properties from one or more source objects into a target object. This method returns the modified target object. The copy is shallow copy, that is nested objects are not deeply cloned.
Object.assign(target, ...sources);
target: The object to receive properties.sources: One or more objects from which properties will be copied.
Object.keys() returns an array of an object’s own enumerable property keys. Those properties inherited from its prototype will not be returned.
Object.freeze() prevents modifications to an object, making it immutable. After freezing, properties cannot be added, removed, or modified. The freeze is shallow, so nested objects within a frozen object can still be modified.
Object.is() determines if two values are the same (in a similar way to the === operator) but with two key differences: it considers NaN equal to NaN and +0 different from -0.
Object.is(value1, value2);
| Method | Purpose | Example Output |
|---|---|---|
Object.assign() | Copies properties from source(s) to target | {a: 1, b: 2, c: 3} |
Object.keys() | Returns an array of an object’s own enumerable property keys | ["a", "b", "c"] |
Object.freeze() | Prevents modifications to an object | {a: 1, b: 2, c: 3} |
Object.is() | Determines if two values are the same | true |
Prototype, class and extension
function Animal(name){
this.name = name;
}
Animal.prototype.eat = function(){
console.log(this.name,'eats')
};
function Dog(name){
Animal.call(this,name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
is equivalent to
class Animal{
constructor(name){
this.name = name;
}
eat(){
console.log(this.name, 'eats')
}
}
class Dog extends Animal{
constructor(name){
super(name);
}
}
prototype exists on functions (including classes) and define shared properties/methods for instances created by that function, used in inheritance, allowing methods like eat to be defined once and shared across instances.
__proto__ exists on instances and points to the prototype object of the constructor function. It establishes the prototype chain, enabling inheritance of properties and methods up the chain.
In class-based syntax, JavaScript simplifies these concepts by abstracting prototype and __proto__ handling, allowing more readable and maintainable code.
Decorators
Decorators are a proposed feature for classes and class methods that allow you to modify or enhance the behavior of classes or their properties. While they are not offcially part of the JavaScript specification yet, decorators are supported in some JavaScript environments through transpilers like Babel and TypeScript.
Modules
The ES6 (ECMAScript 2015) module system introduces two types of exports: named exports and default exports.
- Named exports are exported as properties of an object, allowing multiple named exports from a single module.
- Default exports are exported as the default export of a module, allowing only one default export per module.
Example:
// Named exports
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// Default export
export default function (a, b) {
return a / b;
}
Browser and Events
Document Object Model
The Document Object Model (DOM) is a programming interface for web documents. It represents the structure of a document as a tree of objects, where each object corresponds to a part of the document. The DOM provides a standardized way to access and manipulate the content, structure, and style of a document.
Example:
const element = document.getElementById("myElement");
element.textContent = "New content";
In the DOM, everything is a node:
- Element Nodes: Represent HTML elements (e.g.,
<div>,<a>,<p>). - Attribute Nodes: Represent attributes of an element.
- Text Nodes: Represent text content within an element.
- Root node: the topmost node in the DOM tree is the document node, which represents the entire document. The document node contains other nodes as its children.
DOM API
JavaScript provides several methods and properties to interact with the DOM.
- Selecting Elements:
document.querySelector(selector),document.querySelectorAll(selector). - Accessing Children:
element.children,element.firstChild,element.lastChild. - Manipulating Content:
element.innerHTML,element.textContent. - Working with Attributes:
element.getAttribute(name),element.setAttribute(name, value) - Styling and Classes:
element.style,element.classList
Example question: How to get the second child of an <a> element? (document.querySelector('a nth-child(2)'))
Events API
Event Handling in JavaScript
Event handler can be assigned in HTML using attributes like onclick, onmouseover, etc. For example
<button onclick="handleClick()">Click Me</button>
Event handler can be assigned to DOM elements using properties. For instance
const button = document.getElementById("myButton");
button.onclick = handleClick;
Event handler can be assigned to DOM elements using addEventListener() method. For instance
const button = document.getElementById("myButton");
button.addEventListener("click", handleClick);
Event types
Load events are triggered when a document or resource has been loaded: DOMContentLoaded, load, beforeunload, unload
You can create and dispatch custom events using the CustomEvent constructor. This allows you to define your own event types with additional details.
document.body.dispatchEvent(new CustomElement('someevent',{detail:xx}));
Event propagation
Events in the DOM can propagate in two ways: bubbling and capturing.
- Event Bubbling: The event starts from the target element (the most specific element) and propagates up to its ancestors. This is the default behavior for most events.
- Event Capturing: The event starts from the topmost element and propagates down to the target element. This can be achieved by setting the third parameter of
addEventListenerto true.
The options parameter can be an object that specifies settings for the event listener: capture, once
Example question: Execute onclick only once. (Using once option and Using removeEventListener)
Browser Object Model
The Browser object model (BOM) specifically refers to the objects provided by the browser that facilitate interactions with the browser itself rather than the HTML or XML content of the page.
It includes global objects like window, document, navigator, location, history, screen, among others.
window:alert,confirm,prompt,setTimeout,setInterval,localstorage(only string is allowed to store),sessionstoragedocument: DOM,getElementById,querySelector,querySelectorAll,addEventListener,removeEventListener,createEvent,dispatchEventnavigator:userAgent,language,online,geolocation,platform,cookieEnabledlocation:href,pathname,search,hashhistory:back,forward,goscreen:width,height,availWidth,availHeight
Debugging and Error Handling
Errors
Errors include syntax errors, which occur when JavaScript code does not conform to the syntax rules of the language and caught by the interpreter before the code is executed, and runtime errors (Exception), which occur during the execution of the code.
The throw statement allows you to throw a built-in or custom error and stop the execution of the current function.
The onerror event handler is used to handle unhandled errors globally in the web application. This can catch syntax errors and runtime errors that are not caught by other try-catch blocks.
Common console methods
console.log('..%d..%f', xx, xx)console.table(x, (columns))console.trace();console.time(),console.timeLog(),console.timeEnd()console.group('groupName'),console.groupEnd('groupName')debugger
Example question: How to measure time spent in function calls? (Use console.time() and console.timeEnd())
Asynchronous Programming
JavaScript execution model
Execution Context is the environment where code runs, it includes Global Execution Context (for the whole script) and Function Execution Contexts (for each function call).
Call stack is a stack data structure that holds execution contexts in a Last-In-First-Out (LIFO) order. The single-threaded nature of JavaScript means it can only process one call stack frame (or execution context) at a time, which is why it is synchronous by default.
Heap is an unstructured memory area where dynamically allocated objects and functions are stored. It is used to store large or complex data structures that persist beyond the immediate execution of the call stack.
Callback Queue or Task queue holds asynchronous callback functions (like setTimeout callbacks, I/O, and event callbacks) once they are ready to run. Callbacks in the queue are processed by the Event Loop when the call stack is empty.
Microtask queue is a separate, prioritized queue specifically for Promise resolutions and some other microtasks (e.g. MutationObserver). The Event loop processes all tasks in the microtask queue before any tasks in the callback queue.
Event Loop is a mechanism that manages asynchronous execution by constantly monitoring the Call Stack and queues, it will move tasks from the callback queue or microtask queue to the call stack when it’s empty, ensuring that async code runs without blocking the main thread.
Example question: The result of this code.
console.log(1);
new Promise((resolve, reject) => {
resolve(2);
}).then(result => {
console.log(result);
});
console.log(3);
Promises
Promise has three states: pending, fulfilled, rejected
new Promise(executor);
var executor = function(resolve,reject){
// async actions // pending
// if completed call
resolve(''); // fulfilled
// if failed
reject(''); // rejected
}
The executor function will be invoked immediately after Promise being created.
Promise methods
promise.then(onResolveCallback, onRejectCallback)
promise.catch(onRejectCallback) // equivalent to promise.then(undefined, onRejectCallback)
promise.finally(onFinally) // equivalent to promise.then(onFinally, onFinally)
The above methods will return Promise.
promise.all: accepts an array of promises as parameters, executed after all promises are settled (either resolved or rejected)
promise.race: accepts an array of promises as parameters, executed after the first promise is settled (either resolved or rejected)
promise.any: accepts an array of promises as parameters, executed after the first promise is fulfilled, or all promises are rejected
async and await
Async/await is the syntax sugar for Promises.
function doSomething(){
return new Promise(onResolve){
doAnotherThing().then(function(result){
onResolve(result);
});
}
}
is equivalent to
async function doSomething(){
const result = await doAnotherThing();
return result;
}
Example question: Which is correct about the usage of async/await (async function always returns a Promise)
Server Side JavaScript
Node.js is a JavaScript runtime that allows you to run JavaScript code outside of a web browser. It provides a way to execute code on the server-side, allowing developers to build scalable and efficient applications.
Node.js uses the require function to load modules. The require function is not part of the JavaScript language, but it is provided by Node.js.
Core modules include http, url, querystring, path, fs, util, events.
Create and starts Node.js web server
Example question: How to effectively calculate and monitor execution time in a Node.js web server?
DEBUG=true;DEBUG=http, https;NODE_DEBUG=true;NODE_DEBUG=http, https;(correct one)
NPM uses major.minor.patch format for module versions, version syntax includes ~version, ^version, version, latest. NPM supports private package.
Testing
Concepts about black box, white box, false/true negative/positive