Today’s snippet is going to be shorter than usual, because there’s a really easy solution. Many objects have a default method like toString(). You may want to add one of those to your own functions – provide a default method if no method is specified. In this post I’ll cover one way to do that. In future posts, now that we have v8 in Apps Script, we’ll look at some more exotic alternatives using Proxy and Reflect.
Motivation
While creating Sheets Workbook functions – converted to Apps Script I noticed that sometimes a function should return an Array version of its result and sometimes not. I wanted to find an easy way of detecting whether the .array() method was present.
Examples
// should return a non-array result (the default)
sumproduct(a1,a2)
// should return an array result
sumproduct(a1,a2).array()
example of default method
A simple example
This example just counts the number of arguments, or stringifies them via 2 specific methods. However, I want the countArgs method to be the default when no method is specified
const tdef = () => {
// function with a default method
const dust = (() => {
// these are the methods
const methods = {
countArgs: (...args) => args.length,
stringArgs: (...args) => JSON.stringify(args)
}
// the default method
const defaultMethod = methods.countArgs
// add the others as properties
Object.keys(methods).forEach(key => defaultMethod[key] = methods[key])
return defaultMethod
})()
console.log(dust(1, 2, 3)) //3
console.log(dust.countArgs(1, 2, 3)) //3
console.log(dust.stringArgs(1, 2, 3)) //[1,2,3]
}
inline example
Generalized closure
You can see from the above example that there’s an opportunity to generalize this to create a closure version with a default method.
/**
* makes a closure with a default method
* @param {object} options arg wrapper
* @param {object} options.target the original object
* @param {function} options.defaultMethod the method to add
* @return {function} the closure with the default method
*/
const makeDust = ({ target, defaultMethod }) => {
return (() => {
// add the others as properties
Object.keys(target).forEach(key => defaultMethod[key] = target[key])
return defaultMethod
})()
}
generalized closure
Using the generalized closure
Here’s the original example, but this time using the generalizer
const mDef = () => {
const target = {
countArgs: (...args) => args.length,
stringArgs: (...args) => JSON.stringify(args)
}
const dust = makeDust({ target, defaultMethod: target.countArgs })
console.log(dust(1, 2, 3)) // 3
console.log(dust.countArgs(1, 2, 3)) // 3
console.log(dust.stringArgs(1, 2, 3)) // [1,2,3]
}
using generalized closure
A separate default function
In the examples so far, the default method has been one that already exists in the object, but you can also make a completely separate default method.
const mDef2 = () => {
const target = {
countArgs: (...args) => args.length,
stringArgs: (...args) => JSON.stringify(args)
}
const dust = makeDust({ target, defaultMethod: (...args) => Math.min(...args) })
console.log(dust(1, 2, 3)) // 1
console.log(dust.countArgs(1, 2, 3)) // 3
console.log(dust.stringArgs(1, 2, 3)) // [1,2,3]
}
new default method