A quick recap

Back in the JavaScript Functions chapter in the foundation unit of this course, we got a very warm introduction to the idea of functions in programming, and most importantly, in JavaScript.

Now before we dive into considering more intricate details regarding functions, let's spare a couple of minutes to quickly recap all that we have already learnt regarding them previously.

Beginning with the definition:

A function is a block of code that is executed when the function is called.

In JavaScript, a we could create a function using the keyword function.

The syntax goes as follows:

function function_name(param1, param2, ..., paramN) {
    // function's body
}

We begin with the function keyword, followed by the name of the function, followed by a pair of parentheses (()) within which goes all the parameters of the function, followed by a pair of curly braces ({}) between which we put the main code of the function, also known as the function's body.

To call a function — or in more programmatic terms, to invoke a function — simply means to execute its body. This is accomplished by a simple notation:

function_name()

The name of the function is followed a pair of parentheses (()).

Without the parentheses, we refer to the function object, but with them, we call the function.

Moving on, more often than not, functions require additional data to do their work. This data goes in the form of arguments to the function.

Arguments are values passed on to the function when calling it, in the pair of parentheses (()).

Parameters are a slightly different idea — they are the names with which we refer to given arguments inside the function's body. Parameters go inside the pair of the parentheses (()) in the header of a function when defining it.

Let's create a function sum() taking in two numbers a and b and outputting their sum to the console:

function logSum(a, b) {
    console.log(a + b);
}

When we call this function logSum(), the sum of a and b is logged to the console. This is useful, but not very useful. It would be much better if we could use the sum computed by the function in other places, such as in other functions, variables, expressions and so on.

This can simply be achieved by rewriting logSum() to return the sum of a and b instead of directly logging it.

To return a value from a function, we use the return keyword. When a function f returns a value x, its invocation expression f() is simply replaced by that value x.

function function_name(param1, param2, ..., paramN) {
    // ...
    return value
}

The syntax is pretty intuitive — the return keyword is followed by the expression to be computed and returned from the function.

Let's rewrite the function logSum() above into a more flexible addition function:

function sum() {
    return a + b;
}

Now this function could be used flexibly in many areas. Consider the snippet below for an overview:

sum(10, 20)
30
sum(5, 2) * 7
49
sum(5, 2) + sum(-5, 1)
3

So this is it for the recap of functions. It covered almost everything that we talked about in JavaScript Functions, but in a way more compact manner.

Now with a fresh overview of the very foundations of functions in mind, we are all good to go on the longer trek and explore more interesting and detailed avenues related to them in JavaScript.

Let's go!

Function declarations and expressions

The syntax that we saw above to create a funtion in JavaScript is commonly known as a function declaration.

Let's review its syntax:

function function_name(param1, param2, ..., paramN) {
    // function's body
}

So what is a function declaration?

A function declaration refers to a function definition that exists as a standalone statement — not as an expression.

All the functions that we have been creating so far in this course were function declarations.

Shown below are a couple of examples:

// hence a function declaration
function sum(a, b) {
    return a + b;
}
// a function declaration
function outerFn() {

    // a function declaration within outerFn
    function innerFn() {
        console.log('Working with functions.')
    }

}

Here sum, outerFn and innerFn are all function declarations, since they are standalone statements:

There is a little exception to this idea i.e. when a function is defined inside some other block statement apart from the body of a function, it isn't considered a function declaration, even though it is a standalone statement. There is a very good reason for this behaviour. Details are given in the last snippet in this section.

In JavaScript, there is another flavor of creating a function which, although has the same syntax as a function declaration, works a bit differently.

We call it a function expression.

A function expression refers to a function definition that exists as an expression and not as a standalone statement.

A function expression can define a function without a name. Such functions are called anonymous functions. We'll see more details to anonymous functions and their practical usage in JavaScript later on in this chapter.

Syntactically, a function expression could be shown as follows if the function was assigned to a variable:

