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:
Types of Execution Contexts
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:
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
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:
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)
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)
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
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:
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
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:
Use Cases for Closures
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?
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:
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!
Recommended by LinkedIn
5. Property Descriptors and Object.defineProperty()
Every property on an object has associated property descriptors. These control the behavior of properties.
Property Descriptors
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
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:
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
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
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