Functional JavaScript
const factorial = function ( n ) {
if ( n === 0 ) {
return 1;
}
return n * factorial( n - 1 );
};
console.log(factorial( 10 )); // 3628800
// Recursion with tail call optiomization
const factorial = function ( n, base ) {
if ( n === 0 ) {
return base;
}
base *= n;
return factorial( n - 1, base );
};
console.log(factorial( 10, 1 )); // 3628800
// Impure function
// A function with a side effect - its output is different with same input
var x = 10;
const myFunc = function ( y ) {
x = x + y;
};
myFunc( 3 );
console.log( x ); // 13
myFunc( 3 );
console.log( x ); // 16
// Pure functions
// Ways to make the above function pure
// Example 1. Make the global variable local.
const myFunc = function ( y ) {
const x = 10;
return x + y;
}
console.log(myFunc( 3 )); // 13
console.log(myFunc( 3 )); // 13
// Example 2. Pass x as an argument.
const x = 10;
const myFunc = function ( x, y ) {
return x + y;
}
console.log(myFunc( x, 3 )); // 13
console.log(myFunc( x, 3 )); // 13
// push mutates arrays.
const arrayOne = [1, 2, 3];
arrayOne.push( 4 );
console.log( arrayOne ); // [1, 2, 3, 4]
// concat creates a new array and leaves the original unchanged.
const arrayTwo = [1, 2, 3];
const arrayThree = arrayTwo.concat([ 4 ]);
console.log( arrayTwo ); // [1, 2, 3]
console.log( arrayThree ); // [1, 2, 3, 4]
// Other useful non-mutator array methods include map, filter, and reduce.
const x = 1;
x = 2; // not allowed
const myArray = [1, 2, 3];
myArray = [0, 2, 3]; // not allowed
myArray[0] = 0; // allowed!
const multiply = function ( x, y, z ) {
return x * y * z;
};
const curry = function ( fn ) {
return function ( x ) {
return function ( y ) {
return function ( z ) {
return fn( x, y, z );
};
};
};
};
const partApply = function ( fn, x ) {
return function ( y, z ) {
return fn( x, y, z );
};
};
const curriedMultiply = curry( multiply );
const partiallyAppliedMultiply = partApply( multiply, 10 );
console.log(curriedMultiply( 10 )( 5 )( 2 )); // 100
console.log(partiallyAppliedMultiply( 5, 2 )); // 100
const multiply = function ( x, y ) {
return x * y;
};
const curry = function ( fn ) {
return function ( x ) {
return function ( y ) {
return fn( x, y );
};
};
};
const curriedMultiply = curry( multiply );
const double = curriedMultiply( 2 );
const triple = curriedMultiply( 3 );
const quadruple = curriedMultiply( 4 );
console.log(triple( 6 )); // 18
// This version of composeTwo can accept any number of arguments for the initial function.
const composeTwo = function ( f, g ) {
return function ( ...args ) {
return g( f( ...args ) );
};
};
// composeMany can accept any number of functions as well as any number of arguments for the
// initial function.
const composeMany = function ( ...args ) {
const funcs = args;
return function ( ...args ) {
funcs.forEach(( func ) => {
args = [func.apply( this, args )];
});
return args[0];
};
};
const add = function ( x, y ) {
return x + y;
};
const square = function ( x ) {
return x * x;
};
const composeTwo = function ( f, g ) {
return function ( x, y ) {
return g( f ( x, y ) );
};
};
const addThenSquare = composeTwo( add, square );
// Partial function application
const multiply = function ( x, y ) {
return x * y;
};
const partApply = function ( fn, x ) {
return function ( y ) {
return fn( x, y );
};
};
const double = partApply( multiply, 2 );
const triple = partApply( multiply, 3 );
const quadruple = partApply( multiply, 4 );
// Object immutability
// Object.freeze
const frozenObject = Object.freeze( { valueOne : 1, valueTwo : { nestedValue : 1 } } );
frozenObject.valueOne = 2; // not allowed
frozenObject.valueTwo.nestedValue = 2; // allowed!
// Object.assign
const objectOne = { valueOne : 1 };
const objectTwo = { valueTwo : 2 };
const objectThree = Object.assign( {}, objectOne, objectTwo );
console.log( objectThree ); // { valueOne : 1, valueTwo : 2 }
From Beth Allchurch's tutorial JavaScript and Functional Programming