Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

JavaScript Function Basics

Chapter 30 40 mins

Learning outcomes:

  1. A quick recap of functions
  2. Function declarations and expressions
  3. Difference between function declarations and expressions
  4. What are anonymous functions
  5. What are IIFEs
  6. Named function expressions
  7. The this keyword

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, we could create a function using the keyword function.

The syntax goes as follows:

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

We begin with the function keyword, followed by the name of the function, followed by the parameter list enclosed in parentheses (()), followed by a pair of curly braces ({}) encapsulating the body of the function, that'll eventually be executed.

Collectively, this whole group of code that defines a function in JavaScript is referred to as the function's definition.

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

functionName()

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

Without the parentheses, we merely refer to the function object — not invoke it. However, with them, we call the function, that is to execute its body.

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 when calling it.

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 the function's definition.

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

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

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 instead return the sum of a and b rather than 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.

Here's how return is used inside a function:

function functionName(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

Voila!

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 quick and 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 function in JavaScript is commonly known as a function declaration, or sometimes even as a function statement.

Let's review its syntax:

function functionName(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 more examples:

// Standalone statement,
// hence a function declaration.
function sum(a, b) {
   return a + b;
}
// Another 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:

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.

The most important difference between a function declaration and a function expression is that the latter can define a function without a name. Such a function is usually called an anonymous function.

We'll see more details to anonymous functions and their practical usage in JavaScript later on in this chapter.

Anyways, here's how a function expression would look syntactically, given that the function was assigned to a variable:

var identifierName = function [functionName](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 (i.e functionName) as is apparent in the syntax above (with the presence of [ ] around functionName).

Let's create the same function sum(), 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, that function object could be called by appending a pair of parentheses (()) after the expression. It is not necessary for the function to be named itself 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, etc.

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, without having to define them on separate lines.
  3. Return function expressions from other functions, without having to define them on separate lines.

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, which is a function, 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 two function expressions to an object's properties:

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

console.log(utils.add(10, 20));
console.log(utils.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.

In the code above, the function created in line 5, passed to execute(), is the callback function.

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 its return value, which is of another function. This 'another' function takes an argument val and makes a log consisting of that argument along with some other text.

Since getLogger() returns a function, the value saved in langIntro is a reference to a function — in particular, to the anonymous function defined on 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 form yet another important concept in JavaScrip, referred to as closures, although the concept doesn't just exist in functions returned from other functions; it exists elsewhere as well.

We shall see more about closures in the next to the next chapter on JavaScript Function Closures.

At the core, execution 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 function declarations and expressions

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

They're as follows:

  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 behavior of processing all variable declarations in their respective scopes 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.
// However, that's not the case.
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 executes them all before running literally any other code. Once this is done, it then parses the code from the very beginning.

This means that before actually executing line 4 above, variable x is effectively declared and therefore accessible. Since var x initializes x to undefined, accessing x before the assignment statement in line 6 returns undefined.

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

The act of processing all function declarations before running any other code 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.

There is a certain edge case to look out for when considering function declarations for hoisting. This is addressed below.

Function declarations inside block statements

As we know by now, all function declarations are hoisted to the top of their scope. However, there is a little exception to this idea, and in fact, an incompatibility across different browsers.

That is, if a function declaration appears inside a block statement, that's not another function's body (which is a block statement as well), the function declaration is not hoisted normally in the majority of browsers — only its name is made accessible with the value undefined.

There is a good reason for this behavior which is apparent in the code below:

var userIsNew = false;

if (userIsNew) {
   function greet() {
      console.log('Hello!');
   }
}

The function greet() is placed inside the block statement of if. The condition upon which the block executes is that userIsNew is true. However, that's clearly not the case. And so, ideally the author of this code would expect the function to not appear in the global scope since its condition of appearance hasn't been met.

Likewise, in such a case, most browsers don't hoist the function declaration, but the name of the function is made available as an undefined identifier in the respective scope.

Some browsers, however, have an incompatibility in this scenario:

  • Safari, however, hoists the complete declaration just like any other function declaration regardless of whether the block statement, in which it dwells, executes or not.
  • Microsoft Edge, on the other hand, doesn't hoist anything, not even the name of the function.

The most important thing to realize 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's hoisted before running anything else. Next, execution starts at the beginning of the script with line 1. At this point, sum is equal to undefined, and therefore can't be called; likewise 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:

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

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

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 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 ways of denoting a function couldn't be used to create anonymous functions.

Let's first 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 as well. Failing to do so simply leads to a syntax error.

Consider the following:

// No function name.
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 Immediately Invoked Function Expression, or simply IIFE.

IIFEs are a highly useful feature of JavaScript as we shall see in the next section.

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.

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; }
}

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 be part of the local scope of the IIFE.

What this further 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 programs.

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>
   <!-- ... -->
</head>
<body>
   <!-- ... -->
   <script src="lib1.js"></script>
   <script src="lib2.js"></script>
</body>
</html>

Let's say that lib1.js defines a global 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.

In other words, there would be name collisions between both the libraries. The consequence of this would simply be unexplained errors and weird behaviors in the 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!

So for instance, the codes of both the libraries lib1.js and lib2.js above could be expressed as follows:

lib1.js
// lib1.js
(function() {
   var main;
   // ...
})();
lib2.js
// lib2.js
(function() {
   var main;
   // ...
})();

Each library has its code encapsulated inside an IIFE and likewise doesn't at all intefere with the code of the other one. Simply amazing.

Without IIFE,s achieving this encapsulation would still be possible — we'd have to first declare a function, given it a name, and then call it. However, IIFEs allow us to accomplish this encapsulation in an extremely compact and cool manner.

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 functionName(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 ES5, recursively invoking a function expression from within itself was possible only via the callee property of the arguments object.

The expression arguments.callee, when used inside a function, simply refers to the function itself.

An example follows:

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]

In the discussion for ES5, however, it was conceived to change this to something more convenient and intuitive.

So it was decided to allow for function expressions to have a name in ES5 that would only be accessible inside the body of the function. In this way, the function would be able to recursively call itself by simply referring to that name, instead of using arguments.callee.

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]

The function expression passed to nums.map() has the name fib. This is used to recursively call the function in line 6.

But note that 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

The access of fib 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.

However, Internet Explorer 8 diverts from this behavior, as detailed below.

Issues on Internet Explorer 8 and earlier

Named function expressions have a buggy behavior in Internet Explorer 8 and earlier.

In particular, a named function expression would create two separate functions in memory — one to denote the declaration and one to denote the expression. Moreover, in violation of the ECMAScript specification, it'll also provide the name of the function expression as a global variable.

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

var f = function fn() {};

Suppose we execute this code on IE8 (or earlier). Likewise, 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 behavior 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 as shown above.

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 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'

Coming back to the idea behind this, it was made to provide a way to refer to the object calling a function. This was the original idea behind its creation.

In fact, the programming language 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 that called 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 code, 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.

Taking the example of the code above, if ever in the future we change the name of the object o or assign it to some other identifier and then use that identifier instead of o, using this.x would mean that we'd always access the property x on the desired object.

This is because this would automatically point to the calling object — we won't need to configure it to the desired object ourself.

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

Ideally, whenever possible, refer to the container object inside a method's definition using the this keyword — NOT using 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 becomes yet more interesting.

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 behavior 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
  • 20
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, in the global context, as well.

In the global context, 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. Another benefit of this.

Shown below is a demonstration:

// The global context.
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 behavior 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