var function_name = function [function_name](param1, param2, ..., paramN) {
    // function's body
}

Note that with a function expression it's optional to include the name of the function after the function keyword as is apparent in the syntax above.

Let's create the same function sum() above, this time using a function expression:

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

What's happening over here is that a function without a name is being assigned to the variable sum. Since, sum points to a function object in memory, we could use the expression sum().

If any given expression resolves down to a function object, the function could be called by appending a pair of parentheses (()) after the expression. It is not necessary for the function to be named in order to call it. We just need to have some reference to the function in hand, which could be in the form of a variable, an array's element, an object's property and so on.

As you can see below, sum() behaves just like it did in the code above where it was defined using the declaration syntax:

sum(10, 20)
30
sum(5, 2) * 7
49
sum(5, 2) + sum(-5, 1)
3

The notion of a function expression is quite handy. They are expressions and hence could be used in every single way we could use a normal expression, right as they are defined.

For instance, we could:

  1. Assign function expressions to variables, and other identifiers, just as we saw above.
  2. Pass function expressions to other functions as arguments.
  3. Return function expressions from other functions.

Consider the code below where we store two separate function expressions as array elements:

var utils = [
    function(a, b) { return a + b; },
    function(a, b) { return a - b; }
];

console.log(utils[0](10, 20));
console.log(utils[1](50, 5));
30
45

The first element of utils serves to add two given numbers together and return the result, while the second element serves to subtract two numbers and return the result.

Below, we assign a function expression to an object's property:

var arithmetic = {
    add: function(a, b) { return a + b; },
    diff: function(a, b) { return a - b; }
}

console.log(arithmetic.add(10, 20));
console.log(arithmetic.diff(50, 5));
30
45

It is an extremely common practice in JavaScript to assign functions to object properties.

Do you remember what are such properties called?

Well they are called methods. We'll see more details in the JavaScript Objects chapter.

Moving on, below we pass a function expression as an argument to another function:

function execute(fn) {
    fn();
}

execute(function() { console.log('Hello World!'); })
Hello World!

This is another highly common coding practice witnessed in JavaScript applications all day long. A function passed to another function as an argument is called a callback function, or simply a callback.

We've been using callbacks in our JavaScript Array Methods chapter. Callbacks play a vital role in the events API of JavaScript and tons and tons of other APIs.

Let's also return a function from a function:

function getLogger() {
    return function(val) {
        console.log('We are learning ' + val + '.');
    }
}

var langIntro = getLogger();

langIntro('JavaScript');
We are learning JavaScript.

In line 7, when getLogger() is called, execution moves to its definition in line 1, and exits out of it with the return value of another function. This 'another' function takes an argument val and makes a log consisting of that argument.

Since getLogger() returns a function, the value saved in langIntro is a reference to a function — in particular, to the anonymous function in line 2. This means that we could use the expression langIntro() to invoke this function. And that's exactly what we do in the last line.

Functions returned from other functions from yet another important concept in JavaScript and programming in general, referred to as closures, thought the concept doesn't just exist in functions returned from other functions — it exists elsewhere as well.

We shall see a bit more about closures later in the following sections, and then a whole ton full of detail in the next to the next chapter on JavaScript Function Closures.

At the core, implementation level, there isn't any difference between a function declaration and a function expression. When called, both will execute the same way. The difference lies only in the way they both are parsed when a script is compiled.

Let's see that difference...

Difference between declarations and expressions

There are mainly two differences between a function declaration and a function expression.

They are:

  1. One is hoisted while the other one is not.
  2. One is capable of creating anonymous functions while the other is not.

Let's see which one meets the points given above and which one doesn't.

Hoisting

Recall the term 'hoisting' from JavaScript Variables? What does it mean?

Hoisting refers to the behaviour of bringing up all variable declarations in their scope to define them before any other code is executed.

Variable hoisting is the reason why the following code works and doesn't throw an error:

// we think that x doesn't exist at this point,
// likewise the following statement would throw an error
console.log(x);

var x = 10;
undefined

What happens here internally is that the engine first searches for all variable declaration statements in the code and places them all at the top of their respective scopes (in the order they appear in the code). Once this is done, it then parses the code from the very beginning.

This means that before actually executing the code above, the engine puts var x at the top of the script and then begins execution. Since var x initializes x to undefined, accessing x before the assignment statement in line 5 would return undefined.

In the case of function declarations, a similar thing happens. We call it functional hoisting.

The act of repositioning all function declarations to the top of their respective scopes is referred to as functional hoisting.

The whole declaration along with the body of the function is taken to the very top of its scope.

What this means is that the following code will work absolutely fine:

console.log(sum(10, 20));

function sum(a, b) {
    return a + b
}
30

The function sum() is accessed before it's actually defined in line 3. Yet the invocation doesn't flag any errors — thanks to functional hoisting.

The most important thing to realise over here is that functional hoisting is only limited to function declarations — NOT to function expressions.

The engine in its first pass over a piece of code looks only for function declarations, not for expressions, and hoists them up.

An illustration is shown below:

console.log(sum(10, 20));

var sum = function(a, b) {
    return a + b
}
Uncaught TypeError: sum is not a function

This code throws an error, the reason for which is very simple. We have a variable declaration statement var sum, likewise it is hoisted to the top of its scope. Next, the statement sum() is executed. At this point, sum is equal to undefined, which can't be called, hence sum() throws an error.

Fair enough!

A novice developer might think that this could be mitigated by adding a name to the function in the expression. However, this won't work either. The reason is still pretty simple.

The engine searches only for function declarations in a given piece of code and ultimately hoists them. Function expressions, be they are named or anonymous don't matter — they are expressions at the end of the day and would likewise be ignored in the hoisting phase.

This is one difference between declarations and expressions, and by far the most noticeable one.

Anonymous functions

The second difference is in the fact that one of these methods of denoting a function couldn't be used to create anonymous functions.

Let's review the idea of an anonymous function:

A function that has no name is called an anonymous function.

When JavaScript sees a function declaration, it expects to see the name of the function specified as well. Failing to do so will lead to an error.

Consider the following:

function(a, b) {
    return a + b;
}
Uncaught SyntaxError: Function statements require a function name

The function definition here denotes a declaration since it is a standalone statement. Consequently, the name of the function is expected. Because we don't have a name, the code terminates with an error.

Leaving out the name of a function is only possible with a function expression as shown below:

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

Here, the function created doesn't have a name of its own, as is apparent in its definition. It's assigned to a variable sum.

It's important to be able to make this distinction between different function definitions in code i.e. when the function has a name vs. when it doesn't have a name and is assigned to a named identifier.

Anonymous functions, which can only be denoted using function expressions, are a paramount feature in JavaScript.

They are used in nearly every program working with events and callbacks — concepts which we shall learn later on in this course. In the JavaScript Events unit, we'll use anonymous functions quite extensively.

A function expression statement

What if we want to create a function as a standalone value but not worry about giving it a name?

As we saw above, this is not possible by creating the function as it is, since that would denote a function declaration. So it seems that there is no way to do so.

Well there is...

There is a clever trick that can be done to denote a function as an expression statement and thus prevent the need of having to assign the function to some variable or other identifier.

It's illustrated as follows:

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

Recall the pair of parentheses from JavaScript Operations? It serves to denote an expression when used without any preceding identifier. Likewise, here, the function definition is an expression, not a statement.

This explains why the code doesn't throw an error even though there is no name in the function's definition.

This type of a function expression statement is extremely common in many many code snippets out there. It could be invoked immediately after it's defined, in which case it's called an IIFE. IIFEs are a highly useful feature of JavaScript as we shall see later on in this chapter

