Design Patterns - Decorating in JavaScript

DealWithJS blog post

JavaScript

// this is the function we will decorate with different capabilities.
function isPrime(num) {
    if (num === 2 || num === 3) { 
    	return true; 
    }
    if (num % 2 === 0) { 
    	return false; 
    }
    let max = Math.ceil(Math.sqrt(num));
    for (let i = 3; i <= max; i += 2) {
        if (num % i === 0) {
            return false;
        }
    }
    return true;
}

// utility to make nice looking argument lists.
function formatArguments(args) {
	let params = JSON.stringify(Array.prototype.slice.call(args));
    return params.substr(1, params.length-2);
}

// utility to dynamically rename a function.
function renameFunction(func, name) {
    Object.defineProperty(func, 'name', { value: name });
    return func;   	
}

// decorator to log function calls with arguments, result and time.
function logDecorator(func) {
    let decorated = function () {
        let params = formatArguments(arguments),
            start = performance.now(),
            result = func.apply(this, arguments),
            time = Math.round((performance.now() - start) * 1000);
        console.log(`${decorated.name}(${params}) => ${result} (${time} μs)`);
        return result;
    };  
    return renameFunction(decorated, func.name + 'Log');
}

// decorator to cache and reuse previous results
function memoDecorator(func) {
    let cache = {},
        decorated = function () {
            let params = formatArguments(arguments);
            if (params in cache) {
                return cache[params];
            } else {
                let result = func.apply(this, arguments);
                cache[params] = result;
                return result;
            }
        };
    return renameFunction(decorated, func.name + 'Memo');
}

// try it out.
// create one with just logging, and another one with logging and memoization
let isPrimeLog = logDecorator(isPrime),
    isPrimeMemoLog = logDecorator(memoDecorator(isPrime));

let num =...