DEV Community

palaklive
palaklive

Posted on

Functions in JavaScript

Functions in JavaScript

What is a Function?

Functions are basically “building blocks” of the program or we can say a function is a process which takes some input called arguments and produces some output called a return value.They allow the code to be called many times without repetition.

It looks like this:

function showMessage() {
  alert('Hello everyone!');
}
showMessage()
Enter fullscreen mode Exit fullscreen mode

Function Declarations Are Hoisted

Function declarations are hoisted—moved in their entirety to the beginning of the current scope. That allows you to refer to functions that are declared later:

function foo() {
    bar();  // OK, bar is hoisted
    function bar() {
        console.log('bar executed successfully.')
    }
}
Enter fullscreen mode Exit fullscreen mode

Note that while var declarations are also hoisted (see Variables Are Hoisted), assignments performed by them are not:

function foo() {
    bar();  // Not OK, bar is still undefined
    var bar = function () {
        // Uncaught TypeError: bar is not a function
    };
}
Enter fullscreen mode Exit fullscreen mode

The Special Variable arguments

You can call any function in JavaScript with an arbitrary amount of arguments; the language will never complain. It will, however, make all parameters available via the special variable arguments. arguments look like an array, but has none of the array methods:

> function f() { return arguments }
> var args = f('a', 'b', 'c');
> args.length
3
> args[0]  // read element at index 0
'a'
Enter fullscreen mode Exit fullscreen mode

Way to Define a Function:

  • Pure Functions
  • Function Declaration
  • Function Expression
  • Native Function
  • Recursion Function
  • Callback
  • Immediately-invoked function expression

Pure Functions

  • Given the same input, will always return the same output.
  • Produces no side effects.

Function declaration:

The function keyword goes first, then goes the name of the function, then a list of parameters between the parentheses (empty in the example above) and finally the code of the function, also named “the function body”, between curly braces.

function add(param1, param2) {
    return param1 + param2;
}
Enter fullscreen mode Exit fullscreen mode

The preceding code defines a function, add, that has two parameters, param1 and param2, and returns the sum of both parameters. This is how you call that function:

> add(6, 1)
7
> add('a', 'b')
'ab'
Enter fullscreen mode Exit fullscreen mode

Function expression

Another way of defining add() is by assigning a function expression to a variable add:

var add = function (param1, param2) {
    return param1 + param2;
};
Enter fullscreen mode Exit fullscreen mode

Native Function

The "prototype" property is widely used by the core of JavaScript itself. All built-in constructor functions use it and native prototypes can be modified.
i.e. If we add a method to String.prototype it becomes available to all strings:


"hello folks!".toUpperCase() // HELLO FOLKS!

"Hello Folks!".toLowerCase() // hello folks!


"hello folks!".toTitleCase()
// Uncaught TypeError: "hello folks!".toTitleCase is not a function


// Let's Define a native function
String.prototype.toTitleCase = function() {
  return this.replace(
            /\w\S*/g,
            function(txt) {
                return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
            }
        );
};

"hello folks!".toTitleCase()

Enter fullscreen mode Exit fullscreen mode

During the process of development, we may have ideas for new built-in methods we’d like to have, and we may be tempted to add them to native prototypes. But that is generally a bad idea.

Important:

Prototypes are global, so it’s easy to get a conflict. If two libraries add a method String.prototype.toTitleCase, then one of them will be overwriting the other.

So, generally, modifying a native prototype is considered a bad idea.

Recursion Function

Recursion is a technique for iterating over an operation by having a function call itself repeatedly until it arrives at a result.

Recursion is best applied when you need to call the same function repeatedly with different parameters from within a loop.

var factorial = function(number) {
  var result = 1;
  var count;
  for (count = number; count > 1; count--) {
    result *= count;
  }
  return result;
};

console.log(factorial(5)); // 120


function factorial(x) {
    if(x === 0) return 1
    return x * factorial(x-1);
}
Enter fullscreen mode Exit fullscreen mode

Callback

In JavaScript, functions are first-class objects.First-class manner like any other object (String, Array, Number, etc).

Because functions are first-class objects, we can pass a function as an argument in another function and later execute that passed-in function or even return it to be executed later. This is the essence of using callback functions in JavaScript.

// classic example of callback functions:

var newbies = ["Amit", "Darshan", "Dharmesh", "Priya", "Shivangi"];

newbies.forEach(function (bee, index){
    console.log(index + 1 + ". " + bee);
});


// User define

function sandwich(param1, param2, callback) {
  alert('Started eating my sandwich.\n\nIt has: ' + param1 + ', ' + param2);
  callback();
}

