Text Practice Mode
javaSriptcode
created May 21st 2015, 22:59 by BogdanLevantovych
2
1384 words
3 completed
0
Rating visible after 3 or more votes
00:00
This behavior helps prevent accidental interference between functions. If all variables were shared by the whole program, it’d take a lot of effort to make sure no name is ever used for two different purposes. And if you did reuse a variable name, you might see strange effects from unrelated code messing with the value of your variable. By treating function-local variables as existing only within the function, the language makes it possible to read and understand functions as small universes, without having to worry about all the code at once.
JavaScript distinguishes not just between global and local variables. Functions can be created inside other functions, producing several degrees of locality.
For example, this rather nonsensical function has two functions inside of it:
The flat and mountain functions can “see” the variable called result, since they are inside the function that defines it. But they cannot see each other’s count variables since they are outside each other’s scope. The environment outside of the landscape function doesn’t see any of the variables defined inside landscape.
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.
People who have experience with other programming languages might expect that any block of code between braces produces a new local environment. But in JavaScript, functions are the only things that create a new scope. You are allowed to use free-standing blocks.
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. In fact, although blocks like this are allowed, they are useful only to group the body of an if statement or a loop.
If you find this odd, you’re not alone. 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.
But the two are different. A function value can do all the things that other values can do—you can use it in arbitrary expressions, not just call it. It is possible to store a function value in a new place, pass it as an argument to a function, and so on. Similarly, a variable that holds a function is still just a regular variable and can be assigned a new value, like so:
var launchMissiles = function(value) {
missileSystem.launch("now");
};
if (safeMode)
launchMissiles = function(value) {/* do nothing */};
In Chapter 5, we will discuss the wonderful things that can be done by passing around function values to other functions.
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. The statement defines the variable square and points it at the given function. So far so good. 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.
What happens when you put such a function definition inside a conditional (if) block or a loop? Well, don’t do that. Different JavaScript platforms in different browsers have traditionally done different things in that situation, and the latest standard actually forbids it. If you want your programs to behave consistently, only use this form of function-defining statements in the outermost block of a function or program.
function example() {
function a() {} // Okay
if (something) {
function b() {} // Danger!
}
}
The call stack
It will be helpful to take a closer look at the way control flows through functions. Here is a simple program that makes a few function calls:
function greet(who) {
console.log("Hello " + who);
}
greet("Harry");
console.log("Bye");
A run through this program goes roughly like this: the call to greet causes control to jump to the start of that function (line 2). It calls console.log (a built-in browser function), which takes control, does its job, and then returns control to line 2. Then it reaches the end of the greet function, so it returns to the place that called it, at line 4. The line after that calls console.log again.
We could show the flow of control schematically like this:
top
greet
console.log
greet
top
console.log
top
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.
Storing this stack requires space in the computer’s memory. When the stack grows too big, the computer will fail with a message like “out of stack space” or “too much recursion”. The following code illustrates this by asking the computer a really hard question, which causes an infinite back-and-forth between two functions. Rather, it would be infinite, if the computer had an infinite stack. As it is, we will run out of space, or “blow the stack”.
function chicken() {
return egg();
}
function egg() {
return chicken();
}
console.log(chicken() + " came first.");
// → ??
Optional Arguments
The following code is allowed and executes without any problem:
alert("Hello", "Good Evening", "How do you do?");
The function alert officially accepts only one argument. Yet when you call it like this, it doesn’t complain. It simply ignores the other arguments and shows you “Hello”.
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. For example, the following version of power can be called either with two arguments or with a single argument, in which case the exponent is assumed to be two, and the function behaves like square.
function power(base, exponent) {
if (exponent == undefined)
exponent = 2;
var result = 1;
for (var count = 0; count < exponent; count++)
result *= base;
return result;
}
console.log(power(4));
// → 16
console.log(power(4, 3));
// → 64
In the next chapter, we will see a way in which a function body can get at the exact list of arguments that were passed. This is helpful because it makes it possible for a function to accept any number of arguments. For example, console.log makes use of this—it outputs all of the values it is given.
console.log("R", 2, "D", 2);
// → R 2 D 2
JavaScript distinguishes not just between global and local variables. Functions can be created inside other functions, producing several degrees of locality.
For example, this rather nonsensical function has two functions inside of it:
The flat and mountain functions can “see” the variable called result, since they are inside the function that defines it. But they cannot see each other’s count variables since they are outside each other’s scope. The environment outside of the landscape function doesn’t see any of the variables defined inside landscape.
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.
People who have experience with other programming languages might expect that any block of code between braces produces a new local environment. But in JavaScript, functions are the only things that create a new scope. You are allowed to use free-standing blocks.
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. In fact, although blocks like this are allowed, they are useful only to group the body of an if statement or a loop.
If you find this odd, you’re not alone. 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.
But the two are different. A function value can do all the things that other values can do—you can use it in arbitrary expressions, not just call it. It is possible to store a function value in a new place, pass it as an argument to a function, and so on. Similarly, a variable that holds a function is still just a regular variable and can be assigned a new value, like so:
var launchMissiles = function(value) {
missileSystem.launch("now");
};
if (safeMode)
launchMissiles = function(value) {/* do nothing */};
In Chapter 5, we will discuss the wonderful things that can be done by passing around function values to other functions.
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. The statement defines the variable square and points it at the given function. So far so good. 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.
What happens when you put such a function definition inside a conditional (if) block or a loop? Well, don’t do that. Different JavaScript platforms in different browsers have traditionally done different things in that situation, and the latest standard actually forbids it. If you want your programs to behave consistently, only use this form of function-defining statements in the outermost block of a function or program.
function example() {
function a() {} // Okay
if (something) {
function b() {} // Danger!
}
}
The call stack
It will be helpful to take a closer look at the way control flows through functions. Here is a simple program that makes a few function calls:
function greet(who) {
console.log("Hello " + who);
}
greet("Harry");
console.log("Bye");
A run through this program goes roughly like this: the call to greet causes control to jump to the start of that function (line 2). It calls console.log (a built-in browser function), which takes control, does its job, and then returns control to line 2. Then it reaches the end of the greet function, so it returns to the place that called it, at line 4. The line after that calls console.log again.
We could show the flow of control schematically like this:
top
greet
console.log
greet
top
console.log
top
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.
Storing this stack requires space in the computer’s memory. When the stack grows too big, the computer will fail with a message like “out of stack space” or “too much recursion”. The following code illustrates this by asking the computer a really hard question, which causes an infinite back-and-forth between two functions. Rather, it would be infinite, if the computer had an infinite stack. As it is, we will run out of space, or “blow the stack”.
function chicken() {
return egg();
}
function egg() {
return chicken();
}
console.log(chicken() + " came first.");
// → ??
Optional Arguments
The following code is allowed and executes without any problem:
alert("Hello", "Good Evening", "How do you do?");
The function alert officially accepts only one argument. Yet when you call it like this, it doesn’t complain. It simply ignores the other arguments and shows you “Hello”.
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. For example, the following version of power can be called either with two arguments or with a single argument, in which case the exponent is assumed to be two, and the function behaves like square.
function power(base, exponent) {
if (exponent == undefined)
exponent = 2;
var result = 1;
for (var count = 0; count < exponent; count++)
result *= base;
return result;
}
console.log(power(4));
// → 16
console.log(power(4, 3));
// → 64
In the next chapter, we will see a way in which a function body can get at the exact list of arguments that were passed. This is helpful because it makes it possible for a function to accept any number of arguments. For example, console.log makes use of this—it outputs all of the values it is given.
console.log("R", 2, "D", 2);
// → R 2 D 2
saving score / loading statistics ...