A bit about JavaScript functions

Disclaimer

We'll talk about a subset of JavaScript's functions semantics
  and some idioms and patterns based on it.


This talk focuses in introducing the following concepts:
  functions as first class entities, closures and higher-order functions.


It's based on some of the readings I've done  to learn JavaScript:

JavaScript Enlightenment, Cody Lindley

JavaScript: The Good Parts, Douglas Crockford

JavaScript Allongé, Reginald Braithwaite

You Don't Know JS: Scope & Closures, Kyle Simpson


Most of the examples were taken from Reginald Braithwaite's wonderful book.

Semantics & Idioms


  • Semantics

What do the various language features mean?



  • Idioms

What are the common approaches to using the language features to express computations?






A function represents a computation to be performed


Parts of a function


  1. The reserved word function.
  2. The function name.
    It's optional.  If no name is given, the function is said to be anonymous.
  3. The set of parameters of the function, wrapped in parentheses.
  4. The body of the function: a set of statements wrapped in curly braces.
    
(function() {})         (function(a,b) {return a + b;})

JavaScript functions are objects


As such they are a reference type,

(as opposed to a primitive or value type).


 (function() {}) === (function() {});

JavaScript functions are objects


So they have properties and methods:

such as, length, arguments, prototype,

apply, call, etc.

Functions are first class entities


Functions are values that can: 

be bound to names like any other value, 

passed as arguments,

returned from other functions,

and used wherever any other value can be used.

Applying a function


We use functions applying them to zero or more values called arguments.


Let fn_expr be an expression that when evaluated produces a function and let args be the arguments.


This is how the function is applied:

fn_expr(args)




Some definitions

Environment

  • Every time a function is invoked a new environment is created.


  • We can think of this environment as a hash that associates by name the variables in the scope to their values.


  • When a variable is associated by name to a value is said that the variable is bound to the value. Therefore an environment is a set of bindings.


  • When you apply a function to its arguments, an entry is placed in the dictionary for each argument.

Passing arguments

  • When any value is passed as an argument to a function, the value that gets bound in the function's environment must be identical to the original one.

Call by value


For primitive types:

JS places a copy of the value in the environment.

   

This is called call by value semantics.

Call by sharing


    For reference types:

 JS places a reference to the reference type in the environment.


    When the value needs to be used,

JS uses the reference in the environment

    to retrieve the original.


    This is called call by sharing semantics.

Parameters eager evaluation

  It's an evaluation strategy.


    JS will first evaluate

the expressions of the parameters

and then apply the function to

the resulting parameter values.


    The alternative strategy is lazy evaluation.

        
 (function (radius) { return 2 * radius;})(1 + 1 * 5);
    




How does invoking a function work?

Invoking a function (I)

    
 (function (x) { return x; })(1 + 1);
  1. JS parses this whole thing as an expression made up of two sub-expressions.
  2. It then starts evaluating each of the sub-expressions.
  3. One of the sub-expressions, function (x) { return x; }, evaluates to a function.
  4. The other sub-expression, 1 + 1, evaluates to 2.
  5. JS now evaluates the whole expression by applying the evaluated function to the argument value.

Invoking a function (II)


  • An environment is created -> {'..': {}}.
  • The value 2 is bound to the name x in the environment -> {x: 2, '..': {}}.
  • The expression x in return x; is evaluated within the environment we just created.
  • The value of a variable like x when evaluated in an environment is the value bound to the variable's name in that environment, which is 2.
  • And that's the value that the function returns (2).

Function Scope


  Pre-ES6 JS everything has function scope*.


  * Only exception is the catch clause which has block scope since ES3.

Creating bindings using var

  • The var keyword allows us to introduce one or more bindings in the current function's environment.
  • Any expression can be bound, even functions, (no surprise here, as they are first-class entities).
    (function(radius) {                                        
    var calcDiameter = function(r) {
    return 2 * r;
    };
    return calcDiameter(radius);
    })(2);
  • You can bind more than one name-value pair by separating them by commas
    (function() {
    var foo = 'foo',
    bar = 'bar';
    return foo + bar;
    })();

