Functional Programming
with
Javascript

Content

  1. History of Javascript
  2. Language Facts
  3. First-class Functions
  4. High-order Functions
  5. Closures
  6. What's this all about?

Quotes

JavaScript is the only language that I’m aware of that people feel they don’t need to learn before they start using it.
- Douglas Crockford
Java is to JavaScript what Car is to Carpet. ”
- Chris Heilman
JavaScript is the first lambda language to go mainstream. Deep down, JavaScript has more in common with Lisp and Scheme than with Java. It is Lisp in C's clothing. This makes JavaScript a remarkably powerful language.
- Douglas Crockford

History

Facts

First-class functions

Javascript: Definition of functions

    
// function definition
function hello() {
    console.log("Hello word!");
};
// function expression
var hello2 = function () {
    console.log("Hello world!");
};
//functions as property in object
var hello_object = {hello: function () {console.log("Hello World!");}};

// function constructor
var hello3 = new Function("hello3", "console.log(\"Hello World\")");
    
  

Javascript: Invocation of functions

    
// call functions
hello();
hello2();
hello_object.hello();
hello3();
(function () {
    console.log("Hello world!");  // call an anonymous function
})();
    
  

call() and apply()

    
hello.call();
hello2.apply();

var add = function (a ,b) {
    return a + b;
};
// call, apply with arguments
console.log("add invoked by apply: " + add.apply(null, [1,2]));
console.log("add invoked by call: " + add.call(null, 4, 6));
    
  

High-order functions

    
// example in Java
Collections.sort(stringCollection, new Comparator() {
        public int compare(String s1, String s2) {
            return s1.length() - s2.length();
        }
    });
    
  

Classic high-order functions

  1. map()
  2. reduce()
  3. filter()

Image-Source: techcrunch.com, dme.rwth-aachen.de

map(), reduce() implementation

    
function map(fn, arr) {
    var result = [];
    for (var i = 0; i < arr.length; i++) {
        result.push(fn(arr[i]));
    }
    return result;
}

function reduce(fn, initial, arr) {
    var result = initial;
    for (var i = 0; i < arr.length; i++) {
        result = fn(result, arr[i]);
    }
    return result;
}

// map-reduce
var sum_of_squares = reduce(function(a, b) {return a+b;}, 0,
                            map(function (x) {return x * x}, [1,2,3,4,5,6,7]));
console.log(sum_of_squares);  // 140
    
  

Monkey patching

    
// monkey patching
Array.prototype.mymap = function (fn) {
    var result = [];
    for (var i = 0; i < this.length; i++) {
        result.push(fn(this[i]));
    }
    return result;
};
var squaredArray = [1,2,3,4,5,6,7].mymap(function (x) {return x * x});
console.log(squaredArray);
    
  

Closures

    
// simple closure
var makeAdder = function (n) {
    // n closes over returned function
    return function (x) {
        return n + x;
    };
};

var add5 = makeAdder(5);
console.log(add5(20));      // 25
    
  

What's this all about?

Namespaces

  
// namespacing
var myNamespace = {}

// don't pollute global namespace
myNamespace.hello = function(name) {
    console.log("hello" + name);
};

myNamespace.goodbye = function() {
    console.log("goodbye....");
};

myNamespace.hello("Daniel");
myNamespace.goodbye();
  

Module pattern

    
// module pattern ==> information hiding, encapsulation
var counterObj  = (function () {
    var my = {};
    var counter = 0;  // private
    var hello = function () {  //private
        return "hello world!";
    };
    // public methods
    my.inc = function () {
        return ++counter;
    };
    my.reset = function () {
        counter = 0; return counter;
    };
    my.say = function () {
        return "Invoke private method:" + hello();
    };
    return my;
})();  // invoke anonymous function
console.log(counterObj.inc());
console.log(counterObj.hello());  // no method hello
    
  

jQuery a big module...

    
(function() {
    var jQuery = window.$ = function() {
        // initialize
    };
    
    // private objects and functions

})();  // call anonymous function immediately
    
  

Callback functions

    
// callback functions
$(document).ready(function(){
  $("#b1").click(function(){
      $("#p1").toggleClass('highlight');
  });
});
    
  

"this" demystified

    
// "this" demystified
var foo = function() {
    return this;
};

console.log(foo());  // window object

var obj1 = {
    name : "obj1",
    obj_foo : foo
};

console.log(obj1.obj_foo());  // obj1

var obj2 = {
    name : "obj2",
    obj_foo : foo
};

console.log(obj2.obj_foo());  // obj2
    
  

Binding function context

with closures and high-order functions

  
//  <button id="test1">Click Me!</button>

// binding function contexts
var bind = function(context, name) {
    return function() {
        return context[name].apply(context, arguments);
    };
};

var button = {
    clicked: false,

    click: function () {
        this.clicked = true;
        console.log("clicked should be true: " + button.clicked);
    }
};

$(function() {
    var elem = document.getElementById("test1");
    elem.addEventListener("click", button.click, false);  // fail
    elem.addEventListener("click", bind(button, "click"), false);  // ok
})
  

Binding function context

jQuery version

  
//  <button id="test1">Click Me!</button>

// binding function contexts
var button = {
    clicked: false,

    click: function () {
        this.clicked = true;
        console.log("clicked should be true: " + button.clicked);
    }
};
// this is the button element NOT the button variable;
$(function() {
    $("#test1").click(button.click)  // fail
    $("#test1").click($.proxy(button.click, button))  // ok
})
  

Memoization

    
// fibonacci
// 1, 1, 2, 3, 5, 8, 13, 21, 34,.....
var fib = function (n) {
    if (n <= 2) {
        return 1;
    } else {
        return fib(n-1) + fib(n-2);
    }
};
   
  

Memoization

    
var memoize = function (fn) {
    var cache = {};

    return function () {
        var args = Array.prototype.slice.call(arguments);
        var args_string = JSON.stringify(args);
        if (cache[args_string] !== undefined) {
            return cache[args_string];
        } else {
            cache[args_string] = fn.apply(null, args);
            return cache[args_string];
        }
    };
};
    
  

Measure performance

  
var time = function (fn) {
    var start = new Date().getTime();
    var args = Array.prototype.slice.call(arguments, 1);
    fn.apply(null, args);
    console.log(new Date().getTime() - start + "ms");
};

//time uncached fibonacci
time(fib, 41);

fib = memoize(fib);
// time cached fibonacci
time(fib, 41);
time(fib, 42);
  

References

  1. Douglas Crockford: JavaScript - the good parts
  2. David Flanagan: JavaScript - The Definitive Guide (6. ed.)
  3. John Resig and Bear Bibeault: Secrets of the JavaScript Ninja
  4. Mozilla Javascript Guide, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide

/

#