Should be investigated:
  • Hidden classes for code optimization

Execution context

Global Execution Context

The Global execution context contains a global object (window) and this. All next contexts, which are created for every executed function contain this and arguments objects.

The arguments is an object, like this:

{
  0: 'parameter value 1',
  1: 'parameter value 2',
  2: 'parameter value 3',
}

In case we have ES6 function function myFunc(...args), the arguments is an array. So we have to use Array.from(arguments). And in general we should avoid using it.

There are the following phases when a new execution context is created:

  • Creation phase
  • Hoisting phase
  • Execution phase

Hoising is a scan for functions and var variables and allocating memory for them.

Consider the following function:

var favFood = 'grapes';

var func = function() {
    console.log('Food 1', favFood);
    var favFood = 'lemon';
    console.log('Food 2', favFood);
}

func();

First Food 1 log returns undefined. It’s because of hoising.

Hoising works only for function declaration & var. It doesn’t work for let & const variables. If we change var favFood = 'lemon' to let favFood = 'lemon' it will cause the compilation error.

Lexical Context

It’s about the function’s location. It can be global or not.

function func1() {
  ...
}

function func2() {
  func func3() {
    console.log(1);
  }
  return func3();
  func func3() {
    console.log(2);
  }
}

func2();

Notice different lexical contexts for func1, func2 & func3. Notice also it log 2 because of hoisting.

Scope chain

There is a local variable environment

use strict

The use strict statement prevents the errorless execution of the following code (for example):

'use strict';
function func1() {
  height = 50;
  console.log(height);
}
func1();
Function & Block scope

Consider the following piece of code:

if (5 > 4) {
  var secret = '123';
}
secret;

This code returns 123, because var works with function scope. But let & const works in block scope.

IIFE

Immediately Invoked Function Expression. It’s a function expression which looks like this:

(function() {
  ...
})();

It used to prevent using global variables, before es modules were intruduced. We can use it the following way:

var script1 = (function() {
  // some code
  return {
    a: 1
  };
})();

Here we still have a global environment, but we have only one global variable.

this

Defenition: this is the object that the function is a property of. this of global functions referes to the window;

Consider the following code:

const obj = {
  name: 'Billy',
  hello() {
    return `Hello ${this.name}`;
  }
}
obj.hello();

In case we use use strict in our global function, this will return undefined:

function myFunc() {
  `use strict`
  console.log(this);
}

Look at the following code:

const a = function() {
    console.log('a', this);
    const b = function() {
        console.log('b', this);
        const c = {
            hi: function() {
                console.log('Hi c', this);
            }
        }
        c.hi();
    }
    b();
}

a();

It returns window twice, then c.

Next example:

const obj = {
  name: 'Billy',
  hello() {
    console.log(`Hello ${this.name}`);
    var z = function() {
      console.log('B', this.name);
    }
  }
}
obj.hello();

this piece of code returns Hello Billy, then undefined; To fix this, we need to change the inner function to ES6 arrow function:

const obj = {
  name: 'Billy',
  hello() {
    console.log(`Hello ${this.name}`);
    var z = () => {
      console.log('B', this.name);
    }
  }
}
obj.hello();

Also we can use bind(this) in order to fix this; Arrow functions are lexically scoped.

bind(), apply(), call()

These functions are for manipulate of this. All functions use call()

We can use call(obj, ...params); apply() allows the same, but using array of parameters. bind() doesn’t call function, it just created a new function which is binded to defined obj.

Example how it can be used:

const array = [1,2,3];
// in this case, the 'this' keyword doesn't matter!
function getMaxNumber(arr){
  return Math.max.apply(null, arr);  
}
getMaxNumber(array)

bind() and currying

function multiply(a, b) {
  return a * b;
}

const multipyByTwo = multiply.bind(this, 2);
multipyByTwo(4);

It returns 8.