DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Eloquent JavaScript(Part I : Chapter 3/Functions)
Pranish Shrestha
Pranish Shrestha

Posted on • Originally published at dev.to

Eloquent JavaScript(Part I : Chapter 3/Functions)

In this post, we will learn about:

  • Defining a Function
  • Bindings and Scopes
  • Nested Scope
  • Functions as Values
  • Declaration Notation
  • Arrow Functions
  • The Call Stack
  • Optional Arguments
  • Closure
  • Recursion
  • Growing Functions
  • Functions and Side Effects

Defining a function

A JavaScript function is a block of code designed to perform a particular task. It is executed when something invokes it.

const square = function(x){
return x*x;
}
function cube(y){
return y*y*y;
}
console.log(square(12)); //144
console.log(cube(6)); //216

Enter fullscreen mode Exit fullscreen mode

function is created with an expression that starts with the keyword function. Functions have a set of parameters(in this case x and y) and a body which contains a statement that are to be called when a function is called. Functions are always wrapped with braces'{}' even when there is only one statement.
A function can have multiple parameters or no at all.

const makeNoise = function() {
  console.log("Pling!");
};

makeNoise();
// β†’ Pling!

const power = function(base, exponent) {
  let result = 1;
  for (let count = 0; count < exponent; count++) {
    result *= base;
  }
  return result;
};

console.log(power(2, 10));
// β†’ 1024
Enter fullscreen mode Exit fullscreen mode

Some values produce a value, such as power, square and cube and some dont like makeNoise, whose only result is a side effect. A return statement determines the value the function returns. Functions that don’t have a return statement at all, such as makeNoise, similarly return undefined.

Syntax:

function name([parameter1, parameter2, parameter3) {
   statements
}
Enter fullscreen mode Exit fullscreen mode

Bindings and Scopes

Each binding(variable) has a scope, which is the part of the program in which the binding is visible.
Scope is the accessibility of variables, functions, and objects in some particular part of your code during runtime. In other words, scope determines the visibility of variables and other resources in areas of your code.
Variables defined inside a function are in local scope while variables defined outside of a function are in the global scope.

JavaScript has 3 types of scope: block, function and global scope. Before ES6(2015) JavaScript only has global and function scope. ES6 introduced let and const variable which provide block scope in JavaScript.

  • Block scope:
{
var x=2; 
let y=4;
}
//x can be used here but y cannot be used here
Enter fullscreen mode Exit fullscreen mode

Variables declared with the var keyword can NOT have block scope.
Local Scope: Variables declared within a JavaScript function, become LOCAL to the function.

  • Function scope : Variables defined inside a function are not accessible (visible) from outside the function.Variables declared with var, let and const are quite similar when declared inside a function.
function number(){
var num = 2; //function scope
}
Enter fullscreen mode Exit fullscreen mode
  • Global scope: Variable outside of a function becomes a function.
let x= 2;
//x can be used here
function sum(){
 //x can be used here
}
//x can be used here
Enter fullscreen mode Exit fullscreen mode

Nested Scope

Blocks and functions can be created inside other blocks and functions, producing multiple degrees of locality.It is known as nested scope.

const hummus = function(factor) {
  const ingredient = function(amount, unit, name) {
    let ingredientAmount = amount * factor;
    if (ingredientAmount > 1) {
      unit += "s";
    }
    console.log(`${ingredientAmount} ${unit} ${name}`);
  };
  ingredient(1, "can", "chickpeas");
  ingredient(0.25, "cup", "tahini");
  ingredient(0.25, "cup", "lemon juice");
  ingredient(1, "clove", "garlic");
  ingredient(2, "tablespoon", "olive oil");
  ingredient(0.5, "teaspoon", "cumin");
};
Enter fullscreen mode Exit fullscreen mode

The code inside the ingredient function can see the factor binding from the outer function. But its local bindings, such as unit or ingredientAmount, are not visible in the outer function.
Each local scope can also see all the local scopes that contain it, and all scopes can see the global scope. This approach to binding visibility is called lexical scoping.

Functions as Values

It is possible to store a function value in a new binding, pass it as an argument to a function, and so on. Similarly, a binding that holds a function is still just a regular binding and can, if not constant, be assigned a new value, like so:

let launchMissiles = function() {
  missileSystem.launch("now");
};
if (safeMode) {
  launchMissiles = function() {/* do nothing */};
}
Enter fullscreen mode Exit fullscreen mode

Declaration Notation

There is a slightly shorter way to create a function binding. When the function keyword is used at the start of a statement, it works differently.

function square(x) {
  return x * x;
}
Enter fullscreen mode Exit fullscreen mode

Function declarations are not part of the regular top-to-bottom flow of control.They are conceptually moved to the top of their scope and can be used by all the code in that scope. This is sometimes useful because it offers the freedom to order code in a way that seems meaningful, without worrying about having to define all functions before they are used.

Arrow functions

An arrow function expression is a compact alternative to a traditional function expression, but is limited and can't be used in all situations.

// Traditional Function
function bob (a){
  return a + 100;
}

// Arrow Function
let bob = a => a + 100;
Enter fullscreen mode Exit fullscreen mode

The Call Stack

The way control flows through functions is somewhat involved. Let’s take a closer look at it. Here is a simple program that makes a few function calls:

function greet(who) {
  console.log("Hello " + who);
}
greet("Harry");
console.log("Bye");
Enter fullscreen mode Exit fullscreen mode

A run through this program goes roughly like this: the call to greet causes control to jump to the start of that function (line 2). The function calls console.log, which takes control, does its job, and then returns control to line 2. There it reaches the end of the greet function, so it returns to the place that called it, which is line 4. The line after that calls console.log again. After that returns, the program reaches its end.

Optional Arguments

function square(x) { return x * x; }
console.log(square(4, true, "hedgehog"));
// β†’ 16
Enter fullscreen mode Exit fullscreen mode

In simple words, the extra arguments are ignored and just returns the value which it demands.
JavaScript is extremely broad-minded about the number of arguments you pass to a function. If you pass too many, the extra ones are ignored. If you pass too few, the missing parameters get assigned the value undefined.

The downside of this is that it is possibleβ€”likely, evenβ€”that you’ll accidentally pass the wrong number of arguments to functions. And no one will tell you about it.

The upside is that this behavior can be used to allow a function to be called with different numbers of arguments.

function minus(a, b) {
  if (b === undefined) return -a;
  else return a - b;
}

console.log(minus(10));
// β†’ -10
console.log(minus(10, 5));
// β†’ 5
Enter fullscreen mode Exit fullscreen mode

Closure

A closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

function multiplier(factor) {
  return number => number * factor;
}

let twice = multiplier(2);
console.log(twice(5));
// β†’ 10
Enter fullscreen mode Exit fullscreen mode

In the example, multiplier is called and creates an environment in which its factor parameter is bound to 2. The function value it returns, which is stored in twice, remembers this environment. So when that is called, it multiplies its argument by 2.

Recursion

It is perfectly okay for a function to call itself, as long as it doesn’t do it so often that it overflows the stack. A function that calls itself is called recursive.

Imperative approach(loop)

function countDownFrom(number) {
    for (let i = number; i > 0; i--) {
        console.log(i);
    }   
}

countDownFrom(5);
// 5
// 4
// 3
// 2
// 1
Enter fullscreen mode Exit fullscreen mode

Recursive approach

function countDownFrom(number) {
    if (number === 0) {
        return;
    }

    console.log(number);    
    countDownFrom(number - 1);
}

countDownFrom(5);
// 5
// 4
// 3
// 2
// 1
Enter fullscreen mode Exit fullscreen mode

Growing Functions

writing a good function name that defines the code in it refers to growing functions. Function name that gives precise explanation is helpful to understand the code but don't write every bit of functionality you come across--you'll just be writing code that you never use.

Functions and side Effects

Functions can be divided into those that they called for their side effects and those that are called for their return values.(also possible to have both in same function).
A pure function is a specific kind of value-producing function that not only has no side effects but also doesn’t rely on side effects from other code.
When you are not sure that a pure function is working correctly, you can test it by simply calling it and know that if it works in that context, it will work in any context.

Conclusion βŒ›
I hope you found these tips helpful. If you need any help please let me know in the comment section.

πŸ‘‹ Thanks for reading, See you next time

Top comments (0)

Stop by this week's meme thread!

CSS margins