We'll talk about a subset of JavaScript's functions semantics
and some idioms and patterns based on it.
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
- Semantics
What do the various language features mean?
- Idioms
What are the common approaches to using the language features to express computations?
- The reserved word function.
- The function name.
It's optional. If no name is given, the function is said to be anonymous.- The set of parameters of the function, wrapped in parentheses.
- The body of the function: a set of statements wrapped in curly braces.
(function() {})
(function(a,b) {return a + b;})
(function() {}) === (function() {});
such as, length, arguments, prototype,
apply, call, etc.
- 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.
(function (radius) { return 2 * radius;})(1 + 1 * 5);
(function (x) { return x; })(1 + 1);
- JS parses this whole thing as an expression made up of two sub-expressions.
- It then starts evaluating each of the sub-expressions.
- One of the sub-expressions, function (x) { return x; }, evaluates to a function.
- The other sub-expression, 1 + 1, evaluates to 2.
- JS now evaluates the whole expression by applying the evaluated function to the argument value.
- 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(radius) {
var calcDiameter = function(r) {
return 2 * r;
};
return calcDiameter(radius);
})(2);
(function() {
var foo = 'foo',
bar = 'bar';
return foo + bar;
})();
(function (x) {
return function(y) {
return x * y;
}
})(3)(4)
var h = 5;
(function(x) {
return function(y) {
return function(z) {
return x + y + z + h;
}
}
})(2)(3)(4);
See example in environmentChain.js
When we are looking for the variable bound to a name.
When we are looking for the value of a variable bound to a name.
function(x) {
return function(x, y) {
return function(w, z) {
return function(w) {
return x + y + z + w;
}
}
}
}(1000)(2, 3)(4000000000, 5)(6)
See example in shadowing.js
var x = 'outer';
(function() {
console.log(x);
if (true) {
var x = 'inner';
console.log(x)
}
})();
Having just one var with the variables separated by commas also helps the minification process.
See example in codePatternUsingVar.js
See examples in reassignmentAndMutation_1.js and reassignmentAndMutation_2.js
Function
+
Environment where the function was defined
var counter = (function() {
var value = 0;
return function() {
return value++
}
})();
function() {
//private state
//private functions
return {
//public state
//public functions
};
}
var bindingName = function actualName () {
};
> bindingName.name
'actualName'
var fn = function even(n) {
if (n === 0) {
return true;
} else {
return !even(n - 1);
}
}
function name () {
}
var name = function name () {
};
var composedFunction = function () {
doSomething();
doSometringElse();
function doSomething () { console.log("something is done"); };
function doSometringElse () { console.log("something else is done"); };
};
// 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]
}
}
- this
- arguments
- 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).
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.
var times = function(n, fn) {
var i;
for (i = 0; i < n; i++) {
fn();
}
}
times(3, function() {
console.log('hola koko');
});
See Strategy pattern and higher-order functions? post
See verbose_strategy.js
See takingFunctionsAsArguments-duck.js
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 currying.js and currying-builders.js
function not(fn) {
return function(argument) {
return !fn(argument)
}
}
May be in another talk some day.