Вы находитесь на странице: 1из 13

Expressions and statements

A fragment of code that produces a value is called an expression.


If an expression corresponds to a sentence fragment, a
JavaScript statement corresponds to a full sentence. A program is a
list of statements.
It could display something on the screen—that counts as changing the
world—or it could change the internal state of the machine in a way
that will affect the statements that come after it. These changes are
called side effects.

Bindings
How does a program keep an internal state? How does it remember
things?
When a binding points at a value, that does not mean it is tied to
that value forever.
You should imagine bindings as tentacles, rather than boxes.
They do not contain values; they grasp them
two bindings can refer to the same value.

Binding names
Binding names can be any word.
A binding name may include dollar signs ($) or underscores (_) but no
other punctuation or special characters.

The environment
The collection of bindings and their values that exist at a given time is
called the environment.

Functions
A function is a piece of program wrapped in a value.
Such values can be applied in order to run the wrapped program.

Return values
Showing a dialog box or writing text to the screen is a side effect.
A lot of functions are useful because of the side effects they produce.
Functions may also produce values, in which case they don’t need to
have a side effect to be useful.
When a function produces a value, it is said to return that value.
Anything that produces a value is an expression in JavaScript.

Control flow
When your program contains more than one statement, the
statements are executed as if they are a story, from top to bottom.

Conditional execution
Not all programs are straight roads.
This is called conditional execution.

while and do loops


Looping control flow allows us to go back to some point in the
program where we were before and repeat it with our current program
state.

Breaking Out of a Loop


Having the looping condition produce false is not the only way a loop
can finish. There is a special statement called break that has the effect
of immediately jumping out of the enclosing loop.
Updating bindings succinctly
Especially when looping, a program often needs to “update” a binding
to hold a value based on that binding’s previous value.

counter += 1;
result *= 2;

Dispatching on a value with switch


switch (prompt("What is the weather like?")) {
case "rainy":
console.log("Remember to bring an umbrella.");
break;
case "sunny":
console.log("Dress lightly.");
case "cloudy":
console.log("Go outside.");
break;
default:
console.log("Unknown weather type!");
break;
}

Functions
Functions are the bread and butter of JavaScript programming.

Defining a function
A function definition is a regular binding where the value of the
binding is a function.

Bindings and scopes


Each binding has a scope, which is the part of the program in which
the binding is visible.
For bindings defined outside of any function or block, the scope is the
whole program—you can refer to such bindings wherever you want.
These are called global.
But bindings created for function parameters or declared inside a
function can be referenced only in that function.
Bindings declared with let and const are in fact local to the block that
they are declared in.

Nested scope
JavaScript distinguishes not just global and local bindings.
Blocks and functions can be created inside other blocks and functions,
producing multiple degrees of locality.
The set of bindings visible inside a block is determined by the place of
that block in the program text.
Each local scope can also see all the local scopes that contain it, and all
scopes can see the global scope.
This approach to binding visibility is called lexical scoping.
Each Lexical Environment has three components:
1. Environment Record
2. Reference to the outer environment,
3. This binding.
Functions as values
A function binding usually simply acts as a name for a specific piece
of the program.
It is possible to store a function value in a new binding, pass it as an
argument to a function, and so on.

Arrow functions
Instead of the function keyword, it uses an arrow (=>) made up of an
equal sign and a greater-than character.
const square1 = (x) => { return x * x; };
const square2 = x => x * x;

The call stack


Because a function has to jump back to the place that called it when it
returns, the computer must remember the context from which the call
happened.
Every time a function is called, the current context is stored on top of
this stack.
When a function returns, it removes the top context from the stack
and uses that context to continue execution.
The place where the computer stores this context is the call stack.
Execution context (EC) is defined as the environment in which
JavaScript code is executed. reference
Global execution context (GEC): This is the default execution
context in which JS code start it’s execution when the file first
loads in the browser.
Functional execution context (FEC): Functional execution
context is defined as the context created by the execution of code
inside a function.

Eval: Execution context inside eval function.

Execution context stack (ECS): Execution context stack is a


stack data structure to store all the execution stacks created while
executing the JS code.

JavaScript engine creates the execution context in the following


two stages:

1. Creation phase
2. Execution phase

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 get assigned the
value undefined.

Recursion
It is perfectly okay for a function to call itself, as long as it doesn’t do it
so often that it overflows the stack.
But this implementation has one problem: in typical JavaScript
implementations, it’s about three times slower than the looping
version. Running through a simple loop is generally cheaper than
calling a function multiple times.
Worrying about efficiency can be a distraction. It’s yet another factor
that complicates program design, and when you’re doing something
that’s already difficult, that extra thing to worry about can be
paralyzing.
Therefore, always start by writing something that’s correct and easy to
understand.

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.
Functions that create values are easier to combine in new ways than
functions that directly perform side effects.
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 (it doesn’t read global bindings whose value might
change).
A pure function has the pleasant property that, when called with the
same arguments, it always produces the same value.
A call to such a function can be substituted by its return value 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.
// A less verbose function value
let h = a => a % 3;
A key aspect in understanding functions is understanding scopes.
Each block creates a new scope.
Bindings declared with var behave differently—they end up in the
nearest function scope or the global scope.

