DEV Community

John Au-Yeung
John Au-Yeung

Posted on • Updated on

Organizing Your JavaScript Code with Functions

Subscribe to my email list now at http://jauyeung.net/subscribe/

Follow me on Twitter at https://twitter.com/AuMayeung

Many more articles at https://medium.com/@hohanga

Functions are small blocks of code that take in some inputs and may return some output or have side effects, which means that it modifies some variables outside the function. We need functions to organize code into small blocks that are reusable. Without functions, if we want to re-run a piece of code, we have to copy it in different places. Functions are critical to any JavaScript program.

Defining Functions

To define a function, we can do the following:

function add(a, b){  
  return a + b;  
}

The function above adds 2 numbers together and returns the sum. a and b are parameters, which lets us pass in arguments into the function to compute and combine them. add is the name of the function, which is optional in JavaScript. The return statement lets the result of the function be sent to another variable or as an argument of another function. This is not the only way to define a function. Another way is to write it as an arrow function:

const add = (a, b) => a + b;

The 2 are equivalent. However, if we have to manipulate this in the function, then the 2 aren’t the same since the arrow function will not change the value of this inside the function, like the function defined with the function keyword does. We can assign the function to a variable since functions are objects in JavaScript. Note that the function above only works if it’s one line since returning is implicitly done if the arrow function is one line. If it’s more than one line, then we write it with brackets like so:

const add = (a, b) => {  
  return a + b;  
}

This way we have to write the return statement explicitly. Otherwise, it won’t return a value.

A third way to define a function is to write:

const add = new Function('a', 'b', 'return a + b');

This allows us to define functions dynamically since all the arguments are strings. However, this is not recommended in most cases since it lets malicious users inject code into your programs if you’re not careful in sanitizing the inputs. Also, it’s a lot harder to read if your function has multiple lines. There almost no need to do this unless you have to define functions dynamically.

Calling a Function

If we are referencing and using a function, then we are calling a function.

To call a the function, we write:

add(1, 2) // 3

Since our function returns a value, if we console log the return value of the add function:

console.log(add(1, 2)) // logs 3

We can also assign the return value to a variable:

const sum = add(1, 2);  
console.log(sum) // logs 3

Part of a Function

All functions have some or all of the following parts:

  • function keyword, which is optional
  • function name, which is optional
  • parentheses — this is required for any function.
  • parameter inside the parentheses, which is optional
  • opening and closing curly brackets — required for all functions except for single line arrow functions
  • return statement, which is optional. If a function doesn’t have a return statement, it returns undefined. A return statement which has nothing follow it will end the executing of the function, so it’s handy for controlling the flow of your function.

Using Arguments

As we can see, many functions have arguments. Arguments are data that is passed into a function for computation, so if we have a function call add(1, 2), then 1 and 2 are the arguments. On the other hand, parameters are what we write in the parentheses when we define a function to clarify what we can pass in as arguments. We do not have to define a function parameters to pass in arguments since we have the arguments object in each function. However, it is not recommended since it’s unclear what you want to pass in. We can use the arguments for optional things that we want to pass in, however.

For example, if we go back to the add function we defined above:

function add(a, b){  
  return a + b;  
}

a and b are parameters. When we call it by writing add(1, 2) . 1 and 2 are arguments.

We can specify up to 255 parameters when we define a function. However, we shouldn’t do define more than 5 usually since it becomes hard to read.

Pass in Arguments by Value

There are 2 ways to pass in arguments in JavaScript. One way is to pass in arguments by value. Passing by value means that the arguments passed in are completely separate from the variables you pass in. The content of the variable is copied into the argument and is completely separate from the original variable if we pass in a variable to a function. The original variable won’t change even if we change the argument variables that we passed in. Primitive data types like string, number, boolean, undefined and the null object are passed in by value in JavaScript.

For instance, if we have the following code:

const a = 1;  
const addOne = (num) => num + 1  
const b = addOne(a);  
console.log(b);

a would still be 1 even after we call addOne on a since we make a copy of a and set it to num when we are passing in a as the argument for the call to addOne.

Pass in Arguments by Reference

Non-primitive are passed into a function by reference, which means that the reference to the object passed in as the argument is passed into the function. The copy of the content is not made for the argument and the passed in object is modified directly.

For example, if we have the following code:

let changeObj = (obj) => obj.foo = 'bar'  
const obj = {  
  foo: 'baz'  
}  
changeObj(obj);  
console.log(obj); // logs {foo: "bar"}

The original obj object is defined as { foo: 'baz' }. However, when we pass obj into the changeObj function, where the passed in obj argument is changed in place. The original obj object that we passed in is changed. obj.foo becomes 'bar' instead of 'baz' as it’s originally defined.

Missing Arguments

You do not need to pass in all the arguments into a function in JavaScript. Whatever argument that’s not passed in will have undefined set in its place. So if you don’t pass in all the arguments, then you have to check for undefined in the arguments so that you don’t get unexpected results. For example, with the add function that we had:

function add(a, b){  
  return a + b;  
}

If we call add without the second argument by writing add(1), then we get NaN since 1 + undefined is not a number.

Default Function Parameter Values

We can set default function parameters for optional parameters to avoid unexpected results. With the add function, if we want to make b optional then we can set a default value to b. The best way to do it is:

function add(a, b = 1){  
  return a + b;  
}

In the code above, we set b to 1 so that we if we don’t pass in anything for b, we automatically set b to 1. So if we run add(1) we get 2 instead of NaN.

An older, alternative way to do this is to check if the type of the parameter is undefined like so:

function add(a, b){  
  if (typeof b === 'undefined'){  
    b = 1;  
  }  
  return a + b;  
}

This achieves the same purpose as the first function, but the syntax is clumsier.

Calling Functions with More Arguments that Parameters

In JavaScript, we can also call a function with more arguments than parameters. If we just pass them in without getting them from the argument object, they’ll be ignored. You can get the extra arguments that aren’t in the parameters with the argument object and use them. The argument object has the parameters with numerical keys just like the indexes of an array.

For example, if we call the add function with extra parameters:

function add(a, b){  
  console.log(arguments);  
  return a + b;  
}  
add(1,2,3)

We should get:

0: 1  
1: 2  
2: 3

in the console.log call, in addition to the length, which should be logged as 3.

Variable Scope in Functions

Functions inside shouldn’t be accessible outside of functions unless they are global variables. We should avoid defining global variables as much as we can to avoid bugs and hard to trace errors since they can be accessed anywhere in the program. To prevent defining global variables, we should use let to define variables and const to define constants. For example, we should define functions like so:

function add(a, b){  
  let sum = a + b;  
  return sum;  
}

In this case, we have sum, which is only accessible within the function since it’s defined with the let keyword.

Anonymous Functions

Anonymous are functions with no names. Since they have no name, they cannot be referenced anywhere. They are often passed into other functions as callback functions, which is called when the function is passed into an argument. However, you can assign anonymous functions into a variable so it becomes a named function.

They can also be self-executing. This is means that you can define the function and make it run immediately. For example, if we write:

const sum = (function(a, b){  
  return a + b;  
})(1, 2);
console.log(sum) // log 3

We log 3 because we defined a function to add 2 numbers, and then passed in 1 and 2 as the arguments immediately after by wrapping the function in parenthesis and then passed the arguments to it.

Recursion

You can call the same function from within itself in JavaScript. This is called recursion. All recursive functions must have an end condition, which is called the base case so that it knows when it stops executing. Otherwise, you can get a function that’s called an infinite number of times, which will crash the browser.

To write a recursive function, we can write:

function sumOfSquares(num) {  
  let sum = Math.pow(num, 2);  
  if (num == 1) {  
    return 1  
  } else {  
    return sum + sumOfSquares(num - 1)  
  }  
  return sum  
}

In this example, we wrote a function to compute the sum of squares for a given number. We compute the square of num and then if we have num equal to 1 then we return 1, otherwise we return the sum of sum plus the result of call sumOfSquares on num — 1. We keep reducing num so that we can reach the base case of 1, adding up the results while doing so.

Nesting Functions

Functions can be nested within each other. This means that we can define a function inside another function. For example, we can write:

function convertToChicken(name){  
  function getChickenName(name){  
    return `Chicken ${name}`;  
  }  
  return getChickenName(name)  
}

In this case, we called getChickeName inside the convertToChicken call. So if we write convertToChicken('chicken') , then we get 'Chicken chicken' since we called get getChickeName and returned the result. The scope of variables are the name. let and const are block-scoped so they cannot be accessed outside of the original function that’s defined, but they are available in the nested function, so if we have:

function convertToChicken(name) {  
  let originalName = name; function getChickenName(newName) {  
    console.log(originalName)  
    return \`Chicken ${newName}\`;  
  }  
  return getChickenName(name)  
}

Then originalName will still be defined in the console.log.

Defining Function in an Object

We can define a function in an object in a few ways. We can use the function keyword or arrow function as usual, but we can also write it with a shorthand for the function keyword. For example, if we have a bird object and we want to define the chirp function, we can write:

const bird = {  
 chirp: function(){  
   console.log('chirp', this)  
  }  
}

or use the following shorthand:

const bird = {  
 chirp(){  
   console.log('chirp', this)  
  }  
}

The 2 are the same since the chirp function will have the bird object as the value of this.

On the other hand, if you use arrow function:

const bird = {  
 chirp: ()=>{  
   console.log('chirp', this)  
  }  
}

this will be logged as the global window object, since arrow functions do not change the value of this to the object in which the function is in.

JavaScript functions allow us to organize code into small parts that can be reused. There’re many ways to define a function, but sticking to the commonly recommended ways like using arrow functions and not using arguments too much is recommended.

Latest comments (0)