Skip to main content

Modern ES6 JavaSript Functions

Learning Objectives

After completing this blog, you’ll be able to:

  • Recognize the fat arrow syntax for functions.
  • Describe the scope used with the this keyword.
  • Explain why defining optional parameters in ES6+ results in cleaner code.
  • Describe the different uses for the ‘...’ operator.

The Trouble with This

You’re probably familiar with defining functions like this:

let result = function (i,j) {
  return i+j; 
}
console.log(result(2,3));

Executing that bit of code displays 5 in the console. ES6 introduced a shorter way to define functions using what is called arrow functions. If you are coming from another language such as C#, then arrow functions will look pretty similar to something you know as lambda expressions.  Using the fat arrow symbol ( => ), you can now create the same function using code like this:

let result = (i,j) => i+j; 
console.log(result(2,3));  

All we have done here is remove the function and return keywords and used the new fat arrow symbol instead. The parenthesis are even optional when there is only one parameter, and you only need the curly braces when you have more than one expression. Just remember that if you do include the curly braces, the return keyword is required.

Arrow functions result in less code and remove some of the confusion when dealing with the this keyword, especially when nested functions are involved. Functions have a special variable called this, often referred to as the “dynamic this,” which refers to the object used to invoke the function.

The dynamic nature of this causes problems in certain situations. Take for example, the following code, in which a function is called using an object:

let message = {
  hello : 'Hello',
  names : ['Sue', 'Joe'],
  showMessage: function() {
    this.names.forEach(function(name) {
      console.log(this.hello + ' ' + name);
    });
  }
}
message.showMessage();  

Executing this code in CodePen should display two messages in the console: “undefined Sue” and “undefined Joe.” The variable named hello cannot be referenced inside the nested function because the JavaScript interpreter thinks it is an unsupplied function argument. It has no scope inside of the nested function. Referencing the this keyword inside the nested function just refers to the scope in which the object was invoked, which in this case is global, meaning the variable hello does not exist.

To get around this, you can do something like the following, in which you add a new variable inside of the showMessage function named self. The self variable references what is known as the “lexical scope,” because it was defined within the showMessage function.

let message = {
  hello : 'Hello',
  names : ['Sue', 'Joe'],
  showMessage: function() {
    let self = this;
    this.names.forEach(function(name) {
      console.log(self.hello + ' ' + name);
    });
  }
}
message.showMessage();  

Executing this code displays the right messages in the console: “Hello Sue” and “Hello Joe.” But using the self variable is a workaround. 

The arrow function that ES6 introduced has the lexical scope built in. So, we can replace the code above with the following and it will work as expected, without having to declare an extra variable to represent this.

let message = {
  hello : 'Hello',
  names : ['Sue', 'Joe'],
  showMessage: function() {
    this.names.forEach(name => {
      console.log(this.hello + ' ' + name);
    });
  }
}
message.showMessage()  

Better Parameter Handling

Prior to ES6, parameter handling in functions was tedious. To ensure that your code ran as expected, you often had to add manual checks within the function for any optional parameters. For example, consider a function that has two parameters. Since the user may not enter the second parameter, you’d need to add a line of code to the function to check whether they did.

function helloMessage (param1, param2) {
  param2 = param2 || 'World';
  return param1 + ' ' + param2;
}
console.log(helloMessage('Hello'));  //Displays "Hello World"  

ES6 offers better ways to handle function parameters. You can now specify default parameter values via an equal sign ( = ) in the parameter list. For the helloMessage function, the second parameter is optional, but you don’t need that extra line of code inside the function.

function helloMessage (param1, param2 = 'World') {
  return param1 + ' ' + param2;
}
console.log(helloMessage('Hello'));  //Displays "Hello World"  

You can even simulate named parameters by utilizing the object destructuring syntax you learned about in the last unit. For example, consider this function with two parameters.

function showMessage(who, {p1 = "Hello", p2="World"} = {}) {
  console.log(who + ' says ' + p1 + ' ' + p2);
}
showMessage("Trailhead");  //Displays "Trailhead says Hello World"  

