Functions Objects Scope and more

Functions Objects Scope and more

Download the full guide at https://basescripts.com/javascript-weird-parts-and-quirks

Execution Contexts and Scopes

1. What is an Execution Context?

An Execution Context is an environment where JavaScript code is parsed and executed. It defines what variables, objects, and functions are accessible during code execution.

When the JavaScript engine runs code, it creates an execution context to keep track of:

  • What variables are accessible?
  • Where is the function currently executing?
  • What is the value of this?

Types of Execution Contexts

  1. Global Execution Context (GEC)
  2. Function Execution Context (FEC)
  3. Eval Execution Context

Example of Global and Function Execution Contexts

// Global Context

let globalVar = 'I am global';

function outerFunction() {

// Function Context (FEC for outerFunction)

let outerVar = 'I am outer';

function innerFunction() {

// Function Context (FEC for innerFunction)

let innerVar = 'I am inner';

console.log(globalVar); // 'I am global' (global context)

console.log(outerVar);  // 'I am outer' (outer function context)

console.log(innerVar);  // 'I am inner' (local context)

}

innerFunction();

}

outerFunction();

Explanation:

  1. The Global Context is created when the script starts running.
  2. When outerFunction is called, a new Function Execution Context (FEC) is created.
  3. When innerFunction is called, another FEC is created, and the scope chain ensures globalVar and outerVar are accessible.

2. Lexical Environment and Scope Chain

A Lexical Environment is a mechanism for variable lookup. It defines how and where variable names are resolved during code execution.

When a function is created, it "remembers" where it was defined, and this creates its lexical environment. This is what makes closures possible.

How the Scope Chain Works

  1. Scope Chain: If a variable is not found in the current context, JavaScript "walks up" to its parent context to look for it.
  2. If it doesn't find the variable in the parent context, it continues to the next outer context, eventually reaching the global context.

Example of Lexical Environment and Scope Chain

let a = 'Global';

function outerFunction() {

let b = 'Outer';

function innerFunction() {

let c = 'Inner';

console.log(a); // Global (global context)

console.log(b); // Outer (outerFunction context)

console.log(c); // Inner (innerFunction context)

}

innerFunction();

}

outerFunction();

Explanation:

  • innerFunction can access a, b, and c, even though a and b are defined outside its scope.
  • This happens because lexical scoping allows a function to "remember" where it was defined, not where it is called.

3. Function Scope vs. Block Scope

JavaScript originally had only function scope with var, but with the introduction of let and const, it now supports block scope as well.

Function Scope (with var)

  • Variables declared with var are scoped to the nearest function.
  • If var is declared outside a function, it is attached to the global object.

Example of Function Scope

function exampleFunction() {

var x = 10;

if (true) {

var x = 20; // This reassigns the same x (since var is function-scoped)

}

console.log(x); // 20

}

exampleFunction();

Explanation: The var x inside the if block modifies the x in the function because var is function-scoped, not block-scoped.

Block Scope (with let and const)

  • let and const are block-scoped, meaning they exist only inside {} curly braces.

Example of Block Scope

function exampleFunction() {

let x = 10;

if (true) {

let x = 20; // This is a new x, scoped only inside this block

console.log(x); // 20

}

console.log(x); // 10 (not affected)

}

exampleFunction();

4. Understanding this in Different Contexts

The value of this depends on how the function is called, not where it was defined.

Rules for this

  1. Global Context: this refers to the global object (window in browsers, global in Node.js).
  2. Object Method: If a function is called as a method of an object, this points to the object.
  3. Arrow Functions: They do not have their own this. They inherit this from the parent scope.
  4. Explicit Binding: Using call, apply, and bind, you can control the value of this.

Example of this in Object Methods

const person = {

name: 'Alice',

greet() {

console.log(`Hello, my name is ${this.name}`);

}

};

person.greet(); // 'Hello, my name is Alice'

Explanation: The this keyword inside greet points to the person object, as it is the caller.

Example of Arrow Functions and this

const person = {

name: 'Alice',

greet: () => {

console.log(`Hello, my name is ${this.name}`);

}

};

person.greet(); // 'Hello, my name is undefined'