Nested functions:

How do they work?

(function (x) {
return function(y) {
return x * y;
}
})(3)(4)

Free variables


A free variable is one that is not bound within the function.


x was a free variable of the inner function in the previous example.


  But how come x was equal to 3?




Lexical scope


  • The body of a function is evaluated in
      the environment where the function was defined, not the environment where the function is being called.


  • Also known as static scope.


  • The alternative semantics is dynamic scope.

Environments chain

  • Whenever a function is applied to some arguments, the newly created environment always has a reference to its parent environment.
var h = 5;

(function(x) {
return function(y) {
return function(z) {
return x + y + z + h;
}
}
})(2)(3)(4);
  • JS always searches for a binding starting with the functions own environment and then each parent in turn until it finds one.
  • Also known as scope chain.
  • The last stop in the scope chain is the global environment.

See example in environmentChain.js

Types of lookup in the scope chain.


  • LHS lookup.

When we are looking for the variable bound to a name.


  • RHS lookup.

When we are looking for the value of a variable bound to a name.

What if the binding is not there?


  • LHS lookup.
    1. If not using strict mode
      A new binding is created in the global environment.

    2. If using strict mode
      A ReferenceError is thrown.

  • RHS lookup.
    A ReferenceError is thrown.

Shadowing


  • When a variable has the same name as an ancestor environment's binding, it is said to shadow the ancestor.
    function(x) {                
    return function(x, y) {
    return function(w, z) {
    return function(w) {
    return x + y + z + w;
    }
    }
    }
    }(1000)(2, 3)(4000000000, 5)(6)
  • If done on purpose, it's often a good thing.

  • If not, you're programming by accident and that can always gets dangerous.


See example in shadowing.js

Hoisting


var x = 'outer';
        
(function() {
console.log(x);
if (true) {
var x = 'inner';
console.log(x)
}
})();

What gets printed on the console?

See example in hoisting.js

Code pattern using var


Declare all your variables at the beginning

of the function using var,

to make your code easier to understand.


Having just one var with the variables separated by commas also helps the minification process.

   

     See example in codePatternUsingVar.js

Reassignment, mutation, aliasing


  • JS allows you to re-assign the value of variables..


  • Actually what we are doing is rebinding a different value to the same name in the same environment.


  • Reference type values can mutate.
    Their identities stay the same, but not their structure/contents..


  • JS’s semantics allow for two different bindings to refer to the same value, they are aliases for the same value.


See examples in reassignmentAndMutation_1.js and reassignmentAndMutation_2.js

Closures


We've been seeing them for a while*.


They are just functions with free variables.


*See Nested Functions.

Closures


To be executed a closure needs:

Function

+

Environment where the function was defined


See example in closures.js

Some idioms using closures:

Private state


var counter = (function() {
var value = 0;

return function() {
return value++
}
})();

   See example in privateState.js

Some idioms using closures:

Module pattern



function() {
//private state
//private functions
return {
//public state
//public functions
};
}

   See example in modulePatternExample.js

Named functions


var bindingName = function actualName () {

};

> bindingName.name
'actualName'


Benefits of using named functions


  • Easier to understand.
    Good names help us express the function intent which improves readability.

  • Easier to debug.
    It gets easier because the names of the functions are displayed in stack traces.

  • Recursion.
    A named function can refer to itself without having to use the deprecated arguments.calle reference.

Recursion


var fn = function even(n) {
if (n === 0) {
return true;
} else { return !even(n - 1); }
}


Function Declaration (I)


function name () {
}

It's more or less like doing:
var name = function name () {

};

The difference is that the function binding is hoisted to the top of the enclosing scope.

Function Declaration (II)


  • It facilitates a certain style of programming where you put the main logic at the top, and the helper functions at the bottom:
    var composedFunction = function () {
doSomething(); doSometringElse(); function doSomething () { console.log("something is done"); }; function doSometringElse () { console.log("something else is done"); };
};

