functional javascript
DESCRIPTION
Functional javascript presentation Ben Nolan showed at railsconf europe 2007.TRANSCRIPT
Functional Javascript
Ben Nolan
function(i){return i};
About me
• New Zealander living in Munich
• Blog at bennolan.com
• Developer of Behaviour.js and Groupswiki
• I promote functional-ish javascript
Target Audience
• Intermediate javascript developers that haven’t used prototypes enumerable functions.
• People that don’t quite follow .bind and anonymous functions(){}
Table of Contents
• Why Functional?
• Lambdas and binding
• Enumerable functions
• Examples
• Takeaways
Why Functional?
• Many small functions - Bugs are reduced by this methodology
• Code is more self-descriptive, promotes teamwork
• Testing is simplified
Why not functional?
• Code looks obtuse to new developers
return function(i){return this.calc(i)}.bind(this);
• Prototype’s functional extensions are slower than native loops. This is getting better in later versions of prototype.
Anonymous functions
• Also known as Lambdas
alert(function(){ return “hello”;}());
• Simply put - it’s a function that doesn’t have a name
Benefit of the Lambda
• Don’t have to pollute your scope with lots of function names.
function mysort(x,y){return x<=>y};ary.sort(mysort);
or
ary.sort(function(x,y){return x<>y});
Benefit of the Lambda
• Functions have their own scope.
while(true){ var x = 5; // not locally scoped}
function(){ var x = ‘a’; // locally scoped}
• Easier to make idempotent functions - no stomping on other variables
The bind Function
• Bind is required because Javascript doesn’t automatically call methods in the correct scope
• Bind returns an anonymous function that calls a method in the scope of the argument
• (It’s a form of currying)
Binding to a Banana
function x(){ alert(this);}
x(); // [object Window]
y = x.bind('Banana!')
y(); // [string Banana!]
Binding to “this”Wrong:
MyClass.prototype = { initialize : function(){ document.body.onclick = this.onclick(); }, onclick : function(){ this.doSomething(); }, doSomething : function(){ }}
Binding to “this”Wrong:
MyClass.prototype = { initialize : function(){ document.body.onclick = this.onclick; }, onclick : function(){ this.doSomething(); }, doSomething : function(){ }}
Binding to “this”Right:
MyClass.prototype = { initialize : function(){ document.body.onclick = this.onclick.bind(this); }, onclick : function(){ this.doSomething(); }, doSomething : function(){ }}
Some idioms
• Smaller functions are better.
Less code means it’s easier to inspect and easier to test. Use lots of small functions.
Some idioms
• Idempotent functions are the ideal.
ie Functions that don’t alter the dom or the properties of a object - they just return a value.
Some idioms
• Pragmatism not idealism.
Start with dirty big functions - refactor to smaller nicer functions as you can.
The Enumerables
• Prototype introduces ruby-ish enumerable functions to javascript.
• Convert a javascript array to an enumerable array with the $A() function.
$A([1,2,3]);
Enumerable Example
• Old-style javascript:
function getInsidesOf(ary){ var outp = new Array; for (i in ary){outp.push(i.innerHTML);} return outp;}
(nb: this code alters the variable i in the global namespace - an easy mistake to make)
Enumerable Example
• Using functional Javascript:
function getInsidesOf(ary){ return $A(ary).pluck(‘innerHTML’);}
Cool enum. methods
• Invoke(methodName)invokes methodName on each elements in the array
• Pluck(propertyName)returns an array of the property gathered from each element in the array
• inGroupsOf(integer)eg: [1,2,3,4,5] -> [[1,2],[3,4],[5]]
Enums and lambdas
• The most powerful enum methods require a function as an argument.
• You can sort, filter and order with these tools.
Lambdas and Enum.
• Simplest example:
return $A([1,2,3,4,5]).map( function(i){ return i+1; }});
// Returns [2,3,4,5,6]
Lambdas and Enum.
• Simplest example:
return $A([1,2,3,4,5]).map( function(i){ return i+1; }});
// Returns [2,3,4,5,6]
Create anarray with
enum extensions
Lambdas and Enum.
• Simplest example:
return $A([1,2,3,4,5]).map( function(i){ return i+1; }});
// Returns [2,3,4,5,6]
Anonymousfunction
Filtering Elements
• Get an array of selected checkboxes:
$$(‘input[type=checkbox]’).select(function(el){ return el.selected;});
Filtering Elements
• $$(‘input[type=checkbox]’).select(function(el){ return el.selected;});
Get an arrayof input tags
Filtering Elements
• $$(‘input[type=checkbox]’).select(function(el){ return el.selected;});
Call theselect
function
Filtering Elements
• $$(‘input[type=checkbox]’).select(function(el){ return el.selected;});
Pass a function as an
argument
Updating Elements
• Empty the innerHTML of all A elements:
$$(‘a’).each(function(el){ el.update(‘’);});
How I structure apps
• Use OO-style classes (class.create and object.extend)
• Try and use a minimum of private properties in my classes - introspect the dom instead
• Try and use idempotent functions
Example code
• I have a rich-text-editor written with Prototype.
• Browsers use different markup
IE Safari Mozilla
strong <span style=..> em
Example code
• Convert bolded spans to <b> tags
$$("span").each(function(el){ if(el.getStyle('font-weight')=='bold'){ el.convertTo(‘B’); }});
Example code
• Find an element which has the style: “display:block”
return this.getAncestors().find(function(el){ return el.getStyle('display') == 'block';});
Take aways
• Use many small functions that are idempotent
• Use enumerable functions
• Use anonymous functions liberally
• See prototypejs.org for more information