Explanation:

  • Arrow functions don't have their own this.
  • this points to the global object instead of the person object.

5. Closures and Their Uses

A closure is a function that "remembers" its outer variables even when the outer function has finished executing.

How Closures Work

  1. When you define a function inside another function, the inner function "remembers" the variables of the outer function.
  2. Even when the outer function has finished execution, the inner function retains access to its parent scope.

Example of a Closure

function outerFunction(outerVariable) {

return function innerFunction(innerVariable) {

console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);

};

}

const closureExample = outerFunction('outside');

closureExample('inside');

// Output: Outer: outside, Inner: inside

Explanation:

  • outerVariable is "remembered" by innerFunction, even after outerFunction has completed execution.

Use Cases for Closures

  1. Data Privacy / Private Variables
  2. Function Factories (functions that return other functions)
  3. Event Handlers (where handlers have access to the context)
  4. Currying (breaking down a multi-argument function into multiple single-argument functions)

Summary of Key Concepts

Concept

Description

Execution Context

Environment for running code

Lexical Environment

Describes variable lookup mechanism

Scope Chain

Chain of variable lookups

this

Dynamic reference to the calling object

Closures

Functions that "remember" outer variables

Objects and Prototypes

1. What are JavaScript Objects?

JavaScript objects are collections of key-value pairs. Unlike primitive data types (like numbers, strings, and booleans), objects can store multiple values in a single variable. Objects can also have methods — functions associated with the object.

How to Create Objects

There are multiple ways to create objects in JavaScript:

Object Literal (Most Common) const person = {

name: 'Alice',

age: 25,

greet: function() {

console.log(`Hello, my name is ${this.name}`);

}

};

person.greet(); // Output: Hello, my name is Alice

Using the Object() Constructor const person = new Object();

person.name = 'Bob';

person.age = 30;

person.greet = function() {

console.log(`Hello, my name is ${this.name}`);

};

person.greet(); // Output: Hello, my name is Bob

Using Object.create() (Prototypal Inheritance) const prototypePerson = {

greet: function() {

console.log(`Hello, my name is ${this.name}`);

}

};

const person = Object.create(prototypePerson);

person.name = 'Charlie';

person.greet(); // Output: Hello, my name is Charlie

Using ES6 Classes (More modern approach) class Person {

constructor(name, age) {

this.name = name;

this.age = age;

}

greet() {

console.log(`Hello, my name is ${this.name}`);

}

}

const person = new Person('Diana', 28);

person.greet(); // Output: Hello, my name is Diana

2. Object Properties and Methods

Object Properties

Properties are key-value pairs stored inside an object.

Accessing Properties

Dot Notation: const car = { brand: 'Toyota', model: 'Corolla' };

console.log(car.brand); // Output: Toyota

Bracket Notation (useful when the property name contains spaces or special characters): const car = { 'car-brand': 'Toyota', model: 'Corolla' };

console.log(car['car-brand']); // Output: Toyota

Adding Properties Dynamically: const car = {};

car.brand = 'Honda';

car.model = 'Civic';

console.log(car); // { brand: 'Honda', model: 'Civic' }

Object Methods

A method is a function that belongs to an object.

How to Add Methods

Using Function Expressions const dog = {

name: 'Buddy',

bark: function() {

console.log('Woof! Woof!');

}

};

dog.bark(); // Output: Woof! Woof!

ES6 Shorthand Syntax const dog = {

name: 'Buddy',

bark() {

console.log('Woof! Woof!');

}

};

dog.bark(); // Output: Woof! Woof!

3. The Prototype Chain and Prototypal Inheritance

What is the Prototype?

Every JavaScript object has a hidden, internal property called [[Prototype]]. It links the object to another object (its prototype), from which it can inherit properties and methods.

How Does Prototypal Inheritance Work?

  1. When you try to access a property on an object, JavaScript looks for it on the object itself.
  2. If the property is not found, it looks for it in the object's prototype.
  3. This lookup continues up the prototype chain until the property is found or the end of the chain is reached (null).

Example of the Prototype Chain

const animal = {

eat() {

console.log('I am eating');

}

};

const dog = Object.create(animal);

dog.bark = function() {

console.log('Woof! Woof!');

};

