There is joy in reading about JavaScript. It's like catching up with an old friend who is sharing what they've been doing. Lots of cool new ideas; super popular. Yet fundamentally, they haven't changed. They're the same, weird, sometimes awkward friend. That first friend, for some of us. In the end, we're just happy they're doing well.
That's the sensation I'm getting with reading Eloquent JavaScript. Last time, I began reading the book after a difficult interview. It opened my eyes that I know Javascript, but do I reallyknow JavaScript? I've received comments that I should read Kyle Simpson'sYDKJS (You Don't Know JS) books. Ido own them. I suppose I didn't want to start with a series. I wanted a beginning-to-end kind of story. That said, I wouldn't be surprised if I decide to pick it up after Eloquent JavaScript.
On to Chapter 2,Program Structure.
And my heart glows bright red under my filmy, translucent skin and they have to administer 10cc of JavaScript to get me to come back. (I respond well to toxins in the blood.) Man, that stuff will kick the peaches right out your gills!
-_why,Why's (Poignant) Guide to Ruby
First of all, what a great quote. I read Why's guide a long time ago. It was humorous and showed me how diverse programming language communities are. Okay, back to thechapter two.
Expressions and Statements
We begin with understanding whatexpressions are and what arestatements. Anything that produces a value is an expression. Anything that is written literally is also a value.22
is an expression."hello world"
is an expression. Within a line of code, there can be multiple expressions. With that said, the line of code itself would be astatement.1
is an expression,1;
is a statement.
Notice the difference?
I like to look at expressions as nouns — statements as verbs or actions. The action can sometimes be implicit, however. In JavaScript, you don't always need to add the;
to denote the end of a statement, so sometimes, you can omit explicit statements for implicit ones.
Statements can be simple, like1;
. But these statements aren't interesting; they are useless. Interesting statements affect something. Have an impact on its world. They could display something on the screen or update the state of a program. These statements can impact other statements, creating what is known asside effects.
Side effects might sound familiar to you if you use React Hooks. I've encountered that description due to learning aboutuseEffect
. I always thought side effects were something that the React developers referenced. It's much more than that. A side effect is simply a statement containing an action or result that could impact other statements.
Bindings
Marijn usesbindings to describe a way to store data and keep an internal state. If that sounds familiar to you, it may be because you know what variables are. However, Marijn seems to insist and call them bindings. I suppose it has something to do with their definition of a variable.
Avariable is labelled as "not consistent" or having a fixed pattern; it is liable to change. This is partly correct with JavaScript variables. Using keywords likelet
orvar
makes sense with this definition. Using the keywordconst
does not fit this definition. Another way I was taught variables was by thinking about them as boxes. You are designating boxes for data you want to store and use later. If you need that data, you open up the box.
The author asks you to think a bit differently. Think of variables, orbindings, like tentacles rather than boxes. Think of them as pointers to values rather than containing values. Here's an example:let ten = 10
.ten
doesn't unpack and reveal the data10
. What it does is it returns you the number10
that it references.
It's a curious way of thinking about variables, and maybe a bit too much time was spent thinking about whether they're more like boxes or tentacles. I believe the author to be correct. Variables are references to data in memory. If we look at the code, we see that they are equal when comparing the two bindings. Why? Because10
is saved in memory once, and bothten
andanotherTen
variables reference the number. Same with the string example.
letten=10;letanotherTen=10;console.log(ten===anotherTen);// true; they are equalletword='hello';letanotherWord='hello';console.log(word===anotherWord);// true
Again, something as simple as variables creates a discussion! It's fascinating how, when I first studied Javascript, I essentially skimmed through whythings were the way they are. The rest of the chapter discusses loops and conditional execution (if-statements). If you are unsure about these topics, please make sure you read the chapter. Otherwise, I've noticed two things that I was not familiar with when using loops.
Do, while loop.
letyourName;do{yourName=prompt('what is your name?');}while(!yourName);
The difference here is that we always execute the block at least once. We always prompt the user for their name.
If they don't enter an accepted value, we will be in a loop until we get the name. I usually don't usedo, while
loops, but it's good to remember as a reference. Another thing about loops, specifically traditionalfor
loops, is that they must contain two semicolons. I write the usual syntax so frequently that I never contemplated why I needed the semicolons in the loops. Well, the statement before the first semicolon is an expression or variable declaration. After the first semicolon, we have thecondition, an expression that is evaluated before each loop iteration. Lastly we have the final expression, which will be evaluated at the end of each loop iteration.
//Notice empty space v -- we don't set a condition so it can run forever if we let itfor(letcurrent=20;;current=current+1){if(current%7==0){console.log(current);break;}}vari=0;for(;;){if(i>3)break;// we need the break statement, and still need the two semicolons!console.log(i);i++;}
So that's it for Chapter two of the book. What did you think of it? Do you think I focused too much on the theory rather than explaining other aspects, such as loops or if-conditions? Are you enjoying the book yourself? I thought this chapter was a bit of a slower pace compared to the first chapter. A minor spoiler, but I have also read the third chapterFunctions, and things pick up. By far my favourite chapter, so it's worth getting through chapter two.
Big thanks for the comments from the dev.to community. If you'd like to see some additional resources recommended by the community, check out the thread for thefirst chapter here.
Until next time.
Originally posted on my personal blog website, which you could see atalex.kharo.uk
Extra Exercises:
Chapter 2 introduced some exercises, which included a FizzBuzz exercise. My first attempt was the traditional way:
// print fizzBuzz from 1..nfunctionfizzBuzz(count){for(leti=1;i<=count;i++){if(i%15===0)console.log('FizzBuzz');elseif(i%3===0)console.log('Fizz');elseif(i%5===0)console.log('Buzz');elseconsole.log(i);}}
However we were told to think about a cleverer solution, combining the printed text together:
functionfizzBuzz(count){for(leti=1;i<=count;i++){letword='';if(i%3===0)word+='Fizz';if(i%5===0)word+='Buzz';console.log(word||i);}}
Top comments(3)

Anything that produces a value is an expression.
That is an important distinction from a statement. But there is more to it:
- Expressionsevaluate to a result value
- Statements are instructions that perform some action and do not return a value.
It's in the nature of JavaScript that expression evaluation can have side effects even though it's generally accepted that doing so makes code less understandable.
On a more abstract level one could observe:
- Expressions manipulate the flow of values (data)
- Statements manipulate the flow of control
The most obvious example is theif…elsestatement_vs. theconditional operator (an _expression).
if(i%5===0)word+='Buzz';
with anif
statement the associated block is only executed if the conditional expression evaluates to a truthy value. Theelse
portion is entirely optional. This is about flow of control.
Contrast this with
constbuzz=i%5===0?'Buzz':'';
Being an expression the conditional operator has to return a valueone way or another , so anelse
value is required - it's not optional. But given that we are guaranteed to get a value we can "grasp it" with a binding. This is about the flow of values. The expression transforms the value ofi
to a value ofBuzz
or an empty string.
Aside:
letword='Fizz';letother='';constresult=other=word+='Buzz';console.log(word);// 'FizzBuzz'console.log(other);// 'FizzBuzz'console.log(result);// 'FizzBuzz'
Theaddition assignment as well asassignment are expressions, i.e. they produce a value that can be bound. In the case of
word+='Buzz';
the produced value is simply ignored.
I like to look at expressions as nouns — statements as verbs or actions.
I'm not sure if you will find this analogy helpful in the long run and I wonder whether you arrived at this because you focused onconstant expressions. While it's true that you ultimately need side effects to get anything done you can also make progress by transforming values (and then only finally performing the side effect). I'm having difficulty correlating "nouns" with the capability of producing and transforming values.
In chapter 3 you'll be introduced to functions and in particular to the concept of "pure functions". All functions in JavaScript return a value, some simply return theundefined value. Pure functions are like expressions because they transform (input) values (producing an identical value for the same input values). Functions with side effects are more like statements. Using pure functions is a more expression-based style while predominantly using functions with side effects leads to a more imperative style (literally programming with statements; do this, then that, then the other thing) of coding.
Another way of looking at it is that expressions are value-oriented while statements are place-oriented (Value of values).
JavaScript allows for a much more expression-based style than other mainstream languages. Starting with
functionfizzBuzz(count){for(leti=1;i<=count;i++){letword='';if(i%3===0)word+='Fizz';if(i%5===0)word+='Buzz';console.log(word||i);}}
we could use the conditional operator
functionfizzBuzzer(n){for(leti=1;i<=n;i+=1){constbuzz=i%5===0?'Buzz':'';console.log((i%3===0?'Fizz'+buzz:buzz)||i);}}
and jumping ahead a bit
functionfizzBuzzer(n){for(leti=1;i<=n;i+=1)console.log(fizzBuzz(i));}functionfizz([word,n]){return[n%3===0?'Fizz'+word:word,n];}functionbuzz([word,n]){return[n%5===0?'Buzz'+word:word,n];}functionfizzBuzz(n){returnbuzz(fizz(['',n]))[0]||n.toString();}
and perhaps
constfizz=([word,n])=>[n%3===0?'Fizz'+word:word,n];constbuzz=([word,n])=>[n%5===0?'Buzz'+word:word,n];constfizzBuzz=(n)=>buzz(fizz(['',n]))[0]||n.toString();functionfizzBuzzer(n){for(leti=1;i<=n;i+=1)console.log(fizzBuzz(i));}
overdoing it a bit
constmakeFbTransform=(divisor,fragment)=>([word,n])=>[n%divisor===0?fragment+word:word,n];constfizz=makeFbTransform(3,'Fizz');constbuzz=makeFbTransform(5,'Buzz');constfizzBuzz=(n)=>buzz(fizz(['',n]))[0]||n.toString();functionfizzBuzzer(n){for(leti=1;i<=n;i+=1)console.log(fizzBuzz(i));}
and delaying the side effects a bit more
onstmakeFbTransform=(divisor,fragment)=>([word,n])=>[n%divisor===0?fragment+word:word,n];constfizz=makeFbTransform(3,'Fizz');constbuzz=makeFbTransform(5,'Buzz');constfbs=Array.from({length:100},fizzBuzz);show(fbs);functionfizzBuzz(_value,index){constn=index+1;returnbuzz(fizz(['',n]))[0]||n.toString();}functionshow(values){for(constvofvalues)console.log(v);}
Now if you learned programming with an imperative, statement-based language (as most people do) then the expression-based style will look alien - simply because it'sunfamiliar (Simplicity Matters).
The point is that the expression-based style doesn't rely on mutation in the same way that the statement-based style does.
GOTO was evil because we asked, "how did I get to this point of execution?" Mutability leaves us with, "how did I get to this state?"14:34 PM - 11 May 2013
In JavaScript mutation isn't "evil", it's par for the course. Local mutability is usually fine but mindless, careless mutation can become a problem. Adopting an expression-based coding style can help reduce the use of mutation - as long as the performance cost and memory consumption remains within acceptable limits (there's always a trade-off and it's always a balancing act).
Another thing that may interest you:JavaScript for-loops are… complicated - HTTP203.

- LocationLondon
- WorkSoftware Engineer at MVF Global
- Joined
Wow,@peerreynders, thanks again for the lovely, in-depth reply.
Just to clarify some things, does that mean in this piece of code:if (i % 5 === 0) word += 'Buzz';
the expression isi % 5 === 0
as that returns a value whilst the whole piece of code is a statement? Isword += 'Buzz'
a statement as well, with a side effect?
I am having difficulty correlating "nouns" with the capability of producing and transforming values.
I initially looked at expressions as values;5
is 5, but with your comment on how expressions result from an evaluation, I see that expressions are far more than just "values". As you mentioned, they can also transform values or manipulate the flow. I suppose I did regard them asconstant expressions.
I have never heard of PLOP or the value of values talk! Interesting. Would you then say the majority of code in JavaScript is place-oriented?
If new information replaces the old, you are doing place oriented programming.
It seems like we are constantly writing statements.
Adopting an expression-based coding style can help reduce the use of mutation - as long as the performance cost and memory consumption remains within acceptable limits
If we look at how you disassembled theFizzBuzz
example to be more expression-based, it became less and less clear on what exactly was the code suppose to be doing. It may have been more expression-based, but it also loses its simplicity (as you mentioned), so I would add that as a limit alongside performance and memory consumption.

Is
word += 'Buzz'
a statement as well, with a side effect?
Assignments are expressions, the only thing that's a statement is theif
statement. It's unfortunate but in JavaScript expression evaluation can have side effects.
Would you then say the majority of code in JavaScript is place-oriented?
Make no mistake, JavaScriptis an imperative language. In fact it's way more statement-based than for example Rust. While also being an imperative language Rust embraced expressions to such an extent that it supports a lot of practices that are typical for functional languages while also supporting an imperative style of coding. But as a result of Brendan Eich being a Scheme fan when he designed JavaScript there is enough wiggle room to pursue a value-oriented style in JavaScript (JavaScript is a LISP). And from what I can judge that is the style that Marijn Haverbeke is leaning towards (though not with any dogma).
It may have been more expression-based, but it also loses its simplicity (as you mentioned)
I would argue that it actually gainssimplicity but loses "easiness" ("simple" scales, "easy" does not).
Before I can dive into that particular discussion it may be useful to expose my thinking behind the various changes:
Again starting with:
functionfizzBuzz(count){for(leti=1;i<=count;i++){letword='';if(i%3===0)word+='Fizz';if(i%5===0)word+='Buzz';console.log(word||i);}}
For my taste this is a bit of alumpers solution - it feels more like a script than a function with focus - and given the context that may be OK.
However thecore rules
... for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.
don't seem to get the boundary they deserve so
functionfizzBuzzer(count){for(leti=1;i<=count;i+=1)console.log(fizzBuzz(i));}functionfizzBuzz(n){letword='';if(n%3===0)word+='Fizz';if(n%5===0)word+='Buzz';returnword||n.toString();}
Now "the rules" are extracted intofizzBuzz
whilefizzBuzzer
orchestrates generating the necessary input values and outputs the result values. Note then.toString()
to ensure thatfizzBuzz
returns astring
rather than astring | number
(sum types).
Now if you're OK with local reassignment/mutation you can stop here.
Eliminating reassignment/mutation takes a bit more work
functionfizzBuzzer(count){for(leti=1;i<=count;i+=1)console.log(fizzBuzz(i));}functionfizzBuzz(n){returnbuzz(fizz(n))||n.toString();}functionfizz(n){returnn%3===0?['Fizz',n]:['',n];}functionbuzz(value){const[word,n]=value;returnn%5===0?word+'Buzz':word;}
Functions that accept one single value compose better given that functions only return one value. To be able to simply writebuzz(fizz(n))
fizz
has to return the string andn
simultaneously. Traditionally in JavaScript an object{ word , n }
would be used for that purpose but for just two values that's a bit verbose so a tuple is used to pass the necessary data tobuzz
.
Aside:
JavaScript only has arrays, however they can be used in different ways. When an array is used "as a list" it can hold zero to many elements but the elements are often expected to be all of the same type. When an array is used as a tuple it is expected to have a certain, exact length but can hold values of varying types though each position holds the expectation of a specific type. Herefuzz
returns a[string,number]
tuple (pair, couple).
There is apipeline proposal under consideration. With thatbuzz(fizz(n))
could be rewritten asn |> fizz |> buzz
- pipelining values through a chain of functions makes the order of operations more obvious.
At this point there is a certain similarity betweenfizz
andbuzz
- they can be easily homogenized:
functionfizzBuzzer(count){for(leti=1;i<=count;i+=1)console.log(fizzBuzz(i));}functionfizzBuzz(n){returnbuzz(fizz(['',n]))[0]||n.toString();}functionfizz(value){const[word,n]=value;returnn%3===0?[word+'Fizz',n]:value;}functionbuzz(value){const[word,n]=value;returnn%5===0?[word+'Buzz',n]:value;}
This makesbuzz(fizz(['', n]))[0]
a bit more complicated.['', n]
has to be supplied as an initial value and at the end we extract theword
with an array index of0
. At this point somebody may yell "repetition":
functionfizzBuzzer(count){for(leti=1;i<=count;i+=1)console.log(fizzBuzz(i));}functionfizzBuzz(n){returnbuzz(fizz(['',n]))[0]||n.toString();}functionmakeTransform(divisor,fragment){returnfunction(value){const[word,n]=value;returnn%divisor===0?[word+fragment,n]:value;};}constfizz=makeTransform(3,'Fizz');constbuzz=makeTransform(5,'Buzz');
Pros: nice highlighting and separation of commonality and variability.
Commonality:
functionmakeTransform(divisor,fragment){returnfunction(value){const[word,n]=value;returnn%divisor===0?[word+fragment,n]:value;};}
Variability:
constfizz=makeTransform(3,'Fizz');constbuzz=makeTransform(5,'Buzz');
Cons:makeTransform
is more difficult to understand. And more importantly I think this is a case ofduplication being far cheaper than the (wrong) abstraction given that we know that we'll only ever needfizz
andbuzz
.
Now one could be perfectly OK with the reassignment/mutation for the sake of afor…loop
but staying with the "no destruction of values" theme:
functionfizzBuzz(n){returnbuzz(fizz(['',n]))[0]||n.toString();}functionfizz(value){const[word,n]=value;returnn%3===0?[word+'Fizz',n]:value;}functionbuzz(value){const[word,n]=value;returnn%5===0?[word+'Buzz',n]:value;}functiondisplayValues(values){for(constvalueofvalues)console.log(value);}displayValues(Array.from({length:100},(_v,i)=>fizzBuzz(i+1)));
Sorry, as far as I'm concernedforEach is an imposter HOF (higher order function), so I'll preferfor…of
Now back to our originally scheduled programming…
Juxtaposing place-oriented (PLOP):
functionfizzBuzz(count){for(leti=1;i<=count;i++){letword='';if(i%3===0)word+='Fizz';if(i%5===0)word+='Buzz';console.log(word||i);}}
with value-oriented (VOP)
functionfizz(value){const[word,n]=value;returnn%3===0?[word+'Fizz',n]:value;}functionbuzz(value){const[word,n]=value;returnn%5===0?[word+'Buzz',n]:value;}functionfizzBuzz(n){returnbuzz(fizz(['',n]))[0]||n.toString();}functiondisplayValues(values){for(constvalueofvalues)console.log(value);}displayValues(Array.from({length:100},(_v,i)=>fizzBuzz(i+1)));
- For one we have to acknowledge that if we learned and primarily practice imperative programming it's going to seem "easier" to us than approaches from other paradigms (for example imperative programming doesn't really prepare you for SQL).
- FizzBuzz is a small enough problem that itFits in Your Head as is. Real problems tend to be much larger which is why we distribute functionality over multiple functions or objects. However even when partitioned, reasoning about code whichfreely interacts with other parts of the system (or "the world") via mutation of shared data and side effects can be difficult primarily because the "state" of mutable things varies with time. For example in the PLOP code
word
isn't just a simple value but it potentially keeps changing (though given that it's not exposed outside of the loop it isn't a big issue in this case). - Looking at the VOP code: It may initially take some time to get your eye in but
fizz
is extremely simple: it either returns the original[word,n]
value or a new[word + 'Fizz',n]
value depending on the value ofn
. Nowfizz
isn't as descriptive ason3AppendFizzToWord
but in this context it's likely good enough so that we can forget about the actual code and just know whatfizz
is about. - Similarly for
buzz
as it fits exactly the same pattern. - In
fizzBuzz
the compactness ofbuzz(fizz(['', n]))[0]
could be an issue - I think(['', n] |> fizz |> buzz))[0]
would be easier to read, i.e. transform['',n]
throughfizz
andbuzz
and use the first element of the resulting value. But againfizzBuzz
only uses a single line of extremely simple functions and operators. Once parsed you can forget about the code and know whatfizzBuzz
means. fizz
,buzz
andfizzBuzz
are pure functions and therefore referentially transparent.referential transparency is the property of being able to replace a function with its return value for a given input; functions that depend on other factors arereferentially opaque, so:- referentially transparent -> simple
- referentially opaque -> not so simple
- The idea is to stick to building "simple" functions as building blocks and create other (simple) building blocks by composing them - yielding functions capable of complex value transformations.
- Aside: Scott Wlaschin viewsfunctions as Interfaces (i.e.simple interfaces). With that view functions should be easier to compose because object interfaces can be a lot more complicated.
displayValues
has side effects. But it doesn't have to know anything aboutfizzBuzz
. These type of functions are necessary but ideally we should separate them for easy identification. Ideally a system should be organized asfunctional core and imperative shell (Hexagonal architecture). The functional core should be easy to test as it just transforms values.
The idea is that VOP scales better than PLOP for larger problems because it can stick to simple building blocks while achieving complex value transformations through composition.
If you're interestedRefactoring JavaScript goes through typical tactics used to improve JavaScript code. It discusses OO-practices as well as functional practices. The functional refactoring largely revolves aroundavoiding destructive actions, mutation, and reassignment.
For further actions, you may consider blocking this person and/orreporting abuse