So to boil it down, function declarations and expressions do have a couple of differences in the way they are handled at compile time. At runtime, however, they both work the same way.

An exception case

We stated above that when a function definition exists as a standalone statement, and not as part of an expression, it is a function declaration.

With this definition in front, let's try to reason about one case.

Given the code below, what do you think is the definition of sum()? Is it a function declaration or a function expression?

if (false) {
    function sum(a, b) {
        return a + b;
    }
}

Following from our old definition, since it is defined as a standalone statement, we conclude that it is a function declaration.

This is NOT the case!

But why? Why is the definition above not considered a function declaration?

Well, the answer is hidden in the code itself. Try to think for a while...

Let's say that the definition of sum() above was a function declaration. That would have meant that it was hoisted at the top of the global scope, and hence available even before the if conditional is parsed.

The scope inside a block statement existing in the global scope is global itself, as in the case above. The only exception is a function's body that creates its own local scope.

This would've simply made the code above useless. The if conditional has a falsey condition likewise its body is never executed, however, the function defined in it is accessible regardless of that fact.

To mitigate this problem, any function definition inside a block statement such as if, else, for while etc., apart from the body of a function (which is also a block statement), is considered a function expression.

And being considered a function expression means that it is not expected for the function to have a name. Right?

Let's try to do that and omit the name sum from the function's definition:

if (false) {
    // this is a function expression
    // likewise, its name can be left out
    function(a, b) {
        return a + b;
    }
}

As soon as we execute this, we get an error thrown. Oh no, not again!

Uncaught SyntaxError: Function statements require a function name

It says that function statements require a function name. However, what we said was that function declarations require a name.

What is all this mess?

Well, once again, if you think of it carefully, it wouldn't seem a mess.

Let's understand everything step by step...

Anytime JavaScript sees a function defined as a standalone statement, it expects it to have a name. Why? Because if the function didn't have a name, there would be no single way to call it afterwards.

The function isn't assigned to an identifier, isn't part of any expression; it is simply nothing more than a standalone statement. And calling this statement is impossible in JavaScript. Why?

Because a pair of parentheses (()) following a function's statement would denote an expression group, not invoking symbols for the function.

So to restate it: a function statement is expected to have a name.

In the code above, the function definition is undoubtedly a statement and hence the JavaScript engine looks for a name in it. When it doesn't find one, it flags an error.

For the sake of simplifying things at the beginning of introducing you to function declarations and expressions, we stated that all function statements are function declarations.

This is true except for the case mentioned above — function statements that occur inside block statements other than a function body.

So technically, the proper definition would be to say:

A function definition that exists as a standalone statement, not nested inside a block statement except for a function's body, is known as a function declaration.

And in the same lines,

A function definition that exists as part of an expression, or is nested inside a block statement except for a function's body, is known as a function expression.

Now both these definitions cover the exception case shown above as well.

As you may agree, starting with these definitions right at the beginning of the section above would have made things a bit more involved.

It's always good to learn something simple, even if that means to leave out some details, and then later on bring the extra details and exceptional cases into consideration. In this way, understanding becomes a lot easier and way more intuitive than when everything is mentioned at once.

What do you think?

IIFEs

In JavaScript, there is a special case of a function expression which is invoked right after it is defined. We call it an IIFE, or an Immediately Invoked Function Expression.

The full form clearly explains what an IIFE is:

An IIFE is a function expression that is invoked immediately, after being defined.

Shown below is an example of an IIFE:

(function() {
    console.log('I am in an IIFE.')
})()
I am an IIFE.

Now you might be thinking:

What's so special about an IIFE. If it's executed immediately after being defined, what's even the point to put code in it. Wouldn't we be better off at placing the code outside it?

This is an extremely good question.

Beginners might think that IIFEs are pointless, but when global namespace overcrowding and name collisions becomes a concern, they aren't.

Let's see what purpose do IIFEs serve.

