Eloquent JavaScript - Chapter 3 - Functions (Notes)

functions

Eloquent JavaScript(The Annotated Version) - Chapter 3 - Functions

Defining a function

var square = function(x) {
return x * x;
};

console.log(square(12));
// → 144

Functions starts with keyword function, has a set of parameters and a body which contains the statements that are to be executed.

The body wrapped in braces.

A function can have multiple parameters or no parameters at all.

A return statement determines the value the function returns.

The return keyword without an expression after it will cause the function to return undefined.

Return statement - Douglas Crockford - The JavaScript Programming Language (Notes)

In JavaScript, every function will return a result, no void type in js.

  1. If there is no expression, then the return value is undefined.

  2. Except for constructors, whose default return value is this.

Parameters and scopes

The parameters to a function behave like regular variables, but their initial values are given by the caller of the function, not the code in the function itself.

An important property of functions is that the variables created inside of them, including their parameters, are local to the function.

This “localness” of variables applies only to the parameters and to variables declared with the var keyword inside the function body.

Variables declared outside of any function are called global, because they are visible throughout the program.

var x = "outside";

var f1 = function() {
// The use of var here is extremely important.
// If you declare a variable in a function using var,
// The variable will only be visible inside the function.
// In other words it’s ‘local’ to the function.
var x = "inside f1";
};
f1();
// Since we’re not inside the function anymore,
// if we print x, we’ll get “outside”.
console.log(x);
// → outside

var f2 = function() {
// We didn’t use var here, so the line below
// will actually grab the x that we declared in the
// first line of our program and set it equal to “inside f2”
// instead of its initial value, “outside”.
x = "inside f2";
};
f2();
// Since f2() changed the x outside of the function,
// printing x outside the function will give us “inside f2”.
console.log(x);
// → inside f2

Nested scope

In short, each local scope can also see all the local scopes that contain it.

The set of variables visible inside a function is determined by the place of that function in the program text.

All variables from blocks around a function’s definition are visible—meaning both those in function bodies that enclose it and those at the top level of the program.

This approach to variable visibility is called lexical scoping.

In JavaScript, functions are the only things that create a new scope not braces.

var something = 1;
{
var something = 2;
// Do stuff with variable something...
}
// Outside of the block again...

But the something inside the block refers to the same variable as the one outside the block.

The next version of JavaScript will introduce a let keyword, which works like var but creates a variable that is local to the enclosing block, not the enclosing function.

Functions as values

Function variables usually simply act as names for a specific piece of the program.
Such a variable is defined once and never changed.
This makes it easy to start confusing the function and its name.

var func = function() {};

function variable: Left side of the equation.
function value: Right side of the equation.

Declaration notation

There is a slightly shorter way to say “var square = function…”. The function keyword can also be used at the start of a statement, as in the following:

function square(x) {
return x * x;
}

This is a function declaration.

There is one subtlety with this form of function definition, however.

console.log("The future says:", future());

function future() {
return "We STILL have no flying cars.";
}

This code works, even though the function is defined below the code that uses it.

This is because function declarations are not part of the regular top-to-bottom flow of control.

They are conceptually moved to the top of their scope and can be used by all the code in that scope.

This is sometimes useful because it gives us the freedom to order code in a way that seems meaningful, without worrying about having to define all functions above their first use.

Do not define functions inside of control flow.

function example() {
function a() {} // Okay
if (something) {
function b() {} // Danger!
}
}

The call stack

Because a function has to jump back to the place of the call when it returns, the computer must remember the context from which the function was called.

In one case, console.log has to jump back to the greet function.

In the other case, it jumps back to the end of the program.

The place where the computer stores this context is the call stack.

Every time a function is called, the current context is put on top of this “stack”.

When the function returns, it removes the top context from the stack and uses it to continue execution.

If you want a more detailed explanation about call stacks and how JavaScript interprets code, this blog post is very good:
http://davidshariff.com/blog/what-is-the-execution-context-in-JavaScript/

Optional Arguments

JavaScript is extremely broad-minded about the number of arguments you pass to a function.

If you pass too many, the extra ones are ignored.

If you pass too few, the missing parameters simply get assigned the value undefined.

The downside of this is that it is possible—likely, even—that you’ll accidentally pass the wrong number of arguments to functions and no one will tell you about it.

The upside is that this behavior can be used to have a function take “optional” arguments.

Closure

function wrapValue(n) {
var localVariable = n;
return function() { return localVariable; };
}

var wrap1 = wrapValue(1);
var wrap2 = wrapValue(2);
console.log(wrap1());
// → 1
console.log(wrap2());
// → 2

In fact, multiple instances of the variable can be alive at the same time, which is another good illustration of the concept that local variables really are re-created for every call—different calls can’t trample on one another’s local variables.

This feature—being able to reference a specific instance of local variables in an enclosing function—is called closure. A function that “closes over” some local variables is called a closure. This behavior not only frees you from having to worry about lifetimes of variables but also allows for some creative use of function values.

Recursion

It is perfectly okay for a function to call itself, as long as it takes care not to overflow the stack. A function that calls itself is called recursive.

Growing functions

There are two more or less natural ways for functions to be introduced into programs.

  1. The first is that you find yourself writing very similar code multiple times. We want to avoid doing that since having more code means more space for mistakes to hide and more material to read for people trying to understand the program. So we take the repeated functionality, find a good name for it, and put it into a function.

  2. The second way is that you find you need some functionality that you haven’t written yet and that sounds like it deserves its own function. You’ll start by naming the function, and you’ll then write its body. You might even start writing code that uses the function before you actually define the function itself.

How difficult it is to find a good name for a function is a good indication of how clear a concept it is that you’re trying to wrap.

Functions and side effects

Functions can be roughly divided into those that are called for their side effects and those that are called for their return value. (Though it is definitely also possible to have both side effects and return a value.)

A pure function is a specific kind of value-producing function that not only has no side effects but also doesn’t rely on side effects from other code—for example, it doesn’t read global variables that are occasionally changed by other code.

A pure function has the pleasant property that, when called with the same arguments, it always produces the same value (and doesn’t do anything else).

This makes it easy to reason about.

A call to such a function can be mentally substituted by its result, without changing the meaning of the code.

When you are not sure that a pure function is working correctly, you can test it by simply calling it, and know that if it works in that context, it will work in any context.

Nonpure functions might return different values based on all kinds of factors and have side effects that might be hard to test and think about.

Exercises

Minimum

function min(a, b) {
if (a < b)
return a;
else
return b;
}

Recursion

 function isEven(n) {
// Base case 1, n is even.
if (n == 0)
return true;
// Base case 2, n is odd.
else if (n == 1)
return false;
// Handle negative numbers by just
// making them positive.
else if (n < 0)
return isEven(-n);
// Recursive case.
else
return isEven(n - 2);
}

Bean counting

function countBs(sampleString) {
var counted = 0;
for (var i = 0; i < sampleString.length; i++)
if (sampleString.charAt(i) == 'B')
counted += 1;
return counted;
}

function countChar(sampleString, searchForChar) {
var counted = 0;
for (var i = 0; i < sampleString.length; i++ )
if (sampleString.charAt(i) == searchForChar)
counted += 1;
return counted;
}

function countBs(sampleString) {
return countChar(sampleString, "B");
}