dog.eat(); // Output: I am eating (inherited from animal)

dog.bark(); // Output: Woof! Woof! (method defined on dog)

Explanation:

  • dog inherits from animal via Object.create(animal).
  • The method eat() exists in animal, so JavaScript "walks up" the prototype chain to find it.

4. Using Object.create() for Prototypes

The Object.create() method allows you to create an object with a specific prototype.

Example

const animal = {

speak() {

console.log('I am an animal');

}

};

const dog = Object.create(animal);

dog.bark = function() {

console.log('Woof! Woof!');

};

dog.speak(); // Output: I am an animal

dog.bark();  // Output: Woof! Woof!

5. Property Descriptors and Object.defineProperty()

Every property on an object has associated property descriptors. These control the behavior of properties.

Property Descriptors

  1. writable: Can the value be changed? (default: true)
  2. enumerable: Can the property be listed in loops like for...in? (default: true)
  3. configurable: Can the property be modified or deleted? (default: true)

Using Object.defineProperty()

This method allows you to define non-enumerable or read-only properties.

Example

const person = {};

Object.defineProperty(person, 'name', {

value: 'Alice',

writable: false,

enumerable: true,

configurable: false

});

console.log(person.name); // Alice

person.name = 'Bob'; // This line will not change the value because writable is false

console.log(person.name); // Alice

6. Prototypes in ES6 Classes

When you create a class in ES6, all methods are automatically placed on the prototype of the object.

Example

class Animal {

constructor(name) {

this.name = name;

}

speak() {

console.log(`${this.name} makes a sound`);

}

}

const dog = new Animal('Buddy');

dog.speak(); // Buddy makes a sound

7. Checking Object Properties

You can check if an object has a specific property using the following methods.

in operator const dog = { breed: 'Labrador' };

console.log('breed' in dog); // true

hasOwnProperty() const dog = { breed: 'Labrador' };

console.log(dog.hasOwnProperty('breed')); // true

8. Object Methods

JavaScript provides several useful object utility methods.

Method

Description

Object.keys(obj)

Returns an array of all property names

Object.values(obj)

Returns an array of all property values

Object.entries(obj)

Returns an array of key-value pairs

Object.assign()

Copies properties from one object to another

Object.freeze()

Makes an object immutable (read-only)

Object.seal()

Prevents adding new properties but allows modification of existing ones

Summary of Key Concepts

Concept

Description

Object

A collection of key-value pairs

Prototype

The object from which other objects inherit

Property Descriptors

Define if a property is writable, enumerable, or configurable

Object.create()

Creates a new object with a specific prototype

ES6 Classes

Modern syntax for creating objects and prototypes

Functions and Closures

1. What are Functions in JavaScript?

A function is a reusable block of code designed to perform a specific task. Functions make it easier to avoid repetition, structure logic, and keep your code clean and modular.

Key Characteristics of Functions

  • Reusable: Functions can be called multiple times.
  • Accept Inputs: Functions accept parameters.
  • Return Outputs: Functions can return values.
  • Modular: Functions help split complex logic into smaller, maintainable parts.

Types of Functions in JavaScript

Function Declaration (hoisted) function greet(name) {

return Hello, ${name};

}

console.log(greet('Alice')); // Output: Hello, Alice

Function Expression (not hoisted) const greet = function(name) {

return Hello, ${name};

};

console.log(greet('Bob')); // Output: Hello, Bob

Arrow Functions (ES6+) const greet = (name) => Hello, ${name};

console.log(greet('Charlie')); // Output: Hello, Charlie

Key Differences:

  • Function Declarations are hoisted, meaning they can be called before their definition.
  • Function Expressions and Arrow Functions are not hoisted.

2. Function Parameters and Arguments

Default Parameters

If a parameter is not provided, JavaScript assigns the default value.

function greet(name = 'Guest') {

console.log(`Hello, ${name}`);

}

greet(); // Output: Hello, Guest

greet('David'); // Output: Hello, David

Rest Parameters

The ...rest syntax allows functions to accept multiple arguments as an array.

function sum(...numbers) {

return numbers.reduce((total, num) => total + num, 0);

}

console.log(sum(1, 2, 3, 4)); // Output: 10

