JavaScript Unwrapped (Part IV): Basics of Functions & Function Context
Topics: Basics of Functions & Function Context
Mastering the basics of Functions in JavaScript
In our previous article, we delved into key JavaScript concepts such as let, const, and var, explored operators, and examined the powerful switch statement, if-else statements, and the intricacies of hoisting. These fundamental topics lay the groundwork for understanding how JavaScript operates under the hood and help you write cleaner, more efficient code.
Now that you have a solid grasp of these essentials, it's time to shift our focus to one of the most critical components of JavaScript: functions. Functions are at the heart of JavaScript programming—they allow you to encapsulate code into reusable blocks, making your code more modular and easier to manage. In this article, we'll explore different types of functions, including function declarations, expressions, arrow functions, default parameters, and higher-order functions. Let's dive in!
1. Function Declarations: The Classic Approach
Function declarations are the traditional way to define functions in JavaScript. They are defined using the function keyword and are hoisted to the top of their scope, allowing you to call them before their definition in the code.
Syntax:
Example:
Explanation:
Declaration:
function greet(name) { ... }defines a function namedgreetthat takes one parameter,name.Hoisting: The
greetfunction can be called before its declaration due to hoisting.
Function declarations are straightforward and easy to understand, making them a reliable choice for defining functions in JavaScript.
2. Function Expressions: Anonymous Functions
Function expressions involve defining a function and assigning it to a variable.
Unlike function declarations, function expressions are not hoisted.
They must be defined before you can use them.
Syntax:
Example:
Explanation:
Expression:
const greeting = function(name) { ... };creates a function and assigns it to thegreetingvariable.Anonymous: This function doesn’t have a name and is often used when a function is needed as a value, such as in callbacks.
Function expressions are useful for cases where you need a function only in a specific context and don’t need to refer to it by name.
3. Arrow Functions: Concise and Modern
Arrow functions provide a more concise syntax for writing functions. They also have a different behavior for this, making them suitable for certain use cases, especially in modern JavaScript.
Syntax:
Example:
Shorter Syntax:
For functions that have a single expression, you can omit curly braces and the return statement:
Concise: Arrow functions allow you to write functions more compactly.
No this Binding: Arrow functions inherit this from their surrounding context, which can be useful in some scenarios. (don’t worry if this goes above your head! - JUST LOOK AT THE NEXT SECTION!)
Arrow functions are a modern addition to JavaScript that can make your code more readable and succinct.
Function Context (Important!)
Function Declarations and Function Expressions
In regular functions (both declarations and expressions), this refers to the object that the function is called on (execution context). If the function is called in the global context, this refers to the global object (window in browsers or global in Node.js). Note that, all functions that we wrote in this article are called in the global context.
However, if a function is called as a method of an object, this refers to that object.
(don’t panic if you don’t understand the last sentence! - this will be cleared when we do objects. Focus on the bold and italicized sentence!)
Let us look at an example. This example will also make it clear why it is recommended to run JavasScript code in strict mode using the words ‘use strict’ at the beginning of the script.
In Non-Strict Mode: this defaults to the global object.
In Strict Mode: this is undefined in regular functions when they're invoked in the global context.
Arrow Functions
Arrow functions are different from regular functions in how they treat this. In arrow functions, this is lexically bound, meaning it uses this from the surrounding code at the time the function is created.
It does not refer to the object on which it was called but instead takes the this value from its enclosing function or scope.
const arrowFunc = () => {
console.log(this);
};
arrowFunc();
// `this` refers to the surrounding context (global object in this context), not to the global object alwaysKey Differences
Regular functions:
thisdepends on how the function is called. It can refer to the global object, an object that the function is called on, or beundefinedin strict mode.Arrow functions:
thisis always inherited from the surrounding scope at the time the function is defined, making it more predictable.
Let us write the same code in strict mode and understand the differences it makes!
Explanation of the Output in Strict Mode
regularFunction()Call:In strict mode, when
regularFunctionis called,thisisundefinedbecause the function is invoked in the global context without any object as its owner.Output:
"Regular Function:" undefined
arrowFunction()Call:Arrow functions do not have their own
this. Instead,thisis lexically bound to the scope in which the arrow function was defined. Here,arrowFunctionwas defined in the global context, sothisrefers to the global object (even in strict mode).Output:
"Arrow Function:" [object Window](or[object global]in Node.js)
outerFunction()Call:Inside
outerFunction, we have two nested functions:innerFunction(a regular function) andinnerArrowFunction(an arrow function).
a.
innerFunction()Call:In strict mode,
thisinsideinnerFunctionisundefinedbecause it's a regular function not invoked as a method of an object.Output:
"Inside Regular Function:" undefined
b.
innerArrowFunction()Call:Since
innerArrowFunctionis an arrow function, it inheritsthisfrom theouterFunction's context. In strict mode,outerFunctionitself is executed withthisasundefined, soinnerArrowFunctionlogsundefined.Output:
"Inside Arrow Function:" undefined
Summary of Outputs in Strict Mode
// Regular Function: undefined
// Arrow Function: [object Window]
// Inside Regular Function: undefined
// Inside Arrow Function: undefinedConclusion
Regular Functions: In strict mode,
thisisundefinedwhen the function is invoked without an explicit context (like the global object).Arrow Functions: They inherit
thisfrom their defining scope. If defined in a scope wherethisisundefined(as it is in strict mode),thisremainsundefinedinside the arrow function as well.
What happens when function is defined within an object? (IMPORTANT!)
(NOTE: I am adding this section, so that, you can refer to this article for a complete reference on Function Context. Come back to this section, once you are done with objects in JS)
When a function is defined inside an object, the behavior of this changes based on whether the function is a regular function or an arrow function. Let's explore how this behaves in both cases, with and without strict mode.
Regular Function Inside an Object
For regular functions, this typically refers to the object that owns the method, regardless of strict mode.
Output: "Regular Function:" { name: "JavaScript", regularFunction: ƒ }
Explanation:
thisrefers toobjbecauseregularFunctionis called as a method ofobj.In both strict mode and non-strict mode,
thispoints toobjwhen invoked asobj.regularFunction().
Arrow Function Inside an Object
For arrow functions, this is lexically bound, meaning it doesn't refer to the object the function is a method of. Instead, it refers to this from the outer scope where the arrow function was defined.
Understanding this in Arrow Functions
When you use an arrow function inside an object, the this keyword is not bound to the object. Instead, this is lexically bound, meaning it takes the value of this from the surrounding context where the arrow function was defined.
Explanation of the Output
Arrow Function Lexical Binding:
The arrow function in
arrowFunctiondoes not have its ownthis. Instead, it inheritsthisfrom the lexical scope in which it was created.Since the arrow function was created in the global scope (where
objis defined),thisrefers to the global object.
Strict Mode Behavior:
In strict mode, the global
thisisundefinedif accessed in a regular function. However, in the global context,thisstill refers towindow(orglobalin Node.js) when accessed directly.The arrow function inherits
thisfrom where it was defined (the global context), which iswindow, even in strict mode.
Output
When you run the code, this inside the arrow function refers to the global object (window in browsers or global in Node.js):
Arrow Function: Window {...}Summary
Arrow Function in an Object:
The arrow function does not create its own
thiscontext. Instead, it capturesthisfrom the surrounding (lexical) context, which, in this case, is the global scope.Even in strict mode, since the arrow function was defined in the global scope,
thispoints to the global object (window).
Despite strict mode being enabled, the arrow function still logs the global object because this is lexically bound to where the function is defined, not where it is called.
Let us now look at Nested Objects
When an arrow function is defined within an object, it does not have its own this context. Instead, it lexically inherits this from the scope in which it was defined. If an arrow function is defined directly inside an object but not inside a method of that object, this refers to the outer (lexical) context.
In a typical global scope (such as in a browser), the outer context is the global object (window). Even in strict mode, the arrow function captures this from where the object was created, which is still the global object. Thus, this will refer to window inside the arrow function.
Explanation
Arrow Function: The arrow function is defined inside
nestedObj. Arrow functions do not create their ownthiscontext; instead, they inheritthisfrom the surrounding lexical environment.Global Scope: In this example, the surrounding lexical environment is the global scope because the arrow function is not enclosed by another function. As a result,
thisrefers to thewindowobject in a browser.Strict Mode: Even in strict mode, the arrow function’s
thisstill refers to thewindowobject because the global context is where the object was created, and the arrow function does not redefinethis.Output
// Inside arrowFunction: Window { ... }Note this statement in this example: The surrounding lexical environment is the global scope because the arrow function is not enclosed by another function.
The above statement raises the question: What happens if the arrow function is enclosed by another function?
What happens if the arrow function is enclosed by another function?
Explanation
regularFunctionMethod:The
regularFunctionis a method ofobj. When you callobj.regularFunction(),thisinsideregularFunctionrefers toobj.Output:
"Inside regularFunction:" { name: "JavaScript", regularFunction: ƒ }
Arrow Function Inside
regularFunction:The arrow function
arrowFunctionis defined insideregularFunction. Since arrow functions don’t have their ownthis, they inheritthisfrom the lexical context in which they were defined.In this case,
thisinsidearrowFunctionwill be the same asthisinsideregularFunction, which isobj.Output:
"Inside arrowFunction:" { name: "JavaScript", regularFunction: ƒ }
Summary of Output
// Inside regularFunction: { name: "JavaScript", regularFunction: ƒ }
// Inside arrowFunction: { name: "JavaScript", regularFunction: ƒ }Key Points
Regular Function (
regularFunction):thisinsideregularFunctionrefers to the objectobjbecause it is called as a method ofobj.Arrow Function (
arrowFunction): The arrow function insideregularFunctioninheritsthisfromregularFunction. SincethisinregularFunctionrefers toobj,thisinsidearrowFunctionalso refers toobj.
When an arrow function is defined inside a regular function within an object, this inside the arrow function is lexically bound to the this of the outer function (in this case, regularFunction). If the outer function is a method of an object, this inside both the regular function and the arrow function will refer to that object.
OK! Now, I expect that you have a clear understanding of the function context of regular and arrow functions.
Let us solve an exercise!
Consider the following code:
If you have chosen the first option, consider going through the section again. If you have chosen the second option, and you are a complete beginner, GREAT! Tally your answer against the one I’ll provide. Last but not the least, if you are last option guy, CONGRATS! WELL DONE!
Let’s now see the explanation and the result we get.
Explanation
outerObjandnestedObj:outerObjcontains a propertyouterNameand a nested objectnestedObj.nestedObjcontains a propertynestedNameand a methodregularFunction.
regularFunctionMethod:The
regularFunctionis a method ofnestedObj. When you callouterObj.nestedObj.regularFunction(),thisinsideregularFunctionrefers tonestedObj.Output:
"Inside regularFunction:" { nestedName: "Nested Object", regularFunction: ƒ }
Arrow Function Inside
regularFunction:The arrow function
arrowFunctionis defined insideregularFunction. Since arrow functions do not have their ownthis, they inheritthisfrom the lexical context in which they were defined.Here, the arrow function inherits
thisfromregularFunction, which isnestedObj.Output:
"Inside arrowFunction:" { nestedName: "Nested Object", regularFunction: ƒ }
Summary of Output
// Inside regularFunction: { nestedName: "Nested Object", regularFunction: ƒ } // Inside arrowFunction: { nestedName: "Nested Object", regularFunction: ƒ }Key Points
Regular Function (
regularFunction):thisinsideregularFunctionrefers tonestedObjbecause it is called as a method ofnestedObj.Arrow Function (
arrowFunction): The arrow function insideregularFunctioninheritsthisfromregularFunction. SincethisinregularFunctionrefers tonestedObj,thisinsidearrowFunctionalso refers tonestedObj.
When an arrow function is nested inside a regular function that is a method of a nested object, the this value inside the arrow function is lexically bound to the this value of the regular function. Since the regular function is a method of the nested object, this refers to that nested object, and so does this inside the arrow function.
(WE ARE DONE WITH OBJECTS! YOU CAN CARRY ON FROM HERE)
Default Parameters: Handling Missing Arguments
Default parameters allow you to specify default values for function parameters. This is handy when you want to ensure that a function has a fallback value if no argument is provided.
Syntax:
Example:
Explanation:
Defaults:
name = 'Guest'ensures thatnamedefaults to'Guest'if no argument is provided.Flexibility: Allows functions to handle optional parameters gracefully.
Default parameters simplify function definitions and avoid the need for manual checks for undefined values.
Higher-Order Functions: Functions That Handle Functions
Higher-order functions are functions that either take other functions as arguments or return functions. They are a key concept in functional programming and can lead to more flexible and reusable code.
Syntax:
Example:
Explanation:
Function as Argument:
repeattakes a functionactionand a numbertimes, and executesactionmultiple times.Flexibility: Higher-order functions can create powerful abstractions and reusable patterns.
Higher-order functions enable advanced coding techniques and can simplify complex operations by encapsulating behavior.
Conclusion
Understanding functions in JavaScript—from declarations and expressions to arrow functions, default parameters, and higher-order functions — is essential for writing effective code. Each type of function serves a specific purpose and can be used in different scenarios to improve your coding practices.
In our next article, we'll dive into arrays and objects, helping you deepen your understanding and enhance your JavaScript skills. Stay tuned for more insights and tips to further elevate your coding expertise!
Happy coding!




