The second parameter is just an object that is specified with the destructuring syntax. But notice how there is an equal sign followed by empty curly braces. This enables you to call the function without parameters. And this is important because without that equals sign, you would get a TypeError when trying to run that last function without all the parameters.

But what if you had a function with an unknown number of arguments? In ES5 you could use the arguments variable. But the arguments variable was a symbol, not an array, and using it was not easy. 

ES6 introduced a better way to access these remaining unknown parameters using rest. Get it?  “Rest,” as in “give me the rest of the parameters.” Rest parameters are indicated with three dots ( … ) and they can appear only at the end of the argument list. For example: 

function showContact (firstName, lastName, ...titles)  {
  console.log(firstName + ' ' + lastName + ', ' + titles[0] + ' and ' + titles[1]);
}
showContact('Sue', 'Johnson', 'Developer', 'Architect');  

Executing this code would result in the message, “Sue Johnson, Developer and Architect” displayed in the console. If you left off the last parameter in the function call, you would get “Sue Johnson, Developer and undefined” instead.

Two Uses for the Same Thing?

Now that you understand how rest parameters work, here’s a question: What do you think this code prints to the console?

let array1 = ['one', 'two'];
let array2 = ['three', 'four'];
array1.push(...array2);
console.log(...array1);  

Did you guess, “one”, two”, “three”, “four”?

If you did, then you understand how the spread operator works. And now are you thinking, “Wait, what did you say. Spread? I thought it was called rest.”

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 is used to expand a single variable into an array. In both cases, it is working with arrays. It is just either expanding or collapsing that array. Make sense?

Comments

Popular posts from this blog

Get started with Simple Lightning Web Component(Bike Card)

Lightning web component HTML files all include the template tag. The template tag contains the HTML that defines the structure of your component. Let’s look at the HTML for a simplified version of the component. Paste the following into app.html (replacing any existing HTML in the file). bikeCard.html < template > < div id = "waiting" if : false = { ready } > Loading.. </ div > < div id = "dispaly" if : true = { ready } > < div > Name: {name} </ div > < div > Description: {description} </ div > < div > Category: {category} </ div > < div > Material: {material} </ div > < div > Price: {price} </ div > < div >< img src = { pictureURL } /></ div > </ div > </ template > The identifiers in the curly braces {} are bound to the fields of the same name in the corresponding JavaScript class. Here’s a JavaScript file to s...

Disable Document Download in Salesforce

Disable downloads for certain users based on the name of the file uploaded to Salesforce. We can use the following example code inside of a Apex class. This code essentially prevents files whose file name starts off with Kaipu- from being downloaded by anyone whose user role’s developer name is not Kaipu_Sales. Modify this code to suit your own purpose. Based on Role: public class ContentDownloadHandlerFactoryImpl implements Sfc.ContentDownloadHandlerFactory {    public Sfc.ContentDownloadHandler getContentDownloadHandler(List<ID> ids, Sfc.ContentDownloadContext context) {      // See if the user has the Kaipu Sales role (based on developer name field).      Boolean isSecretUser = [        SELECT Id        FROM UserRole        WHERE ID = :UserInfo.getUserRoleId()          AND DeveloperName = ' Kaip...

Commonly asked Lightning Developer Interview Questions & Answers

1. Where can we use Lightning Components? We can use Lightning Components in the following places: Drag-and-drop Components in the Lightning App Builder and Community Builder. Add Lightning Components to Lightning Pages. Add Lightning Components to Lightning Experience Record Pages. Launch a Lightning Component as a Quick Action Override Standard Actions with Lightning Components Create Stand-Alone Apps 2. How do you build Lightning Components? We can build Lightning Components using two programming models: the Lightning Web Components model, and the original Aura Components model. 3. How can you create Lightning Record Pages in Salesforce, and what are the different types? We can make use of Lightning App Builder for creating Lightning Record Pages, to create the following three types of pages: App Page Home Page Record Page 4. What options are there for Lightning Record Page assignment? “Lightning Pages” can be assigned at three different levels: The org default App default – this ov...