Introduction to Functions
Functions are one of the fundamental building blocks in JavaScript. They allow you to encapsulate a block of code, give it a name, and reuse it throughout your program. Think of functions as the verbs of programming—they're where the action happens.
The Recipe Analogy
Functions are like recipes in a cookbook:
- They have a name (function name) so you can find them easily
- They list what they need (parameters) to work properly
- They provide detailed instructions (function body) to follow
- They produce a final dish (return value) as a result
- They can be used repeatedly without rewriting the recipe every time
Just as a good chef organizes their recipes for different purposes (appetizers, main courses, desserts), a good programmer organizes functions to handle specific tasks in their program.
Why We Use Functions
- Code Reusability: Write once, use multiple times
- Modularity: Break complex problems into manageable pieces
- Abstraction: Hide implementation details behind a simple interface
- Organization: Structure code in a logical, readable way
- Maintainability: Update code in one place rather than throughout your program
- Testability: Test individual functions in isolation
Function Declarations
A function declaration (also called a function statement) defines a named function that is loaded into the execution context before the code runs. This means you can call the function before it appears in your code—a behavior known as hoisting.
Syntax
function functionName(parameter1, parameter2, /* ..., */ parameterN) {
// Function body: statements that define what the function does
return expression; // Optional return statement
}
Basic Example
// Function declaration
function greet(name) {
return `Hello, ${name}!`;
}
// Function call
console.log(greet('Alice')); // Output: Hello, Alice!
Hoisting in Action
Function declarations are hoisted, meaning you can call them before they appear in your code:
// This works even though the function is defined later
console.log(calculateArea(5, 10)); // Output: 50
// Function declaration
function calculateArea(width, height) {
return width * height;
}
Real-World Example: Form Validation
function validateEmail(email) {
// Simple regex pattern for email validation
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return pattern.test(email);
}
function validatePassword(password) {
// Password must be at least 8 characters with at least one number
return password.length >= 8 && /\d/.test(password);
}
function validateForm(email, password) {
const isEmailValid = validateEmail(email);
const isPasswordValid = validatePassword(password);
if (!isEmailValid) {
return 'Please enter a valid email address';
}
if (!isPasswordValid) {
return 'Password must be at least 8 characters and contain at least one number';
}
return 'Form is valid!';
}
// Testing the functions
console.log(validateForm('user@example.com', 'password123')); // Form is valid!
console.log(validateForm('not-an-email', 'password123')); // Please enter a valid email address
console.log(validateForm('user@example.com', 'password')); // Password must be at least 8 characters...
When to Use Function Declarations
- When you need to use a function before it's defined (thanks to hoisting)
- For main functions that form the core of your application
- When clarity and readability are more important than conciseness
- When you want to make your code's structure obvious at a glance
Function Expressions
A function expression defines a function as part of a larger expression, typically a variable assignment. Unlike function declarations, function expressions are not hoisted, so they cannot be used before they are defined.
Syntax
const functionName = function(parameter1, parameter2, /* ..., */ parameterN) {
// Function body
return expression; // Optional
};
Basic Example
// Function expression
const add = function(a, b) {
return a + b;
};
// Function call
console.log(add(5, 3)); // Output: 8
No Hoisting with Function Expressions
// This will cause an error
// console.log(subtract(10, 5)); // Error: subtract is not a function
// Function expression
const subtract = function(a, b) {
return a - b;
};
// This works because the function is already defined
console.log(subtract(10, 5)); // Output: 5
Anonymous Function Expressions
Function expressions often don't include a name (making them anonymous), but you can name them if needed:
// Anonymous function expression
const sayHello = function() {
console.log('Hello, world!');
};
// Named function expression
const factorial = function calcFactorial(n) {
// The name 'calcFactorial' is only available inside the function
if (n <= 1) return 1;
return n * calcFactorial(n - 1); // Recursion using the function name
};
console.log(factorial(5)); // Output: 120
Real-World Example: Event Handlers
// Function expression as an event handler
document.getElementById('myButton').addEventListener('click', function(event) {
console.log('Button clicked!');
event.preventDefault();
// Perform some action
updateUserInterface();
});
// Helper function
function updateUserInterface() {
// Update UI logic here
console.log('UI updated after button click');
}
When to Use Function Expressions
- When you want to assign a function to a variable or pass it as an argument
- For callback functions that won't be reused elsewhere
- When you need to create closures (functions that "remember" their creation environment)
- When you want to ensure the function is only used after definition
Arrow Functions
Arrow functions are a more concise syntax for writing function expressions, introduced in ES6 (ECMAScript 2015). They have some important differences from traditional functions, particularly regarding this binding and lack of their own arguments object.
Syntax
// Basic syntax
const functionName = (param1, param2, ...) => expression;
// With function body
const functionName = (param1, param2, ...) => {
// Function body
return expression;
};
Basic Examples
// Arrow function with implicit return
const double = x => x * 2;
// Arrow function with multiple parameters
const multiply = (a, b) => a * b;
// Arrow function with function body and explicit return
const divide = (a, b) => {
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
};
console.log(double(4)); // Output: 8
console.log(multiply(3, 5)); // Output: 15
console.log(divide(10, 2)); // Output: 5
Comparing Function Expressions and Arrow Functions
Traditional Function Expression
const numbers = [1, 2, 3, 4, 5];
// Using a function expression
const squares1 = numbers.map(function(num) {
return num * num;
});
console.log(squares1); // [1, 4, 9, 16, 25]
Arrow Function
const numbers = [1, 2, 3, 4, 5];
// Using an arrow function
const squares2 = numbers.map(num => num * num);
console.log(squares2); // [1, 4, 9, 16, 25]
The "this" Binding in Arrow Functions
A key difference with arrow functions is how they handle the this keyword. Arrow functions don't have their own this context—they inherit this from the surrounding code:
'this' is determined
when function is called"] B["Arrow Function:
'this' is inherited from
surrounding scope"] C["const obj = {
name: 'Example',
regularMethod() { /* this = obj */ },
arrowMethod: () => { /* this = parent scope */ }
}"]
Traditional Function: New this Context
const person = {
name: 'Alice',
regularFunction: function() {
console.log(this.name);
}
};
person.regularFunction(); // Output: Alice
Arrow Function: Inherited this
const person = {
name: 'Alice',
arrowFunction: () => {
console.log(this.name); // 'this' is from parent scope
}
};
person.arrowFunction(); // Output: undefined (in browser,
// 'this' refers to window)
Common Use Case: Preserving this in Callbacks
const counter = {
count: 0,
// Problem: 'this' is lost in setTimeout callback
startWithRegular: function() {
console.log("Starting regular function timer...");
setTimeout(function() {
this.count++; // 'this' refers to the timeout context, not counter
console.log(this.count); // NaN (window.count is undefined)
}, 1000);
},
// Solution: Arrow function preserves 'this'
startWithArrow: function() {
console.log("Starting arrow function timer...");
setTimeout(() => {
this.count++; // 'this' refers to counter
console.log(this.count); // 1
}, 1000);
}
};
counter.startWithRegular(); // NaN after 1 second
counter.startWithArrow(); // 1 after 1 second
Limitations of Arrow Functions
- No
argumentsobject: Arrow functions don't have their ownargumentsobject (use rest parameters instead) - Cannot be used as constructors: Arrow functions cannot be used with
new - No
superkeyword: Arrow functions don't have access tosuper - Cannot change
thisbinding: Methods likecall(),apply(), andbind()won't changethisin arrow functions
// No arguments object in arrow functions
const regularFunc = function() {
console.log(arguments);
};
const arrowFunc = () => {
console.log(arguments); // Error or refers to parent scope's arguments
};
regularFunc(1, 2, 3); // Arguments object: [1, 2, 3]
// arrowFunc(1, 2, 3); // Error or unexpected behavior
// Use rest parameters instead
const betterArrowFunc = (...args) => {
console.log(args); // Works as expected
};
betterArrowFunc(1, 2, 3); // Output: [1, 2, 3]
When to Use Arrow Functions
- For short, simple functions, especially callbacks
- When you want to preserve the
thiscontext from the surrounding code - For functional programming techniques with array methods (map, filter, reduce)
- When you need concise, one-line functions
When Not to Use Arrow Functions
- For methods in objects or classes that need their own
thiscontext - For constructor functions
- When you need access to the
argumentsobject - For functions that use
yield(generators)
Immediately Invoked Function Expressions (IIFE)
An IIFE (pronounced "iffy") is a function that runs as soon as it's defined. It's a design pattern used to create a new scope and avoid polluting the global namespace.
Syntax
// Basic IIFE syntax
(function() {
// Code executed immediately
})();
// IIFE with parameters
(function(param1, param2) {
// Code executed immediately
})(value1, value2);
Basic Example
// IIFE that computes and logs the result immediately
(function() {
const x = 10;
const y = 20;
console.log(x + y); // Output: 30
})();
// Trying to access variables defined in the IIFE
console.log(typeof x); // Output: undefined (x is not in scope)
Common Use Cases for IIFEs
- Avoiding Global Variables: Create a private scope to avoid polluting the global namespace
- Module Pattern: Create modules with private and public parts
- Isolating Declarations: Run code without interference from other scripts
- Capturing Variable Values: Preserve variable values in loops or asynchronous code
Real-World Example: Module Pattern with IIFE
// Create a counter module with private variables
const counter = (function() {
// Private variables
let count = 0;
// Return an object with public methods
return {
increment: function() {
count += 1;
return count;
},
decrement: function() {
count -= 1;
return count;
},
getValue: function() {
return count;
},
reset: function() {
count = 0;
return count;
}
};
})();
// Using the counter module
console.log(counter.getValue()); // Output: 0
console.log(counter.increment()); // Output: 1
console.log(counter.increment()); // Output: 2
console.log(counter.decrement()); // Output: 1
console.log(counter.reset()); // Output: 0
// The 'count' variable is private and not accessible
console.log(counter.count); // Output: undefined
Modern Alternative: ES6 Modules
With the introduction of ES6 modules, many use cases for IIFEs are now better handled with proper module imports and exports:
counter.js
// Private variable within the module scope
let count = 0;
// Exported functions
export function increment() {
count += 1;
return count;
}
export function decrement() {
count -= 1;
return count;
}
export function getValue() {
return count;
}
export function reset() {
count = 0;
return count;
}
main.js
// Import the functions from the counter module
import { increment, decrement, getValue, reset } from './counter.js';
console.log(getValue()); // Output: 0
console.log(increment()); // Output: 1
console.log(increment()); // Output: 2
console.log(decrement()); // Output: 1
console.log(reset()); // Output: 0
Comparing Function Types
Let's compare all the function types we've covered to help you choose the right approach for different situations.
Feature Comparison
| Feature | Function Declaration | Function Expression | Arrow Function | IIFE |
|---|---|---|---|---|
| Hoisting | Yes | No | No | N/A (runs immediately) |
Own this binding |
Yes | Yes | No (inherits from parent) | Yes |
arguments object |
Yes | Yes | No | Yes |
| Can be used as constructor | Yes | Yes | No | Technically yes, but not practical |
| Can be named | Yes (required) | Optional | No | Optional |
| Conciseness | Medium | Medium | High | Low |
Quick Decision Guide
Common Function Patterns
Beyond the basic syntax, several function patterns appear frequently in JavaScript development.
Callback Functions
A callback function is passed as an argument to another function and is executed after some operation has been completed.
// Function that takes a callback
function fetchData(callback) {
console.log('Fetching data...');
// Simulate asynchronous operation
setTimeout(() => {
const data = { id: 1, name: 'Example Data' };
callback(data);
}, 1000);
}
// Using the function with a callback
fetchData(function(data) {
console.log('Data received:', data);
});
// Using an arrow function as callback
fetchData(data => {
console.log('Data received with arrow function:', data);
});
Higher-Order Functions
Higher-order functions either take functions as arguments or return functions as results.
// Higher-order function that returns a function
function multiplier(factor) {
// Returns a function that multiplies its argument by factor
return function(number) {
return number * factor;
};
}
// Create specific multiplier functions
const double = multiplier(2);
const triple = multiplier(3);
console.log(double(5)); // Output: 10
console.log(triple(5)); // Output: 15
// Higher-order function that takes a function as an argument
function applyOperation(numbers, operation) {
const results = [];
for (const num of numbers) {
results.push(operation(num));
}
return results;
}
const numbers = [1, 2, 3, 4, 5];
console.log(applyOperation(numbers, double)); // Output: [2, 4, 6, 8, 10]
console.log(applyOperation(numbers, n => n * n)); // Output: [1, 4, 9, 16, 25]
Function Composition
Function composition involves combining multiple functions to create a new function where the output of one function becomes the input to the next.
// Simple functions to compose
function addTen(x) {
return x + 10;
}
function double(x) {
return x * 2;
}
function square(x) {
return x * x;
}
// Manual composition
const result = square(double(addTen(5)));
console.log(result); // Output: 900 (5 + 10 = 15, 15 * 2 = 30, 30² = 900)
// Helper function for composition
function compose(...functions) {
return function(x) {
return functions.reduceRight((acc, fn) => fn(acc), x);
};
}
// Create composed function
const addThenDoubleAndSquare = compose(square, double, addTen);
console.log(addThenDoubleAndSquare(5)); // Output: 900
Currying
Currying is a technique of transforming a function that takes multiple arguments into a sequence of functions that each take a single argument.
// Regular function with multiple arguments
function calculateVolume(length, width, height) {
return length * width * height;
}
// Curried version
function curriedVolume(length) {
return function(width) {
return function(height) {
return length * width * height;
};
};
}
// Using the curried function
console.log(calculateVolume(2, 3, 4)); // Output: 24
console.log(curriedVolume(2)(3)(4)); // Output: 24
// Partial application
const volumeWithLength2 = curriedVolume(2);
const volumeWithLength2Width3 = volumeWithLength2(3);
console.log(volumeWithLength2Width3(4)); // Output: 24
// Arrow function version (more concise)
const curriedVolumeArrow = length => width => height => length * width * height;
console.log(curriedVolumeArrow(2)(3)(4)); // Output: 24
Real-World Example: Form Validation System
Combining different function patterns to create a flexible validation system:
// Higher-order function to create validators
function createValidator(validationFn, errorMessage) {
return function(value) {
const isValid = validationFn(value);
return {
isValid,
message: isValid ? null : errorMessage
};
};
}
// Simple validation functions
const isNotEmpty = value => !!value && value.trim().length > 0;
const isEmail = value => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
const isMinLength = length => value => value.length >= length;
// Create specific validators using the higher-order function
const validateRequired = createValidator(isNotEmpty, 'This field is required');
const validateEmail = createValidator(isEmail, 'Please enter a valid email address');
const validatePassword = createValidator(isMinLength(8), 'Password must be at least 8 characters');
// Function composition to run multiple validators
function runValidators(value, validators) {
for (const validator of validators) {
const result = validator(value);
if (!result.isValid) {
return result;
}
}
return { isValid: true, message: null };
}
// Test the validation system
function validateForm(formData) {
const validationResults = {
name: runValidators(formData.name, [validateRequired]),
email: runValidators(formData.email, [validateRequired, validateEmail]),
password: runValidators(formData.password, [validateRequired, validatePassword])
};
// Check if all validations passed
const isFormValid = Object.values(validationResults)
.every(result => result.isValid);
return {
isValid: isFormValid,
fields: validationResults
};
}
// Test with valid data
const validData = {
name: 'John Doe',
email: 'john@example.com',
password: 'password123'
};
// Test with invalid data
const invalidData = {
name: 'Jane',
email: 'not-an-email',
password: 'short'
};
console.log(validateForm(validData));
console.log(validateForm(invalidData));
Best Practices for Functions
Writing clean, maintainable functions is an essential skill for JavaScript developers. Here are some best practices to follow:
Follow the Single Responsibility Principle
Each function should do one thing and do it well.
❌ Poor Practice
function processUserData(userData) {
// Validate data
if (!userData.name) throw new Error('Name is required');
// Update database
database.update(userData);
// Send welcome email
sendEmail(userData.email, 'Welcome!');
// Update UI
updateUserInterface(userData);
}
✅ Better Practice
function validateUserData(userData) {
if (!userData.name) throw new Error('Name is required');
return userData;
}
function updateUserInDatabase(userData) {
return database.update(userData);
}
function sendWelcomeEmail(email) {
return sendEmail(email, 'Welcome!');
}
function updateUserInterface(userData) {
// UI update logic
}
// Orchestrating function
function processUserData(userData) {
const validatedData = validateUserData(userData);
updateUserInDatabase(validatedData);
sendWelcomeEmail(validatedData.email);
updateUserInterface(validatedData);
}
Keep Functions Small and Focused
Aim for functions that are short, focused, and readable at a glance.
- Functions should typically be 10-20 lines of code
- If a function grows larger, consider refactoring into smaller functions
- Follow the "do one thing" rule: each function should solve a single problem
Use Descriptive Function Names
Function names should clearly describe what the function does, using verb-noun format when appropriate.
❌ Poor Naming
function process(data) { /* ... */ }
function handle(user, action) { /* ... */ }
function execute() { /* ... */ }
✅ Better Naming
function validateFormData(data) { /* ... */ }
function updateUserPermissions(user, action) { /* ... */ }
function fetchUserProfile() { /* ... */ }
Limit the Number of Parameters
Too many parameters make functions harder to use and understand. Aim for 3 or fewer parameters.
❌ Too Many Parameters
function createUser(
name,
email,
password,
age,
location,
preferences,
role,
status
) { /* ... */ }
✅ Object Parameter Pattern
function createUser(userData) {
// Destructure with defaults
const {
name,
email,
password,
age = 18,
location = 'Unknown',
preferences = {},
role = 'user',
status = 'active'
} = userData;
// Function logic
}
Return Early to Avoid Deep Nesting
Use early returns (guard clauses) to handle edge cases and reduce indentation.
❌ Deeply Nested Approach
function processPayment(payment) {
if (payment) {
if (payment.amount > 0) {
if (payment.method) {
// Process the payment
return true;
} else {
console.error('Payment method missing');
return false;
}
} else {
console.error('Invalid payment amount');
return false;
}
} else {
console.error('Payment object missing');
return false;
}
}
✅ Early Return Approach
function processPayment(payment) {
// Guard clauses for validation
if (!payment) {
console.error('Payment object missing');
return false;
}
if (payment.amount <= 0) {
console.error('Invalid payment amount');
return false;
}
if (!payment.method) {
console.error('Payment method missing');
return false;
}
// Main function logic
// Process the payment
return true;
}
Use Default Parameters
Default parameters make functions more robust and reduce the need for additional logic.
// Without default parameters
function createUser(name, email, role) {
// Check for missing parameters
if (role === undefined) {
role = 'user';
}
return { name, email, role };
}
// With default parameters
function createUser(name, email, role = 'user') {
return { name, email, role };
}
console.log(createUser('Alice', 'alice@example.com'));
// Output: { name: 'Alice', email: 'alice@example.com', role: 'user' }
Document Your Functions
Use comments to explain the purpose, parameters, and return value of your functions, especially for complex ones.
/**
* Calculates the discounted price based on the original price and discount percentage.
*
* @param {number} price - The original price
* @param {number} discountPercent - The discount percentage (0-100)
* @returns {number} The discounted price
* @throws {Error} If price is negative or discount is invalid
*/
function calculateDiscountedPrice(price, discountPercent = 0) {
if (price < 0) {
throw new Error('Price cannot be negative');
}
if (discountPercent < 0 || discountPercent > 100) {
throw new Error('Discount must be between 0 and 100');
}
const discount = price * (discountPercent / 100);
return price - discount;
}
Practical Exercise: Building a Calculator Library
Let's put our knowledge of functions into practice by building a calculator library that demonstrates different function types and patterns.
// Calculator library using different function types
const Calculator = (function() {
// Private utilities using function declarations
function validateNumbers(...args) {
return args.every(arg => typeof arg === 'number' && !isNaN(arg));
}
function formatResult(result) {
// Round to 2 decimal places if necessary
return Math.round(result * 100) / 100;
}
// Basic operations using function expressions
const add = function(a, b) {
if (!validateNumbers(a, b)) {
throw new Error('Both arguments must be numbers');
}
return a + b;
};
const subtract = function(a, b) {
if (!validateNumbers(a, b)) {
throw new Error('Both arguments must be numbers');
}
return a - b;
};
// Advanced operations using arrow functions
const multiply = (a, b) => {
if (!validateNumbers(a, b)) {
throw new Error('Both arguments must be numbers');
}
return a * b;
};
const divide = (a, b) => {
if (!validateNumbers(a, b)) {
throw new Error('Both arguments must be numbers');
}
if (b === 0) {
throw new Error('Cannot divide by zero');
}
return a / b;
};
// Higher-order function for creating power functions
function createPowerFn(exponent) {
return function(base) {
if (!validateNumbers(base, exponent)) {
throw new Error('Arguments must be numbers');
}
return Math.pow(base, exponent);
};
}
// Create specialized power functions
const square = createPowerFn(2);
const cube = createPowerFn(3);
// Function that takes a callback
function calculate(a, b, operation) {
try {
const result = operation(a, b);
return formatResult(result);
} catch (error) {
console.error('Calculation error:', error.message);
return null;
}
}
// Chain calculations using a builder pattern
function chain(initialValue = 0) {
let result = initialValue;
return {
add: function(value) {
result = add(result, value);
return this;
},
subtract: function(value) {
result = subtract(result, value);
return this;
},
multiply: function(value) {
result = multiply(result, value);
return this;
},
divide: function(value) {
result = divide(result, value);
return this;
},
square: function() {
result = square(result);
return this;
},
cube: function() {
result = cube(result);
return this;
},
value: function() {
return formatResult(result);
}
};
}
// Public API
return {
add,
subtract,
multiply,
divide,
square,
cube,
calculate,
chain,
// Add a power method that uses currying
power: base => exponent => Math.pow(base, exponent)
};
})();
// Using the calculator library
console.log('Basic operations:');
console.log('5 + 3 =', Calculator.add(5, 3));
console.log('10 - 4 =', Calculator.subtract(10, 4));
console.log('7 * 6 =', Calculator.multiply(7, 6));
console.log('20 / 4 =', Calculator.divide(20, 4));
console.log('\nPower functions:');
console.log('Square of 5 =', Calculator.square(5));
console.log('Cube of 3 =', Calculator.cube(3));
console.log('2 to the power of 8 =', Calculator.power(2)(8));
console.log('\nCalculate with callbacks:');
console.log('8 + 2 =', Calculator.calculate(8, 2, Calculator.add));
console.log('8 - 2 =', Calculator.calculate(8, 2, Calculator.subtract));
console.log('8 * 2 =', Calculator.calculate(8, 2, Calculator.multiply));
console.log('8 / 2 =', Calculator.calculate(8, 2, Calculator.divide));
console.log('8 / 0 =', Calculator.calculate(8, 0, Calculator.divide)); // Error handling
console.log('\nChain calculations:');
const result = Calculator.chain(10)
.add(5) // 15
.multiply(2) // 30
.subtract(8) // 22
.divide(2) // 11
.square() // 121
.value();
console.log('Chain result:', result);
// Advanced usage: create custom operation
const average = (a, b) => (a + b) / 2;
console.log('\nCustom operation (average):', Calculator.calculate(10, 20, average));
Challenge Extensions
- Add more operations like modulo, percentage, and square root
- Extend the calculator to handle arrays of numbers
- Add memory functions (store, recall, clear memory)
- Implement a history feature that stores previous calculations
- Create a scientific calculator with trigonometric functions
Summary
In this lecture, we've explored the different ways to create and use functions in JavaScript:
- Function Declarations: Named, hoisted functions that can be called before they're defined
- Function Expressions: Functions assigned to variables, not hoisted
- Arrow Functions: Concise functions with lexical this binding, ideal for callbacks
- Immediately Invoked Function Expressions (IIFEs): Self-executing functions that create private scopes
We also covered common function patterns and best practices that will help you write clean, maintainable code.
Functions are one of the most powerful features in JavaScript, allowing you to:
- Create reusable code
- Organize your program into logical units
- Implement complex patterns like closures, currying, and composition
- Build modular applications with private and public parts
By mastering the different types of functions and when to use them, you'll be well-equipped to tackle a wide range of programming challenges.
Further Learning
Practice Activities
- Function Type Converter: Take existing JavaScript code and rewrite functions using different function types (declaration to expression, expression to arrow, etc.).
- Utility Library: Create a library of utility functions for string and array manipulation using a mixture of function types.
- Higher-Order Function Challenge: Implement map, filter, and reduce functions from scratch that work like the native array methods.
- Currying and Composition: Create a set of functions that can be composed and curried to process text data in different ways.
- Event Manager: Build an event system with subscribe and publish functions that demonstrates callbacks and closure concepts.