See an example in composedFunctions.js

Block Scope in ES6

// Pre ES6
(function(x, y) {
var gamma = 3;
if (x > y) {
var gamma = 1 + y;
console.log(gamma * x);
}
console.log(gamma);
})(2, 1);
// ES6 example executed in Chrome using ScratchJs
(function(x, y) {
var gamma = 3;
if (x > y) {
let gamma = 1 + y;
console.log(gamma * x);
}
console.log(gamma);
})(2, 1);
// Another ES6 let example executed in Chrome using ScratchJs
var introductions = [], names = ['Karl', 'Friedrich', 'Gauss'];
for (let i = 0; i < 3; i++) {
introductions[i] = function(soAndSo) {
return "Hello, " + soAndSo + ", my name is " + names[i]
}
}


Block Scope polyfill for pre-ES6


  • You can polyfill block scope in pre-ES6 using Traceur.

  • Traceur is a transpiler of ES6 features into pre-ES6 (mostly ES5, but not all!) for general usage.

  • It's actually what ScratchJs is using behind the scenes.

Dynamic Scope


  • this


  • arguments

Recap.

  • JS functions are first class entities (in fact, they are objects).
This means that they can be computed, passed, stored, etc, wherever other values can be computed, passed, stored, etc.
  • Function scope
  • Everything follows lexical scope semantics except this and arguments which follow dynamic scope.

  • There is a scope chain (environment chain).




Higher-order functions

Higher-order functions


Functions that take or/and return other functions.


They are a powerful mean of abstraction,
  because they can abstract both control flow and
  algorithms that use other algorithms.





Passing functions as arguments

Passing functions as arguments

var times = function(n, fn) {
var i;
for (i = 0; i < n; i++) {
fn();
}
}

times(3, function() {
console.log('hola koko');
});










It's all about composition


Strategy pattern example

   

    See Strategy pattern and higher-order functions? post

    See verbose_strategy.js
    See takingFunctionsAsArguments-duck.js

"Like many patterns, using it when it applies is only 20% of the benefit. 

  The other 80% comes from organizing your code
  such that you can use it:

Writing functions that can be composed in various ways"


Reginald Braithwaite

Applicative programming


  • Pattern of defining a function that takes a function and then invokes that function for each element in a collection.


  • This pattern is very important because it splits the traversal of the data structure from the processing done on its elements.


  • These higher-order functions abstract typical flows.

Applicative programming


  • Most known examples: map, filter, reduce.


  • But there are many more.




Creating functions

Basic strategies


  1. Compose
  2. Partial application
  3. Currying
  4. Home-made

Composition

var cook = function(food) {
console.log("Cook " + food + "!");
return food;
};

var eat = function(food) {
console.log("Eat " + food + "!");
};

var cookAndEat = function(food) {
return eat(cook(food));
};

var compose = function(f, g) {
return function(c) {
return f(g(c))
}
};

var cookAndEat = compose(eat, cook);

   See example in composition.js

Partial application


It refers to the process of fixing

a number of arguments of a function,

to produce a new function

with a smaller number of arguments.



   See partialApplication.js

Currying


The process of taking a function that

takes multiple arguments

and transform it into

a chain of single argument functions.


  See currying.js and currying-builders.js

Decorators


A function decorator is a higher-order function

that takes a  function as an argument

and returns another function

which is a variation of the function

passed as an argument.


function not(fn) { 
return function(argument) {
return !fn(argument)
}
}

Adapters


A function adapter is a higher-order function

that takes a function as an argument

and returns a new function

that adapts the function passed as an argument

to a different signature.


        See example in adapter.js

There's much more!


Important things we didn't see in this talk:


    • this
    • Functions prototype property
    • Prototype chain
    • Functions as constructors
    • Functions apply and call methods


May be in another talk some day.

Thanks!


@trikitrok


http://garajeando.blogspot.com.es/

ABitOfJavaScriptFunctions

By Manuel Rivero

ABitOfJavaScriptFunctions

  • 2,205