Data Structures: Objects and Arrays

Numbers, Booleans, and strings are the atoms that data structures are
built from. Many types of information require more than one atom,
though. Objects allow us to group values—including other objects—to
build more complex structures.
Almost all JavaScript values have properties. The exceptions
are null and undefined.
The two main ways to access properties in JavaScript are with a dot
and with square brackets. Both value.x and value[x] access a
property on value—but not necessarily the same property.

Mutability
We saw that object values can be modified. The types of values such as
numbers, strings, and Booleans, are all immutable.
You can combine them and derive new values from them, but when
you take a specific string value, that value will always remain the same.
Objects work differently. You can change their properties, causing a
single object value to have different content at different times.
Correlation is a measure of dependence between statistical variables.

Rest parameters
The rest parameter is bound to an array containing all further
arguments.

The Math object


The Math object is used as a container to group a bunch of related
functionality. There is only one Math object, and it is almost never
useful as a value. Rather, it provides a namespace so that all these
functions and values do not have to be global bindings.
Having too many global bindings “pollutes” the namespace.
The more names have been taken, the more likely you are to
accidentally overwrite the value of some existing binding.
Though computers are deterministic machines—they always react the
same way if given the same input—it is possible to have them produce
numbers that appear random.

JSON
JSON looks similar to JavaScript’s way of writing arrays and objects,
with a few restrictions. All property names have to be surrounded by
double quotes, and only simple data expressions are allowed—no
function calls, bindings, or anything that involves actual computation.
Comments are not allowed in JSON.
JavaScript gives us the functions JSON.stringify and JSON.parse to
convert data to and from this format.
Chapter 5
Higher-Order Functions

A large program is a costly program, and not just because of the time it
takes to build.
Size almost always involves complexity, and complexity confuses
programmers.

Abstraction
It is a useful skill, in programming, to notice when you are working at
too low a level of abstraction.
Higher-order functions
Functions that operate on other functions, either by taking them as
arguments or by returning them, are called higher-order functions.
Higher-order functions allow us to abstract over actions, not just
values.

Transforming with map


The map method transforms an array by applying a function to all of its
elements and building a new array from the returned values.
The new array will have the same length as the input array, but its
content will have been mapped to a new form by the function.
Summarizing with reduce
Another common thing to do with arrays is to compute a single value
from them.
The higher-order operation that represents this pattern is
called reduce(sometimes also called fold).

Composability
Higher-order functions start to shine when you need
to compose operations
function average(array) {
return array.reduce((a, b) => a + b) / array.length;
}
console.log(Math.round(average(SCRIPTS.filter(s => s.living).map(s => s.year))));

// → 1188

console.log(Math.round(average(SCRIPTS.filter(s => !s.living).map(s => s.year))));

// → 188

The Secret Life of Objects


Prototypes
A prototype is another object that is used as a fallback source of
properties. When an object gets a request for a property that it does
not have, its prototype will be searched for the property, then the
prototype’s prototype, and so on.
So who is the prototype of that empty object? It is the great ancestral
prototype, the entity behind almost all objects, Object.prototype.
Many objects don’t directly have Object.prototype as their
prototype but instead have another object that provides a different set
of default properties. Functions derive from Function.prototype,
and arrays derive from Array.prototype.

Classes
Prototypes are useful for defining properties for which all instances of
a class share the same value, such as methods.
So to create an instance of a given class, you have to make an object
that derives from the proper prototype, but you also have to make sure
it, itself, has the properties that instances of this class are supposed to
have. This is what a constructor function does.
Constructors (all functions, in fact) automatically get a property
named prototype, which by default holds a plain, empty object that
derives from Object.prototype.
It is important to understand the distinction between the way a
prototype is associated with a constructor (through
its prototype property) and the way objects have a prototype (which
can be found with Object.getPrototypeOf).

Overriding derived properties


When you add a property to an object, whether it is present in the
prototype or not, the property is added to the object itself.
If there was already a property with the same name in the prototype,
this property will no longer affect the object, as it is now hidden
behind the object’s own property.
Overriding properties that exist in a prototype can be a useful thing to
do.

Maps
A map (noun) is a data structure that associates values (the keys) with
other values.
First, it is possible to create objects with no prototype. If you
pass null to Object.create, the resulting object will not derive
from Object.prototype and can safely be used as a map.
Object property names must be strings. If you need a map whose keys
can’t easily be converted to strings—such as objects—you cannot use
an object as your map.

Fortunately, JavaScript comes with a class called Map that is written for
this exact purpose. It stores a mapping and allows any type of keys.
let ages = new Map();
ages.set("Boris", 39);
ages.set("Liang", 22);
ages.set("Júlia", 62);

Polymorphism
When a piece of code is written to work with objects that have a
certain interface any kind of object that happens to support this
interface can be plugged into the code, and it will just work.
This technique is called polymorphism. Polymorphic code can work
with values of different shapes, as long as they support the interface it
expects.

Symbols
Symbols are values created with the Symbol function. Unlike strings,
newly created symbols are unique—you cannot create the same symbol
twice.

Вам также может понравиться