Recall that functions, regardless of the way they are defined, create their own local scope when they are called. Variables declared in this local scope are accessible only inside the function, not outside it.

In the code below, all the variables declared inside the function fn() are accessible only within it:

function fn() {
    var a; // local to fn
    var b = 10; // local to fn

    // local to fn
    function sum(a, b) { return a + b; }
}

// All of the following would throw an error

// console.log(a);
// console.log(b);
// console.log(sum);

This means that if we take a piece of code, where identifiers are declared globally, and then put it inside an IIFE, those identifiers won't overcrowd the global namespace any longer — rather they would now part of the local scope of the IIFE.

What this means is that our programs would work exactly as before, yet the global scope would be clean and serene without any new identifier names.

This is a huge benefit!

Developers can create as many global variables as they want to in their programs without worrying about any future name collision issues with other scripts.

For instance, suppose that we have an application utilising two JavaScript libraries lib1.js and lib2.js, both of which have code operating on the global scope.

The markup of the application looks something as follows:

<html>
<head>
    <!-- code here -->
</head>
<body>
    <!-- code here -->
    <script src="lib1.js"></script>
    <script src="lib2.js"></script>
</body>
</html>

Let's say that lib1.js defines a variable main set to some value x which it uses in many of its routines. Let's also say that lib2.js defines a variable main as well.

What do you think will happen as these two libraries work together?

Supposing that either of the libraries changes its main variable while it runs and then uses its value later on, this would change the main variable of the other script as well, since they both share the same global scope.

We say that there would be name collisions between both the libraries. The consequence of this would simply be unexplained errors and weird behaviours in the main application.

IIFEs solve this problem in a very concise syntax. They encapsulate code in a local environment which doesn't mess with the global scope.

The best part is that it is a very quick process — we don't have to name these functions or worry about assigning them to any identifiers. Just create an anonymous (or named) function, encapsulate it around a pair of parentheses, put the code within it, and then invoke it immediately. Voila!

Without IIFEs achieving this would only be possible if we give the function a name, or equivalently assign it to any identifier, which isn't that cool.

IIFEs are simply magic!

Named function expressions

ES5 introduced the idea of named function expressions into JavaScript. Before this, it was invalid to name function expressions.

Named function expressions weren't introduced into the language without a purpose. We'll see that purpose in a while.

For now, let's see what a named function expression looks like if it's assigned to an identifier:

identifier = function function_name(param1, param2, ..., paramN) {
    // function's body
}

It's simply a function expression with a name, that's it.

Note that, as before, it's not necessary to assign the function expression to a variable. It could be used like any other valid expression in JavaScript i.e. passed to functions, returned from functions and so on.

Time to see some actual examples:

var sum = function hidden_sum(a, b) {
    return a + b;
}

console.log(sum(10, 20));
console.log(sum(30, 200));
30
230

This was fairly straightforward. We merely assign a named function expression to a variable sum.

Below we have a bit more interesting example:

var nums = [1, 2, 3];
var squares = nums.map(function toSquare(num) { return num ** 2; });

console.log(squares);
[1, 4, 9]

Here we pass a named function expression into the array method map(), as an argument. See more about map() at JavaScript Array Methods — map().

Alright, having seen what a named function expression looks like, let's now see what purpose it serves.

Back before ES3, recursively invoking a function expression from within itself was possible only via the arguments.callee expression.

var nums = [1, 2, 3, 4, 5, 6];

var fibs = nums.map(function(n) {
    if (n === 1) return 0;
    if (n === 2) return 1;
    return arguments.callee(n - 1) + arguments.callee(n - 2);
});

console.log(fibs);
[0, 1, 1, 2, 3, 5]

Within a function f, arguments.callee contains a reference to the function itself.

However, in the discussion for ES5, it was conceived to change this to something more convenient and intuitive. So it was decided to allow for function expression to have a name in ES5. In this way, the expression's body would be able to recursively call itself by simply referring to the name.

