make(your)
(life(
better(
with {
function(al) {
programming();
}})));
Questions?
Interrupt me anytime to ask them. :)
The title should be make your life DIFFERENT... but that doesn't sell ;)
JavaScript is a prototype-based scripting language with dynamic typing and has first-class functions.
JavaScript blablabla cookies kittens unicorns dynamic typing wolfenstein patrick swayze first-class functions.
Having first-class functions means we can throw them around as we please.
And dynamic typing means that the interpreter won't complain as to what they take in or return.
What does that add up to?
JS will do what we want and how we want it to- without asking too much questions.
// some `happiness` and `GUI` stuff accessible globally
function handleHappiness(howHappyAmI) {
switch (howHappyAmI) {
case "very happy":
happiness += 2;
GUI.showAHappyFace();
break;
case "happy":
happiness += 1;
GUI.showAHappyFace();
break;
case "sad":
happiness -= 2;
GUI.showASadFace();
break;
}
}
A switch is not so bad- it works, right? But we all can do better...
right?
How about this...
var happinessStateEffects = {
"very happy": function (happiness, GUI) {
GUI.showAHappyFace();
return happiness + 2;
},
"happy": function (happiness, GUI) {
GUI.showAHappyFace();
return happiness + 1;
},
"sad": function (happiness, GUI) {
GUI.showASadFace();
return happiness - 2;
}
};
function handleHappiness(howHappyAmI) {
return happinessStateEffects[howHappyAmI](happiness, GUI);
}
Ok, how about returning a function or passing it as a param?
Let's also split this functionality in two to reflect the responsibilities.
Firstly, what can be done with the happiness:
function HappinessStateEffectsFactory() {
var happinessStateEffects = {
"very happy": function (happiness, GUI) {
GUI.showAHappyFace();
return happiness + 2;
},
"happy": function (happiness, GUI) {
GUI.showAHappyFace();
return happiness + 1;
},
"sad": function (happiness, GUI) {
GUI.showASadFace();
return happiness - 2;
}
};
this.getHappinessStateEffect = function(howHappyAmI) {
return happinessStateEffects[howHappyAmI];
};
}
And then how should it be arranged to be done:
(function(happinessFactory, happiness, GUI){
function handleHappiness(howHappyAmI) {
return happinessFactory
.getHappinessStateEffect
(howHappyAmI)
(happiness, GUI);
}
window
.useABetterModuleSolution
.handleHappiness = handleHappiness;
}(theHappinessFactory, someHappinessProvider, someGuiModule));
I'm not going to elaborate on that topic a lot - but it's implied as a rule in this presentation that we should avoid mutating state unless it's really necessary
How your code should be like...
How your code is like when you mutate too much:
The rest of this talk is going to be pure JS meat.
Please don't forget about asking me questions! ;)A function returning a function. Simple as that.
function iIncreaseValues(howMuchToIncreaseIt){
return function thatIsHowIRoll(whatToIncrease) {
return whatToIncrease + howMuchToIncreaseIt;
};
}
Usage
var increaseDatByTwoPlease = iIncreaseValues(2);
var hereYouGoBoss = increaseDatByTwoPlease(4); // 6!!! WOW!
iIncreaseValues(2)(4); // also 6!!!
In this case, the simplest of tools open the biggest world of opportunities.
Array.prototype.forEach
Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.some
(homework ;))
These should practically be the lowest level you get with higher order functions.
Remember the for loop?
var interestingData = [/*...*/];
for(var i = 0, l = interestingData.length; i < l; i += 1){
var x1 = Math.sqrt(Math.exp(data[i]));
var x2 = Math.pow(Math.atan(data[i]), data[i] % 3);
printer.print(Math.pow(x1,x2));
}
How about...
var interestingData = /*...*/;
function compute(baseValue) {
var x1 = Math.sqrt(Math.exp(baseValue));
var x2 = Math.pow(Math.atan(baseValue), baseValue % 3);
return Math.pow(x1, x2);
}
interestingData.forEach(function(value){ printer.print(compute(value)); });
Not really mind-blowind, I know.
But it can be taken further:
function printComputationResult(value) { printer.print(compute(value)); }
And what this allows:
interestingData.forEach(printComputationResult); // I like this!
Now, to me, that's expressive. I think all top-level code should be like that.
It's basically a forEach
, but it returns the values.
var computedResults = interestingData.map(function(value) {
return compute(value);
});
function print(result) { printer.print(result); }
computedResults.forEach(print);
And that leads up to...
interestingData.map(compute).forEach(print);
It map
s the elements of an array to boolean values through the given predicate.
If the result is truthy, the element gets included in the result.
var freeDays = ["sat","sun"];
function isAWorkingDay(day) { return freeDays.indexOf(day) === -1; }
["mon","tue","wed","thu","fri","sat","su"].filter(isAWorkingDay);
// ["mon","tue","wed","thu","fri"]
Applies a function to an accumulator and each value of the array.
The return value is being taken as the accumulator for the next iteration.
function add(a,b) { return a + b; }
function sum(arr) { return arr.reduce(add); }
sum([1,3,5]); // 9
function sumVariadic() { return sum([].slice.call(arguments)); }
sumVariadic(1,3,5); // 9
Walking through the reduction process...
1) add(0, 1) { return 0 + 1; }
2) add(1, 3) { return 1 + 3; }
3) add(4, 5) { return 4 + 5; }
reduce
is the mother of the higher-order toolset functions. You can do pretty much everything presented before with it- and more.
These are the most basic blocks you can get.
Let's get on to more interesting stuff.
Remedies the need for having null
checks everywhere.
function maybe (fn) {
return function () {
var i;
if (arguments.length === 0) return;
else {
for (i = 0; i < arguments.length; ++i) {
if (arguments[i] == null || typeof(arguments[i]) === "undefined") return;
}
return fn.apply(this, arguments);
};
}
}
Makes your code more idiomatic, removing technical boilerplate.
function computeStupidly(a,b,c) {
if(a!== null && typeof(a) !== "undefined" &&
b!== null && typeof(b) !== "undefined" &&
c!== null && typeof(c) !== "undefined") {
console.log(a*Math.pow(b,c));
}
}
// it's much more concise with maybe
var computeWisely = maybe(function(a,b,c){
console.log(a*Math.pow(b,c));
});
Note that this version of maybe
will not preserve the function's arity!
var computeWisely = maybe(function(a,b,c){
console.log(a*Math.pow(b,c));
});
computeWisely(1,null,5); // undefined
computeWisely(1,5); // NaN :(
To remedy that, you'd have to use toString
on the function and extract the number of arguments from its definition.
Makes sure that a function is executed only once.
function once(fn) {
var executed = false;
return function() {
return executed ? void 0 : ((executed = true), fn.apply(this, arguments))
};
}
An example - quite straightforward
function magicComputator(value) {
return Math.pow(Math.exp(3/value), value * Math.sqrt(3));
}
var doNotDateToComputeTwice = once(magicComputator);
doNotDareToComputeTwice(3); // 180.57612295783412
doNotDareToComputeTwice(3); // undefined
There is a pitfall here- you have to use a named function.
once(function() { return "Uh"; }); // Uh.
once(function() { return "Uh"; }); // Uh.
The recipe can be easily modified to alleviate some of that pain by using the Function.name
property.
Unfortunately, this property is not even on a JS standards track, so it's not safe to use it.
Make instance methods chainable.
function fluent (methodBody) {
return function () {
methodBody.apply(this, arguments);
return this;
}
}
Obvious usage - relieve yourself of having to write return this;
all the time.
function Pigeon() {}
Pigeon.prototype = {
defecateOnPeople: fluent(function(){/* do something without returning `this` */}),
defecateSomewhereElse: fluent(function(){/* do something without returning `this` */})
};
new Pigeon()
.defecateOnPeople()
.defecateOnPeople()
.defecateOnPeople(); // they don't use the other method.
Transforms any function into a mapping, working on an array.
function mapWith (fn) {
return function (list) {
return Array.prototype.map.call(list, function (something) {
return fn.call(this, something);
});
};
};
Remember the Array.prototype.map
example?
function compute(baseValue) {
var x1 = Math.sqrt(Math.exp(baseValue));
var x2 = Math.pow(Math.atan(baseValue), baseValue % 3);
return Math.pow(x1, x2);
}
function print(value) { printer.print(value); }
interestingData.map(compute).forEach(print);
It could be written even more concisely now.
var computeAll = mapWith(compute);
computeAll(interestingData).forEach(print);
In practice, for me that is probably the most used 'toolset' function. It's insanely helpful.
These are just a few helpers. There is a lot more.
To get a better idea- I recommend @raganwald's excellent Javascript Allongé book.
He formalized thoughts from the book in the allong.es library.
Underscore/lodash also implement similar ideas, though with a little different approach and naming.
Speaking of Underscore...
Underscore provides a _.compose
method.
Function composition is another one of the simple concepts giving great power.
To compose functions means to connect them in a cascading chain, called in a specific order.
function a() { /*...*/ }
function b() { /*...*/ }
_.compose(a,b) === function () { return a(b()); };
Right to Left.
I talked about explicitly separating the two behavior types earlier.
Function composition provides an abstraction which allows explicitly stating how should certain behaviors be orchestrated.
I see it as the final piece to make the aforementioned approach feasible and practical.
With composition, every step of the process can be made idiomatic.
Small demo.
Summary
a list of things to look up :)
about me: