How JavaScript Works: Functional Style and Comparison
2021-06-10•6 min read
How JavaScript Works: Functional Style and Comparison
Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data. In this article, we'll explore functional programming in JavaScript and compare it with other programming paradigms.
Core Concepts of Functional Programming
1. Pure Functions
javascript// Impure function
let total = 0;
function addToTotal(amount) {
total += amount;
return total;
}
// Pure function
function add(a, b) {
return a + b;
}
2. Immutability
javascript// Mutable approach
const user = { name: 'John', age: 30 };
user.age = 31;
// Immutable approach
const updatedUser = { ...user, age: 31 };
3. Function Composition
javascriptconst compose = (...fns) =>
fns.reduce((f, g) => (...args) => f(g(...args)));
const addOne = x => x + 1;
const double = x => x * 2;
const addOneAndDouble = compose(double, addOne);
console.log(addOneAndDouble(3)); // 8
Functional Programming Techniques
1. Higher-Order Functions
javascript// Map
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(x => x * 2);
// Filter
const evenNumbers = numbers.filter(x => x % 2 === 0);
// Reduce
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
2. Currying
javascriptconst curry = (fn) => {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return (...moreArgs) => curried.apply(this, args.concat(moreArgs));
};
};
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
3. Function Pipelines
javascriptconst pipe = (...fns) =>
fns.reduce((f, g) => (...args) => g(f(...args)));
const addOne = x => x + 1;
const double = x => x * 2;
const square = x => x * x;
const transform = pipe(addOne, double, square);
console.log(transform(3)); // 64
Comparison with Other Paradigms
1. Imperative Programming
javascript// Imperative
function sumArray(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
// Functional
const sumArray = arr => arr.reduce((sum, num) => sum + num, 0);
2. Object-Oriented Programming
javascript// Object-Oriented
class Calculator {
constructor() {
this.value = 0;
}
add(x) {
this.value += x;
return this;
}
subtract(x) {
this.value -= x;
return this;
}
}
// Functional
const calculator = {
add: x => y => x + y,
subtract: x => y => x - y
};
Practical Applications
1. Data Transformation
javascriptconst users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
];
// Functional approach
const getAdultNames = users =>
users
.filter(user => user.age >= 18)
.map(user => user.name)
.sort();
2. Event Handling
javascript// Functional event handling
const handleClick = compose(
logEvent,
updateState,
preventDefault
);
button.addEventListener('click', handleClick);
3. State Management
javascript// Functional state management
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
Best Practices
1. Use Pure Functions
javascript// Bad: Impure function
function calculateTotal(items) {
let total = 0;
items.forEach(item => {
total += item.price;
});
return total;
}
// Good: Pure function
const calculateTotal = items =>
items.reduce((total, item) => total + item.price, 0);
2. Avoid Side Effects
javascript// Bad: Side effects
function updateUser(user) {
user.lastUpdated = new Date();
return user;
}
// Good: No side effects
const updateUser = user => ({
...user,
lastUpdated: new Date()
});
3. Use Function Composition
javascript// Bad: Nested function calls
const result = processData(transformData(filterData(data)));
// Good: Function composition
const process = compose(processData, transformData, filterData);
const result = process(data);
Performance Considerations
1. Memoization
javascriptconst memoize = (fn) => {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
};
const expensiveOperation = memoize(x => {
// Expensive computation
return x * x;
});
2. Lazy Evaluation
javascriptconst lazyMap = function* (fn, iterable) {
for (const item of iterable) {
yield fn(item);
}
};
const numbers = [1, 2, 3, 4];
const doubled = lazyMap(x => x * 2, numbers);
Conclusion
Functional programming in JavaScript offers several benefits:
- More predictable code
- Easier testing
- Better code reuse
- Improved maintainability
However, it's important to:
- Choose the right paradigm for your use case
- Balance functional and imperative approaches
- Consider performance implications
- Use appropriate tools and libraries
By understanding and applying functional programming concepts, you can write more maintainable and reliable JavaScript code.