Shown below is an example:

var nums = [1, 2, 3, 4, 5, 6];

var fibs = nums.map(function fib(n) {
    if (n === 1) return 0;
    if (n === 2) return 1;
    return fib(n - 1) + fib(n - 2);
});

console.log(fibs);
[0, 1, 1, 2, 3, 5]

But there is one very peculiar detail to note regarding named function expressions.

That is, the name of the function expression isn't accessible in the scope where the function is defined, contrary to what one might expect. It's only accessible inside the function itself.

Let's take the same code above and try logging fib:

var nums = [1, 2, 3, 4, 5, 6];

var fibs = nums.map(function fib(n) {
    if (n === 1) return 0;
    if (n === 2) return 1;
    return fib(n - 1) + fib(n - 2);
});

console.log(fibs);

// log the function fib
console.log(fib);
Uncaught ReferenceError: fib is not defined

As stated before, line 12 throws an error. Why?

Simply because no identifier with the name fib exists in the global scope. The name binding only exists inside the function fib itself, as is clear in the function's body above.

Issues on IE8 and earlier

Keep in mind that named function expressions have a buggy behaviour in IE8 and earlier. In particular, a named function expression would create two separate two separate functions in memory — one to denote the declaration and one to denote the expression, and in violation of the spec, provide the name of the function expression as a global variable.

Let's see it in terms of an actual code snippet.

var f = function fn() {};

Here f and fn denote two different functions in memory (with different references), as can be confirmed using a simple log.

// executing in IE8 and earlier
var f = function fn() {
    console.log(fn === f);
};

f(); // false
The code above would log true on all latest browsers, and it should.

The IE8 engine parses fn as a function declaration and creates it like one, while considers f as a variable which is assigned a function expression, and hence, creates it like one.

What's imperative to note is that both the functions are created separately. This doesn't make any sense at all and is one of the reasons the ancient browser's implementation is termed as buggy.

The second reason is that the name fn is accessible outside the function fn() itself — after all, IE8 treats the function as a declaration in one way and so creates a global identifier fn pointing to it. This is in contradiction to the ECMAScript spec which states that the name must remain local to the function.

Now the question is that could this lead to any problems in real code. Well, it definitely could.

Due to this buggy behaviour of IE8, if you wish to support the browser, make sure to go through your code snippets once more to see if there are any places where you have used function expressions in a way that's incompatible with IE8.

However, if IE8 is not on your support list then you're all good to go since IE9 onwards this incompatibility is rectified.

The this keyword

Amongst the many confusing aspects of JavaScript lies the concept of this. In this section, we aim to introduce it to the mere reader and explain it to the core.

First of all, this is a reserved keyword in JavaScript which means that it can't be used as a variable's name.

This is demonstrated below:

var this = 'Hello World';
Uncaught SyntaxError: Unexpected token 'this'

Moving on, this special keyword was made to provide a way to refer to the object calling a function. This was original idea behind its creation.

In fact, Java, which was definitely a source of influence for JavaScript, also contains this serving essentially a very similar purpose.

So stating it formally:

When used inside a function, this refers to the object calling the function.

For instance, consider the object o below. It has a property x and a method f():

var o = {
    x: 10,
    f: function() { console.log(this.x); }
};

var x = 20;
o.f();

As soon as we execute this, we get the value 10 logged in the console. How? Let's see it.

In line 7, the invocation expression o.f() calls the function stored in o.f, on the object o, and likewise its this points to o. This simply means that the expression this.x inside the function object translates to o.x, which is equal to the value 10, and thus 10 is logged in the console.

Now, to refer to the property x of o inside the method o.f(), we could've directly used the expression o.x, instead of this.x.

So what's so special about this? Well, there is just a lot to appreciate about this.

If ever in the future, we change the name of this object o or assign it to some other identifier and then use that identifier instead of o, using this means that any method defined on o would work absolutely fine.

This is because this would automatically point to the container object — we don't need to configure it to the respective object.

In contrast, if we use o.x (instead of this.x) inside a method of o, we'd have to rewrite this expression when the name of o gets changed in the future or is assigned to some other identifier and we have to use that identifier instead.

Ideally, whenever possible, refer to the container object of a method using the this keyword — NOT the name of the object itself.

But this is not the only benefit of this.

When constructor functions are used, this proves to be exceptionally useful as we shall see in the JavaScript Objects unit. It refers to the object being created and thus enables one to assign properties to that object right inside the constructor.

And when prototypes enter the game, the story is completely different.

In short, this is not something you'd use occassionally in your programs. As soon as you start programming in JavaScript, you'll find yourself in need of it for even some of the most basic programs. It's that important!

Anyways, let's come back to exploring more about this.

When a function object is called directly, not as part of an object, its this value resolves down to the global window object, given that the script is running in non-strict mode.

There's a different behaviour for strict mode which we shall see later on below.

Consider the following code:

function f() {
    console.log(this.x);
}

var x = 10;
f();

In line 6, the function f() is called directly, not as a method of an object, likewise its this gets resolved down to window. So this.x becomes window.x which returns the value of the global variable x, ultimately logging the value 10 in the console.

Let's consider another example. Try to determine the log made by the following code:

var o = {
    x: 20,
    f: function() { console.log(this.x); }
};

var x = 10;
var func = o.f;

func();
10

So the log made is 10, not 20 which one might have guessed.

Here's what's happneing above: the function saved in o.f is assigned to the global variable func and then func() is called. Since func() is called directly, not as part of an object, its this points to window, which means that this.x becomes window.x evaluating down to 10.

Keep in mind that this idea of this resolving down to window would hold even when a function is called directly from inside another function. That is, this idea doesn't just hold for functions called in the global scope.

As long as a function is called directly, in any place in a piece of code, its this gets set to window (once again, given that the script is running in non-strict mode).

An illustration is shown below:

var o = {
    x: 20,
    f: function() { console.log(this.x); }
};

function f() {
    var func = o.f;
    func();
}

var x = 10;
f();
10

Inside the function f(), we create a local variable func and assign it the function saved in o.f. Then we call func(). Once again, since func() is called directly, not as part of an object, its this resolves to window.

Hence, this.x becomes window.x, consequently resulting in the log 10.

As a rule of thumb, just take note of the way a function is invoked to figure out its this binding. Don't resolve this by looking at the definition of the function — doing so might give the wrong result.

Determine the log made by the code below:

function f() {
    console.log(this.x);
}

var o = {
    x: 10,
    func: f
};

var x = 20;
o.func();
10

The function f is assigned to the property func of o, and then called via o.func(). Because the function is called as part of an object, o in this case, its this points to o.

Now, this is not just available inside a function. It's available outside a function as well, in the global scope.

There, this merely points to the global object i.e. window.

In other JavaScript environments such as web workers or Node.js, instead of window, the global object is self or global respectively, and this neatly points to the respective object, so we don't need to worry about which environment we are in if we are using this.

Shown below is a demonstration:

// this is the global scope
console.log(this);
Window {window: Window, self: Window, document: document, name: "", location: Location, …}

As you can clearly see, the log made is of the global window object.

One last thing left to be discovered in regards to this is when the script is running in strict mode. Let's see what strict mode has to offer us.

this and strict mode

As we have seen above, when a function is called directly, not as part of an object, its this resolves down to the global window object. Now this holds only as long as the script or function is running in non-strict mode.

In strict mode, when a function is called directly, not as part of an object, its this value is set to undefined.

Frankly speaking, the strict mode behaviour seems to be more appropriate — when a function is NOT called as part of an object, i.e. there is no calling object for the function, its this is undefined.

Shown below is an example:

'use strict'

function f() {
    console.log(this);
}

f();
undefined