sandwich('jam', 'cheese', function() {
  alert('Finished eating my sandwich.');
});

Enter fullscreen mode Exit fullscreen mode

Again, note the way we pass an anonymous function (a function without a name) to the forEach method as a parameter.

So far we have passed anonymous functions as a parameter to other functions or methods. Lets now understand how callbacks work before we look at more concrete examples and start making our own callback functions.

How Callback Functions Work?

We can pass functions around like variables and return them in functions and use them in other functions. When we pass a callback function as an argument to another function, we are only passing the function definition. We are not executing the function in the parameter.

Note that the callback function is not executed immediately. It is “called back” (hence the name) at some specified point inside the containing function’s body.

the anonymous function will be called later inside the function body. Even without a name, it can still be accessed later via the arguments object by the containing function.

Use Named Functions as Callbacks

Here is a quick example:

function greeting(name) {
  if(name) alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('Please enter your name.');
  callback(name);
}

processUserInput(greeting);
Enter fullscreen mode Exit fullscreen mode

Pass Parameters to Callback Functions

function createQuote(quote, callback){ 
  var myQuote = "Like I always say, " + quote;
  callback(myQuote); // 2
}
function logQuote(quote){
  console.log(quote);
}
createQuote("Be a voice not an echo!", logQuote);

Enter fullscreen mode Exit fullscreen mode

What is "callback hell"?
JavaScript that uses callbacks, is hard to get right intuitively. A lot of code ends up looking like this:

const verifyUser = function(username, password, callback){
   dataBase.verifyUser(username, password, (error, userInfo) => {
       if (error) {
           callback(error)
       }else{
           dataBase.getRoles(username, (error, roles) => {
               if (error){
                   callback(error)
               }else {
                   dataBase.logAccess(username, (error) => {
                       if (error){
                           callback(error);
                       }else{
                           callback(null, userInfo, roles);
                       }
                   })
               }
           })
       }
   })
};
Enter fullscreen mode Exit fullscreen mode

How do I fix callback hell?

Callback hell is caused by poor coding practices. Luckily writing better code isn't that hard!

You only need to follow rules:

Keep your code shallow:
Here is some messy browser JavaScript that uses browser-request to make an AJAX request to a server:

var form = document.querySelector('form')
form.onsubmit = function (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function (err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}
Enter fullscreen mode Exit fullscreen mode

This code has two anonymous functions. Let's give them names!

var form = document.querySelector('form')
form.onsubmit = function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, function postResponse (err, response, body) {
    var statusMessage = document.querySelector('.status')
    if (err) return statusMessage.value = err
    statusMessage.value = body
  })
}
Enter fullscreen mode Exit fullscreen mode

Now we can move the functions to the top level of our program:

document.querySelector('form').onsubmit = formSubmit

function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}
Enter fullscreen mode Exit fullscreen mode

Note that the function declarations here are defined at the bottom of the file. This is thanks to function hoisting.

Modularize

This is the most important part: Anyone is capable of creating modules (aka libraries)

Let's take out the boilerplate code from above and turn it into a module by splitting it up into a couple of files. I'll show a module pattern that works for either browser code or server code (or code that works in both):

// formuploader.js
module.exports.submit = formSubmit

function formSubmit (submitEvent) {
  var name = document.querySelector('input').value
  request({
    uri: "http://example.com/upload",
    body: name,
    method: "POST"
  }, postResponse)
}

function postResponse (err, response, body) {
  var statusMessage = document.querySelector('.status')
  if (err) return statusMessage.value = err
  statusMessage.value = body
}
Enter fullscreen mode Exit fullscreen mode

The module.exports bit is an example of the node.js module system which works in node front-end frameworks follow the ES Module structure.

// formuploader.js
var formUploader = require('formuploader')
document.querySelector('form').onsubmit = formUploader.submit
Enter fullscreen mode Exit fullscreen mode

Immediately Invoked Function Expression (IIFE)

An IIFE (Immediately Invoked Function Expression) is a JavaScript function that runs as soon as it is defined:

The function becomes a function expression which is immediately executed. The variable within the expression cannot be accessed from outside it.

(function () {
    var aName = "Barry";
})();
// Variable name is not accessible from the outside scope
aName // throws "Uncaught ReferenceError: aName is not defined"
Enter fullscreen mode Exit fullscreen mode

Assigning the IIFE to a variable stores the function's return value, not the function definition itself.

var result = (function () {
    var name = "Barry"; 
    return name; 
})(); 
// Immediately creates the output: 
result; // "Barry"
Enter fullscreen mode Exit fullscreen mode

Special Thanks to:

Top comments (0)