Arguments Object (Non-Arrow Functions Only)

In traditional (non-arrow) functions, the arguments object contains all arguments passed to the function.

function showArguments() {

console.log(arguments);

}

showArguments(1, 'hello', true);

// Output: [1, "hello", true]

3. Function Scope and Hoisting

Function Scope

Variables declared inside a function are not accessible outside that function.

function exampleFunction() {

let localVar = 'I am local';

console.log(localVar); // Accessible here

}

exampleFunction();

console.log(localVar); // ReferenceError: localVar is not defined

Function Hoisting

Function declarations are hoisted to the top of their scope, meaning you can call them before they are defined.

helloWorld(); // Works even before the function definition

function helloWorld() {

console.log('Hello, World!');

}

Function expressions and arrow functions are not hoisted.

greet(); // ReferenceError: Cannot access 'greet' before initialization

const greet = () => {

console.log('Hi there!');

};

4. Callbacks and Higher-Order Functions

What is a Callback Function?

A callback function is a function passed as an argument to another function and is called later.

function processUserInput(callback) {

const userInput = 'Hello';

callback(userInput);

}

processUserInput((input) => console.log(`User input is: ${input}`));

// Output: User input is: Hello

What is a Higher-Order Function?

A higher-order function is a function that can accept other functions as arguments or return another function.

Example: Higher-Order Function

function multiplyBy(factor) {

return function(number) {

return number * factor;

};

}

const double = multiplyBy(2);

console.log(double(5)); // Output: 10

5. Closures: A Deep Dive

A closure is created when a function "remembers" its outer scope. Even after the outer function has finished executing, the inner function still has access to the outer function's variables.

How Closures Work

  1. When a function is returned from another function, it "remembers" the variables from its lexical environment.
  2. This allows the inner function to access variables that were in scope at the time the outer function was defined.

Example of a Closure

function outerFunction(outerVariable) {

return function innerFunction(innerVariable) {

console.log(`Outer: ${outerVariable}, Inner: ${innerVariable}`);

};

}

const closureExample = outerFunction('outside');

closureExample('inside');

// Output: Outer: outside, Inner: inside

Practical Use Cases of Closures

  1. Data Privacy / Private Variables
  2. Function Factories (functions that create other functions)
  3. Event Handlers (functions bound to UI events)
  4. Currying (transforming functions into multiple functions)

Example: Private Variables

function createCounter() {

let count = 0;

return function() {

count++;

console.log(`Current count: ${count}`);

};

}

const counter = createCounter();

counter(); // Output: Current count: 1

counter(); // Output: Current count: 2

6. Function Methods (call, apply, bind)

call()

Calls a function with a specific this value and arguments.

const person = {

name: 'Alice',

};

function greet(greeting) {

console.log(`${greeting}, ${this.name}`);

}

greet.call(person, 'Hello'); // Output: Hello, Alice

apply()

Like call(), but it takes an array of arguments instead of individual arguments.

greet.apply(person, ['Hi']); // Output: Hi, Alice

bind()

Returns a new function with this bound to a specific value.

const boundGreet = greet.bind(person, 'Hey');

boundGreet(); // Output: Hey, Alice

7. Currying and Partial Application

What is Currying?

Currying is a technique of transforming a multi-argument function into a chain of functions, each accepting a single argument.

Example of Currying

function add(x) {

return function(y) {

return x + y;

};

}

const add5 = add(5);

console.log(add5(3)); // Output: 8

8. Arrow Functions: Key Differences

Feature

Traditional Functions

Arrow Functions

this context

Dynamic (based on caller)

Lexically bound

arguments object

Available

Not available

Syntax

Verbose

Short and concise

Summary of Key Concepts

Concept

Description

Function

A reusable block of code

Closure

A function that "remembers" outer variables

Call, Apply, Bind

Control this in function calls

Currying

Break down a function into smaller parts

Rest Parameters

Accept multiple arguments as an array

Practice Questions

  1. What is the main difference between var, let, and const in function declarations?
  2. How do you create a private variable using closures?
  3. What does bind() do, and how does it differ from call()?

To view or add a comment, sign in

More articles by JavaScript Developer WorldWide

Others also viewed

Explore content categories