Upload
others
View
1
Download
0
Embed Size (px)
Citation preview
Tasting JavaScript
Minimum Actionable Knowledge to be Skilled JSer
Ken Chen
Tasting JavaScriptMinimum Actionable Knowledge to be Skilled JSer
Ken Chen
© 2016 Ken Chen
Contents
I Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.1 Environment Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
II JavaScript Basics . . . . . . . . . . . . . . . . . . . . . . . . . 5
2. Data Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.1 Number . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62.2 Special Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.3 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.4 undefined & null . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.5 Boolean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.6 Automatic Type Coersion . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.7 Conditional & Logical Operators . . . . . . . . . . . . . . . . . . . . . . . 12
3. Program Structure and Flow Control . . . . . . . . . . . . . . . . . . . . . . . 153.1 Expressions & Statements . . . . . . . . . . . . . . . . . . . . . . . . . . 153.2 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Convention . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17Refer, not contain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18Default value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18Dynamic nature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.3 Run program file in Node.js . . . . . . . . . . . . . . . . . . . . . . . . . 193.4 Debug program file in Node.js . . . . . . . . . . . . . . . . . . . . . . . . 203.5 Control Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Conditional - if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23Conditional - switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25Loop - while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26Loop - for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28Loop Interruption - break . . . . . . . . . . . . . . . . . . . . . . . . . . 28Loop Interruption - continue . . . . . . . . . . . . . . . . . . . . . . . . 29
3.6 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
CONTENTS
3.7 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Triangle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Holed triangle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31Bing! Go! Bingo! Boom! . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4. Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334.1 Definition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Expression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.2 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36Optional Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36Arguments Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36Parameters are keys, not boxes . . . . . . . . . . . . . . . . . . . . . . . 37
4.3 Scopes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 374.4 Recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394.5 Call Stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Any Sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42Directory Printing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5. Data Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 445.1 Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45The special this . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46Enumerate Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48Mutability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
5.2 Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
5.3 Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52Object as Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52Native Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
5.4 Set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 545.5 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.6 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56Month Name Resolver . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56Array to Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57Matrix Convertor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
6. Object-oriented . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 596.1 Core concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
CONTENTS
6.2 Encapsulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60Getter and Setter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63Enumerable vs Non-enumerable . . . . . . . . . . . . . . . . . . . . . . 64
6.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65Override . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67Self-defined inheritance relationship . . . . . . . . . . . . . . . . . . . . 69
6.4 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 706.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
Whac-A-Mole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
7. Functional Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 757.1 Core concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
First-class & Higher-order Function . . . . . . . . . . . . . . . . . . . . . 75Pure Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
7.2 Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 777.3 Functional behaviors on Array . . . . . . . . . . . . . . . . . . . . . . . . 787.4 Closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
Common Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81Common Mistake . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
7.5 Exercises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87Array to Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87Trampoline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87Currying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
I Hello World
1. IntroductionMost good programmers do programming not because they expect to getpaid or get adulation by the public, but because it is fun to program. – LinusTorvalds
Tasting JavaScript is a book makes you learn JavaScript like tasting some food, bit bybit, enjoyable and memorable. But you might be wondering whether JavaScript is reallytasty and easy to digest because the rumor of it goes completely opposite.
Some people said that JavaScript is a language poorly designed andwritten in 7 days. Andyou can tell the gap between JavaScript: The Good Parts1 and JavaScript: The DefinitiveGuide2 from their sizes.
The Good Parts vs Definitive Guide
1https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/05965177422https://www.amazon.com/JavaScript-Definitive-Guide-David-Flanagan/dp/0596101996
Introduction 3
However, many others are fancy about it because JavaScript is a so-called Full-Stacklanguage. It can be used in the browser to build an interactive web page, and also canbe used in the server side to operate database as a web server. What is more, you canuse it to build cross-platform desktop, or even a mobile application. JavaScript soundsso powerful and mysterious like Popeye the Sailor’s favorate spinach.
I can tell you that both are truth depending on your angle. In my opinion, JavaScript isnot difficult to digest (no complex development environment, nor compilation process)and tasty (funny to use it in plenty of areas). You have to try and make your own decisionregardless what others say.
1.1 Environment Setup
We’d better have our table set before the main course arrives.
There are two main environments for JavaScript to be executed, the browser and theserver. Although browser should be ready in everyone’s computer, it’s not friendlyenough for a JavaScript starter. So, I will choose Node.js3 as the environment for youto start tasting JavaScript. You don’t really need to figure out what it’s now.
For Windows & Mac users, the simplest way is to use the installer shown in homepage.For Linux user, since you are tough enough to use Linux, I think you should be able tofigure out your way to install it. You’d better choose the v6 LTS version which means it’sLong-term supported.
After the installation, please type node in the command line and you should be able tosee similar screen as below. It means you have entered the Node.js console already, it’sa playground and you can type in JavaScript statements there for execution. If you wantto leave, just press Ctrl+C (which means pressing Ctrl and C on your keyboard at thesame time), twice.
3http://nodejs.org/
Introduction 4
REPL
OK, you are all set already. Simple, right? In later chapters, when you are requested toinput some code, you just need to fire up this Node.js console in the command line andtype the code there. Let’s move on if you already want to try the course.
1.2 Exercises
Starting from chapter 3, there are some exercises at the end. Please try your best toaccomplish them before seeing the answer4.
4https://github.com/kenspirit/tasting-javascript-exercise
II JavaScript Basics
2. Data TypesI am not a great programmer; I am just a good programmer with great habits.– Kent Beck
Data/value to computer program is the ingredient to food. There are several basic datatypes in JavaScript, number, string, boolean, undefined, function and object. Object andFunction will be discussed in standalone chapters as they are so special and importantin JavaScript.
2.1 Number
A calculator might be the most commonly seen and basic form of computer in the olddays. How about we try to use JavaScript to do some numeric calculation first? Please tryto type 1 + 2 * 3 / 4 - 5 and press Enter in Node.js console.
1 -1 + 2 * 3 / 4 - 5
2 // -> -4.5
Notes: The two slashes and arrow are indicating the output of the program. They arenot part of the program. But even if you input that part in console, it makes no harm asanything after two slashes are treated as comment.
The symbols +, -, * and / are called operator. They can be used on numbers. + is foraddition, * is for multiplication, / is for division. - is for subtraction and also negatesa value. The order of applying these operators, determined by their precedence, alignswith the math we learn in school. If you want to ajust the order, parentheses () can beused.
1 -(1 + 2) * 3 / (4 - 5)
2 // -> 9
There is one more arithmetic operator %, called Modulus. It is used for remaindercalculation. X % Y calculates the remainder of dividing X by Y. For example, 10 % 3
produces 1. You can try it out combining with other operators to verify its precedence.
Data Types 7
2.2 Special Numbers
The computer we are using has such huge memory and fast CPU now. Have you everwonder how fast it calculates and what is themaximumnumber supported in JavaScript?Try below calculation by multiplying 987654321987654321987654321987654321 to itself eighttimes.
1 987654321987654321987654321987654321 * 987654321987654321987654321987654321 * 98\
2 7654321987654321987654321987654321 * 987654321987654321987654321987654321 * 9876\
3 54321987654321987654321987654321 * 987654321987654321987654321987654321 * 987654\
4 321987654321987654321987654321 * 987654321987654321987654321987654321
5 // -> 9.05398453661256e+287
Above result comes out can be roughly estimated as 9e+287 which means that there are287 zero after 9. Does it really has a maximum limit? Yes. You can type in below symbolto see the largest value supported in JavaScript.
1 Number.MAX_VALUE
2 // -> 1.7976931348623157e+308
So what happen if you try to plus 1 on it?
1 Number.MAX_VALUE + 1
2 // -> 1.7976931348623157e+308
Seems nothing happen. Actually, in JavaScript, when you hit some threshold, the cal-culation on the number is not that accurate already. The Number.MAX_SAFE_INTEGER is themaximum integer for safe calculation.
1 Number.MAX_SAFE_INTEGER
2 // -> 9007199254740991
3
4 Number.MAX_SAFE_INTEGER + 1
5 // -> 9007199254740992
6
7 Number.MAX_SAFE_INTEGER + 2
8 // -> 9007199254740992
Calculations with fractional numbers can also produce imprecise result either.
Data Types 8
1 0.001 - 0.0003
2 // -> 0.0007000000000000001
Hence, keep in mind that fractional digital numbers should be treated as approximatedresult and not be used in exact equality comparison either. If you want to learn more indetail, you can take a look on the Double-precision floating-point format1.
What happen if we multiply the maximum value by 2? Will there be any error? Stay calm.Experiment on programming language is much safer than physics or chemistry. Let’s tryout the Number.MAX_VALUE * 2 in the Node.js console.
What do you got? Infinity, right? That is a special number in JavaScript. See what youcan get if we use the operators we learned before on it, like Infinity + Infinity, Infinity/ Infinity, Infinity * -1.
Another strange result NaN should be produced if you did try. NaNmeans Not a Number,and 0 / 0 can also produced that. Any numeric operation involved NaN yields the resultof NaN itself. One very special feature on it is that it does not equals to itself. You can tryabout that after you learned the comparison operators later.
2.3 String
Besides numbers in real world, another common type of information we produced istext. String data type is used to represent text in many programming language, so doesJavaScript. Below are two String representative using quotes to enclose the text content.
1 'Ever imagine how easy JavaScript is?'
2 "Tell me about it."
If quotes are used to enclose the text content to represent String, what can I do if thetext I want to show contains quotes? You just need to make sure the quote inside thetext content is different than the ones used to enclose it. In case you do have to use thesame style of quote, you need to escape the quote character in text content by usingbackslash (\).
1https://en.wikipedia.org/wiki/Double-precision_floating-point_format
Data Types 9
1 console.log('I love reading "Tasting JavaScript".');
2 // -> I love reading "Tasting JavaScript".
3
4 console.log("It's fantastic");
5 // -> It's fantastic
6
7 console.log('It\'s fantastic');
8 // -> It's fantastic
Notes: The console.log() is a JavaScript function. Function concept will be introducedlater. For now, you just need to know that it helps to print out the thing you put insideits parentheses.
Besides escaping, backslash actually have the capability of empowering the characterright after it has special meaning. For example, if you want to separate your text contentinto multiple lines, you can use \n (new line character) inbetween.
1 console.log('It\'s fantastic.\nCome and learn JavaScript.');
2 // -> It's fantastic.
3 // -> Come and learn JavaScript.
I will leave a question here: How to print out the backslash itself?
Actually, if the environment you play around complies with ES6, ECMAScript 6 standard,there is one more convenient way to represent multiple lines’ text. You can use a pair ofback-tick to enclose the multi-line strings.
1 console.log(`
2 Free-style writing here.
3
4 Without any constraints.
5 Feels great!
6 `);
Is that we can just print out the String?What else canwe do about it? Themost commonlyseen String operation is to use the + operator to concatenate them. Why go through thetrouble of concatenation while we can simply write a full sentence? Because after welearn about variable and function later, we might need to dynamically glue the thingstogether. At that time, this would be very useful.
Data Types 10
1 console.log('It' + '\'s' + ' ' + 'combined' + ' together.');
2 // -> It's combined together.
2.4 undefined & null
In JavaScript, two values both represent the absence of a value, but belong to twodifferent data types. undefined is undefined data type while null is object data type. This isactually a design flaw in JavaScript. Most of the time, they are interchangable. However,I would suggest that you should use them following some good practices which I willshow you in chapters about Object and Function.
2.5 Boolean
Boolean data type has just two value of Boolean, true and false. They are seldom usedvisually like the number and string type. Decisionmaking in program flow control is theirmajor usage and it will be introduced in next chapter. Normally, the Boolean value isyielded from comparison operators, <, >, <=, >=, ==, ===, != and !==.
1 1 == 1
2 // -> true
3
4 2 != 2
5 // -> false
6
7 10 > 9
8 // -> true
9
10 8 < 6
11 // -> false
12
13 'hello' < 'hi'
14 // -> true
15
16 'Hello' < 'hello'
17 // -> true
18
19 'Hello' < 'ä½ å¥½'
20 // -> true
Comparing String value might seems unnecessary at first. However, it’s useful whenyou want to sort some text, sentences for example. When comparing String, uppercase
Data Types 11
character is smaller than lowercase; english character is smaller than non-Englishcharacter. The actual mechanism behind is that each character is virtually assigned anumber based on Unicode standard. And JavaScript compares each character in Stringone by one from left to right using that numberic code behind.
2.6 Automatic Type Coersion
The operators we introduced normally require value of same data type in order to havemeaningful result. However, it will not generate any error but produce some strangeresult sometimes if you feed different types of value to it. JavaScript does somethingcalled Automatic Type Coersion behind based on some complex rule.
1 1 > 'a' // The 'a' is converted to number type which is NaN here
2 // -> false
3
4 1 < 'a'
5 // -> false
6
7 1 == true
8 // -> true
9
10 0 == false
11 // -> true
12
13 '' != false
14 // -> false
15
16 '5' + 1
17 // -> '51'
18
19 '5' - 1
20 // -> 4
21
22 undefined == null
23 // -> true
24
25 1 * null
26 // -> 0
27
28 1 * undefined
29 // -> NaN
Data Types 12
30
31 !''
32 // -> true
It’s really not easy to remember all the rules. Hence, we should avoid any operationinvolved different types of value. That is why two strict equality comparison operators=== and !== are provided to enforce comparing data type as well.
1 undefined === null
2 // -> false
3
4 0 === false
5 // -> false
2.7 Conditional & Logical Operators
Conditional operator, also the only tenary operator, consists of a question mark and acolon.
1 true ? 'left' : 'right'
2 // -> 'left'
3
4 false ? 'left' : 'right'
5 // -> 'right'
The expression on the left of the question mark is the condition to evaluate. The valueon the left of the colon mark is picked when the condition is true; the value on the rightis picked when the condition is false.
Logical operators &&, ||, ! (exclamation mark) represents the logical AND, OR and NOTrespectively.
1 !true
2 // -> false
3
4 true && false
5 // -> false
6
7 false || true
8 // -> true
Actually, it’s not necessary to use boolean type value in && and ||. What is more, thereturn value of the expression is not the final boolean result but the left side or rightside value. Let’s see some example before explanation.
Data Types 13
1 // --- && scenarios --- //
2
3 1 < 2 && 2 // When left side expression is true, return right side value
4 // -> 2
5
6 1 > 2 && 2 // When left side expression is false, return left side value
7 // false
8
9 1 && 2 // Converts 1 to true, return right side value
10 // -> 2
11
12 0 && 2 // Converts 0 to false, return left side value
13 // -> 0
14
15 // --- || scenarios --- //
16
17 1 < 2 || 0 // When left side expression is true, return left side value
18 // -> true
19
20 1 > 2 || 0 // When left side expression is false, return right side value
21 // -> 0
22
23 1 || 2 // Converts 1 to true, return left side value
24 // -> 1
25
26 0 || 2 // converts 0 to false, return right side value
27 // -> 2
Why is that? It seems confusing at the beginning and difficult to memorize it. But it willbe clear after you know the mechanism behind, the Short-circuit evaluation.
You know, if && logically represents AND, that means the whole && expression can be true
only when both side of the expression is true. Hence, if the left side is false, then theright side expression is not necessary to be evaluated and so it returns value of the leftside expression immediately. If the left side is true, the right side value determines thevalue of the whole expression and so it returns the right side expression.
For the same reason, if || locally represents OR, that means the whole || expressioncan be true when either side of the value is true. Hence, if the left side is true, then theright side value is not necessary to be evaluated and so it returns value of the left sideexpression immediately. If the left side is false, the right side value determines the valueof the whole expression and so it returns the right side expression.
The Short-circuit evaluation allows us to put more complex evaluation to the right-handside for better performance by reducing unnecessary operation. And another commonly
Data Types 14
use case to take advantage of Short-circuit evaluation is to fall back on a default value.Below example seems silly but this concept is useful when it’s used in variable declarationor function parameter.
1 console.log(null || 'default name');
2 // -> 'default name'
3
4 console.log('Ken' || 'default name');
5 // -> 'Ken'
3. Program Structure and FlowControlAny fool can write code that a computer can understand. Good programmerswrite code that humans can understand.– Martin Fowler
What we have learned in the previous chapters cannot be really called programming yet.A finished program is like an article. The main purpose of writing a program is to let thecomputer to do some work for you, but also it’s more important to concisely and clearlyexpress your idea.
3.1 Expressions & Statements
A program is formed by a series of statements just like an article is formed by sentences.What we have learned so far is just the building block, expressions, of statements. To bemore precise, an expression is a fragment of code that produces value. Simple value isthe most basic unit of expression. You can combine many simple expressions to form amore complex expression just like composing phrases together. Below are some samplesof expressions.
1 'me'
2 // -> 'me'
3
4 1 + 1
5 // -> true
6
7 'you' && 'me'
8 // -> 'me'
As writing program is actually instructing what the computer should do, each statementshould be a complete instruction, although that is not mandantory just like you cansimply use only an exclam as a sentence. The completeness in JavaScript is normallyindicated by an ending ; character. Below are samples of statements.
Program Structure and Flow Control 16
1 'aha'; // Not useful but still a statement
2
3 console.log('Show me the code.'); // This instructs computer to print out some t\
4 ext
Notes: the ; is not necessary to annotate the end of a statement. Simply pressing Enter
and form a newlie will do. JavaScript execution environment is intelligent enough todetect whether it’s a complete statement.
3.2 Variables
Looked back to the expressions or statements we wroted before, we use the valueimmediately and cannot refer it back later. If we want to build a program to keep track ofour bank account, wemust refer to it from time to time in order to do deposit, withdrawalor balance checking, right? Variable is what we use in almost every programminglanguage for this purpose.
In JavaScript, the var keyword is used to define a variable andwe can assign a value to thevariable by using a = followed by the value. We can simulate an bank account operationswith below statements.
1 var myBankAccountBalance = 100; // Account opened with 100 do\
2 llar
3 myBankAccountBalance = myBankAccountBalance + 200; // Deposit 200 dollar
4 myBankAccountBalance = myBankAccountBalance - 50; // Withdraw 50 dollar
5 console.log(myBankAccountBalance); // Check account balance
6 // -> 250
Besides var, keyword const and let also allow you to define variables. Both of these twokeywords are newly supported in ES6 standard. letwill be introducedwhenwe talk aboutFunction and scope later. For const, it’s used for the case you don’t want your variable tobe changed to point to another value after it’s first assigned.
Notes: keyword const and let are introduced in ES6. In order to use it, you need to putthe String 'use strict' in the first line of the JS file. In the future, please put this Stringin all of your JS files.
Program Structure and Flow Control 17
1 const notChangable = 1;
2 notChangable = 2;
3 console.log(notChangable);
4 // -> 1
Convention
There are some rules need to be followed when we choose a variable name:
1. Cannot use the reserved words, such as var.2. Cannot contain spaces.3. Cannot start with number, like 123abc.4. Cannot contain punctuation except characters $ or _.5. It’s case sensitive.
Notes: The full list of keywords and reserved words below are pretty long, and you donotneed tomemorize all of them at once. You will learnmost of them through out this book.
break case catch class const continue debugger default delete do else enumexport extends false finally for function if implements import in instanceofinterface let new null package private protected public return static superswitch this throw true try typeof var void while with yield
Some general conventions are formed for naming variables through out the years ofprogramming history. Two commonly used in naming variables are as below.
1. Use camelCase1 or snake_case2 consistently in your program.2. Constants normally use capital letters, such as PI.
Actually, many companies have formed a complete style guides on different aspectsin JavaScript beside naming variables, such as Google3, Mozilla4, Airbnb5, etc. To be aqualified programmer, writing code with good style is a must. If you are just learning byyourself now, you can simply pick one above and refer to corresponding sections whenyou learn different chapters in this book. If you are in company already, you may askfrom senior staffs for your company’s version.
1https://en.wikipedia.org/wiki/CamelCase2https://en.wikipedia.org/wiki/Snake_case3https://google.github.io/styleguide/javascriptguide.xml4https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style5https://github.com/airbnb/javascript
Program Structure and Flow Control 18
Refer, not contain
The relationship between the variable and the value behind it can be imagine as the keyto a box. It’s NOT that the variable contains the value in it. A variable can be reassignedto another value, like a key can be reformed for another box. But the consequence of itis that the original value (box) might have no variable (key) to point to. The value in thebox, when no variable pointing to, would be recycled so that your computer’s memorycan be freed up for other usage. This is called Garbage Collection and it is automaticallydone. Another scenario is that we can produce multiple keys to point to the same box.
1 var color = 'Blue';
2 console.log(color);
3 // -> Blue
4
5 color = 'Green'; // Now the box with value 'Blue' has no key pointing to it.
6 console.log(color); // It's NOT that the box with 'Blue' value is replaced by 'G\
7 reen'
8 // -> Green
9
10 var colour = color; // Now there is one more key pointing to box with value 'Gre\
11 en'.
12 console.log(colour)
13 // -> Green
Let’s see one more example:
1 var a = 100
2 var b = a
3
4 a -= 50
5
6 console.log(a) // -> 50
7 console.log(b) // -> 100
Default value
When we define a variable but not assign a value right away, the key points to no box.Now if you check the value of the variable, it tells you undefined.
Program Structure and Flow Control 19
1 var nameOfNewBornBaby;
2 console.log(nameOfNewBornBaby);
3 // -> undefined
Dynamic nature
In JavaScript, you can freely assign any value to a variable. The key can first point to aString type value. And then you can reassign it to point to a Number or Boolean typevalue and there is no error or warning about it. This is because JavaScript is a dynamicprogramming language6. However, it’s NOT a good practice to change a variable to pointto different value. And we should name our variable better to help you infer its usageand data type.
When you need to check a data type of the value one variable points to, you can usetypeof operator. It returns a String value indicating what type it is so that you can usecomparison operators === or !== to perform your logic accordingly based on appropriatedata type.
1 var whoAmI;
2 console.log(typeof whoAmI);
3 // -> undefined
3.3 Run program file in Node.js
After we learn control flow, wewill write longer andmore complex program havingmanystatements. It is not convenient to directly type statements in Node.js console anymore.Actually, you can provide a file containing all the statements for Node.js to execute. Thereare just two steps required:
1. Put all your JavaScript statements in a file with .js suffix, such as program.js2. Execute command node program.js in the command line from the path where the
file is located.
Sample program.js file can contain just one line as below:
1 console.log('Program in file is executed');
6https://en.wikipedia.org/wiki/Dynamic_programming_language
Program Structure and Flow Control 20
Node.js Execute JS File
3.4 Debug program file in Node.js
Notes: This section should not be difficult if you can get the node-inspector installedsuccessfully. And this section will not affect your JavaScript learning if you skip it. Youcan simply use console.log to print out anything anywhere to debug as well.
As the program grows, we often need to debug the program which means we canexamine the statements to be executed one by one and check what value is assignedto each variable during execution. Unbelievable? There are just three steps required tolearn this time-frozen magic.
1. Install Google Chrome7 browser2. Type command npm install node-inspector -g in command line and press Enter
(Not in the Node.js console).3. Type command node-debug program.js in the command line and press Enter.
Before try it out, let’s change the above program.js to be less silly as below. This programexpects you to pass a name into it so that it can print out a complete salut message. Theprocess.argv helps you to get everything you input in command line when running theprogram with Node.js. Just memorize this usage first.
1 // Leave an empty line at the beginning of the file to display better in debugger
2 var helloMessage = 'Hello, ';
3 var args = process.argv;
4 console.log(helloMessage + args[2]);
After typing in the command as below screencap, you will see the Chrome launches withyour JS file highlighted on first line. Also, you can find that there is a “Scope Variables”sectionwhich shows the “Local” variables there. The two variables helloMessage and argsare both undefined as the program is not run yet. The two buttons highlighted with redrectangle are what we need to pay attention now. The left one is to run the program fromcurrent highlighted line until it reaches a breakpoint, which is a pause signal; the rightone is to run current highlighted statement only and then pause on next statement.
7https://www.google.com/chrome/browser/desktop/index.html
Program Structure and Flow Control 21
Node.js Debug Start
If you use your mouse to click on the line number 4, it creates a breakpoint there. Allthe breakpoints are shown in the “Breakpoint” section where I highlighted with bluerectangle.
Node.js Debug Breakpoint
Now if you click the button on the left mentioned above, the execution will stop at thebreakpoint. At this moment, you can see that the value of helloMessage and args areassigned with actual value.
Program Structure and Flow Control 22
Node.js Debug Step
When you click the button on the right mentioned above, it runs the highlightedstatement only. You can find out the message is printed out in the Chrome console.
Program Structure and Flow Control 23
Node.js Debug End
3.5 Control Flow
When reading an article, we normally read from top to bottom, sentence after sentencesequentially. Similarly, that is also the basic flow that statements are executed inside aprogram. But more diverse ways are supported in programming so that we can instructthe computer to do more complex tasks.
Conditional - if
The if keyword allows us to control some statements to be executed under particularconditions only. For example, we want our program to verify the digit passed fromcommand line is even number (multiple of 2) or not. If it is, it prints out a message.We can write the program as below. Then if we run the program as node program.js 2, itprints out the message.
Program Structure and Flow Control 24
1 var args = process.argv;
2 var digit = Number(args[2]); // Converts the input parameter to number
3
4 if (digit % 2 === 0) {
5 console.log('Digit ' + digit + ' is even number.');
6 }
Notes: The curly braces {} is used to enclose statements to form a code block. We canput multiple statements inside it. The {} is not necessary here because there is just onestatement to be executed. However, it’s considered to be good practice for consistencyand avoid introducing bug when adding/removing statements in the future.
When our input digit is not an even number, the program does nothing. Would it bemoreuser-friendly to print something else to let user knows what happened? else keywordallow us to specify the otherwise execution path when the if condition is not met.
1 var args = process.argv;
2 var digit = Number(args[2]);
3
4 if (digit % 2 === 0) {
5 console.log('Digit ' + digit + ' is even number.');
6 } else {
7 console.log('Digit ' + digit + ' is odd number.');
8 }
Actually, amulti-path execution flow can be built by combining unlimited number of elseif into it.
1 var args = process.argv;
2 var digit = Number(args[2]);
3
4 if (digit % 5 === 0) {
5 console.log('Digit ' + digit + ' is multiple of 5.');
6 } else if (digit % 3 === 0) {
7 console.log('Digit ' + digit + ' is multiple of 3.');
8 } else if (digit % 2 === 0) {
9 console.log('Digit ' + digit + ' is multiple of 2.');
10 } else {
11 console.log('Digit ' + digit + ' is confusing me.')
12 }
Program Structure and Flow Control 25
Conditional - switch
JavaScript provides another keyword switch for conditional flow that supports multipleexecution path. We can use the switch keyword to simulate the above even and oddnumber example as below, plus fixing a bug when you don’t provide a digit.
1 var args = process.argv;
2 var digit = Number(args[2]);
3
4 switch (digit % 2) {
5 case 0:
6 console.log('Digit ' + digit + ' is even number.');
7 break;
8 case 1:
9 console.log('Digit ' + digit + ' is odd number.');
10 break;
11 default:
12 console.log('What did you gave me to reach here?');
13 break;
14 }
15
16 console.log('Tough decision is made.');
switch evaluate the value of the expression digit % 2. Each value followed the case
keyword is used to strictly compare (using ===) with the expression value. If there isno case matched, the statements under default clause are executed. When the break
keyword is met, the execution breaks out of the switch and continues the statementafter it (“Tough decision is made.” will be printed).
Some key points to be noted:
1. If there are multiple cases matched, which we shouldn’t do, the statements belowthe first case matched will be executed.
2. If the break statement is omitted, fall-through happens. That means the programwill not stop and continue to execute the statements under other cases. We shouldtry to avoid this usage which easily causes bug.
3. default clause is optional and by convention is the last clause but it does not needto be.
4. Considering efficiency, frequently met case should be arranged at the beginning.
Program Structure and Flow Control 26
Loop - while
Reducing repeated tasks is an advantage of computer and one of the basic feature inalmost every programming language. Not like workout, repeating task doesn’t meandoing exactly the same thing many times. For example, if we want to know the sum ofthe number from 1 to 100, doing addition on different digits is also a form of repeatedtasks.
Loop in programming language is used to address this issue. Without it, writing programas below makes no difference than using a calculator. But for the god’s sake, how muchtime and space it needs to take?
1 1 + 2 + 3 + 4 + 5 + ... // Where would be the end?
JavaScript provides a while keyword to indicate the code block for loop. Below statementscan achieve the same purpose above with greater brevity.
1 var sum = 0;
2 var currentNumber = 1;
3 while (currentNumber <= 100) {
4 sum = sum + currentNumber;
5 currentNumber = currentNumber + 1;
6 }
7 console.log(sum);
8 // -> 5050
It is not that difficult to understand if you take it like reading an article: While current-
Number is smaller or equal to 100, plus it to the sum and plus 1 to currentNumber. WhencurrentNumber does not meet above condition anymore, loop stops and the programprints out the sum.
Let’s make slight change in order to have greater brevity.
1 var sum = 0, currentNumber = 1;
2 while (currentNumber <= 100) {
3 sum += currentNumber;
4 currentNumber += 1;
5 }
6 console.log(sum);
1. Using var to declare variables can be combined into one line using a comma.
Program Structure and Flow Control 27
2. sum = sum + currentNumber can be replaced to sum += currentNumber and similarlycurrentNumber = currentNumber + 1 can be replaced with currentNumber += 1. It’snot easy to understand at the beginning. For this kind of self modification, theoperation and assignment can be combined with such short form. This rule appliesto *, -, /, % as well. If the variable self increase or decrease one, there is anothermore concise form, ++ and --.
1 var sum = 0, currentNumber = 1;
2 while (currentNumber <= 100) {
3 sum += currentNumber;
4 currentNumber++;
5 }
6 console.log(sum);
You may not know, the ++ and -- can even be placed before the variable. But its behaviorhas a little bit difference. Please check below two samples which produce the same resultand find out the minor difference.
1 var sum = 0, currentNumber = 1;
2 while (currentNumber <= 100) {
3 sum += currentNumber++;
4 }
5 console.log(sum);
6
7 var sum = 0, currentNumber = 0; // Initial value need to be 0.
8 while (currentNumber < 100) { // currentNumber cannot be equal to 100 here.
9 sum += ++currentNumber;
10 }
11 console.log(sum);
When the ++ is placed after the variable, the behavior is normal. It means the value ofthe variable, currentNumber here, is used in the outer statement, sum += currentNumber
first and after that, the currentNumber self-increase one. But if the ++ is placed before thevariable, the behavior changes. It means the currentNumber self-increase one first, andthen its value is used in the outer statement.
I am not intent to twist your mind here. You don’t need to force yourself to understandthe different usage of ++ at this moment. You just have to keep in mind that when yousee it, be careful. After written the code in above form, although it’s shorter, it’s not thateasy to understand any more. The intention here is to let you know that purely pursuingshorter code using some fancy language feature is not true brevity.
while loop actually still have one more form, do ... while. If some task has to be doneat least once, you can use this style.
Program Structure and Flow Control 28
1 var sum = 0, currentNumber = 1;
2
3 do {
4 sum += currentNumber;
5 currentNumber++;
6 } while (currentNumber <= 100);
7
8 console.log(sum);
Loop - for
for is another keyword can be used to express a loop statement. Please see belowrewritten sample.
1 var sum = 0;
2 for (var currentNumber = 1; currentNumber <= 100; currentNumber++) {
3 sum += currentNumber;
4 }
5 console.log(sum);
There are three statements inside the parentheses ():
1. The leftmost one is called initialization. Actually you can put the sum = 0 part thereas well, such as var sum = 0, currentNumber = 1. It’s evaluated once.
2. The middle one is called condition. It’s evaluated before each loop iteration. If theexpression is evaluated to false, the code block inside will not be executed.
3. The rightmost one is called final-expression. It’s evaluated at the end of each loopiteration, before the next evaluation of condition. Hence, it’s generally used toupdate the variable used in condition.
The above three parts are not mandatory and you can put the initialization before for,such as in the same line of initializing variable sum. And you can put the condition andfinal-expression part inside the {}. But if you do so, does it just like a while loop? Thesethree parts are common pattern in a loop. The major advantage of for loop over whileloop is that it groups all state control statements at the beginning for brevity.
Loop Interruption - break
Sometimes, we want to stop the loop before the loop’s condition in for and while to beevaluated as false. This is very useful when we are inspecting a bunch of things and wantto skip the rest after we find out the one we need.
Below is the rewritten sample of the above while and for loop sample on calculating sum.
Program Structure and Flow Control 29
1 // Rewritten while sample
2 var sum = 0, currentNumber = 1;
3 while (true) {
4 sum += currentNumber;
5 currentNumber++;
6
7 if (currentNumber > 100) {
8 break;
9 }
10 }
11 console.log(sum);
12
13
14 // Rewritten for sample
15 var sum = 0;
16 for (var currentNumber = 1; ; currentNumber++) {
17 if (currentNumber > 100) {
18 break;
19 }
20 sum += currentNumber;
21 }
22 console.log(sum);
Loop Interruption - continue
There are scenarios that we just need to skip some iteration inside a loop but notbreaking out the whole loop like above. For example, I just want to print out the evennumber from 1 to 100. The program can be written as below.
1 var currentNumber = 0;
2 while (currentNumber <= 10) {
3 if (currentNumber % 2 == 0) {
4 console.log(currentNumber);
5 }
6
7 currentNumber++;
8 }
9
10 for (var currentNumber = 0; currentNumber <= 10; currentNumber++) {
11 if (currentNumber % 2 == 0) {
12 console.log(currentNumber);
13 }
14 }
Program Structure and Flow Control 30
But we can also use a continue statement to explicitly skip a particular loop iteration.
1 var currentNumber = -1;
2 while (currentNumber <= 10) {
3 currentNumber++;
4 if (currentNumber % 2 == 1) {
5 continue;
6 }
7
8 console.log(currentNumber);
9 }
10
11 for (var currentNumber = 0; currentNumber <= 10; currentNumber++) {
12 if (currentNumber % 2 == 1) {
13 continue;
14 }
15
16 console.log(currentNumber);
17 }
So, when should we choose continue to explicitly skip an iteration against judging theopposite condition and enclose the statements to execute? It might be just a matter oftaste or depend on how many statements actually need to execute under the condition.If there aremany statements instead of just an console.log, the code can bemore brevityif you use continue in the beginning of the loop so that you don’t need to placemany linesof statements inside the curly braces and indent too many levels.
However, make sure the continue statement don’t skip the statements that changethe condition. Else, the loop can be turned into an infinite loop. For example, thecurrentNumber++; in above while loop cannot be placed at the end of the code block.
3.6 Comments
I have mentioned previously that words after two slashes // are a comment and it doesnot affect program running result. That usage is for a one-liner comment. If we need towrite multiple sentences with line break in comments, we can put those words between/* and */.
Program Structure and Flow Control 31
1 /*
2 My birthday is 21st Jun.
3
4 That is why I decided to set the price of this book to be
5 maximum 6.21 and minimum 1.26 dollar.
6 */
7 var originOfPrice = 621;
3.7 Exercises
Triangle
Please write a program to accept a numeric parameter from command line and print outa triangle. Below is an example of triangle when input parameter is 5:
1 #
2 ##
3 ###
4 ####
5 #####
Hints: process.argv, console.log, Loop, String concatenation
Holed triangle
Below is also an example of triangle with input parameter of 5. Your task is still acceptinga number from command line and print out the triangle accordingly.
1 #
2 # #
3 # # #
4 # # # #
5 # # # # #
Hints: process.argv, console.log, Loop, if, String concatenation
Program Structure and Flow Control 32
Bing! Go! Bingo! Boom!
Write a program to accept a numeric parameter from command line. Make a loop from1 to 100. Check each number and see if it’s divisible by the parameter. If it’s, print out thenumber followed by “: Bing!”. Check each number and see if it’s divisible by the numberwhich equals to the parameter plus 1. If it’s, print out the number followed by “: Go!”.If the number fulfill both rules above, print out the number first, and then followed by“: Bingo!”. If the number is divisible by (parameter * (parameter + 1) * (parameter + 2)),print out the number and followed by “: Boom!” and break out of the loop. Below is theexample if you input 2 as parameter.
1 2: Bing!
2 3: Go!
3 4: Bing!
4 6: Bingo!
5 ...
6 24: Boom!
Hints: process.argv, console.log, Loop, if, String concatenation, break
4. FunctionIt is better to have 100 functions operate on one data structure than 10functions on 10 data structures.– Alan J. Perlis
As wementioned before, function is also one kind of data type in JavaScript. It representsa piece of executable code which includes some statements. But why do we need to wrapsome statements into a function?
For example, if we want to calculate the absolute value of provided numbers fromcommand line, we might write some code similar as below:
1 var args = process.argv;
2
3 console.log(args[2] >= 0 ? args[2] : -args[2]);
4 console.log(args[3] >= 0 ? args[3] : -args[3]);
5 console.log(args[4] >= 0 ? args[4] : -args[4]);
6 ...
What are the problems here? What can be improved?
1. Repetition As you can see, the expression args[2] >= 0 ? args[2] : -args[2] re-peats several times and the difference is just the variable used in it. Oncewewant tochange the way we calculate absolute value, we have to change all the occurences.
2. Readability Even though the single expression args[2] >= 0 ? args[2] : -args[2]
is quite simple already, it’s still not obvious at first glance. What if we can give it aname, like getAbsoluteValue? Would it be more readable?
Function is a very important concept, Abstraction, in every programming language. Ithides the implementation detail and allows us to use more expressive vocabularies topresent what a program intends to accomplish.
It’s a very good practice to write clear and concise functions, and compose them togetherto accomplish a great thing instead of writing a single large one. Some companies mighteven have a hard rule to enforce the max length of a function, such as 20 lines.
Let’s see how it looks like and why it’s indispensable in every programming language.
Function 34
4.1 Definition
Expression
The first approach to define a Function is like declaring a variable and called function
expression. The above program can be revised as below if we define a getAbosoluteValue
function.
1 var getAbsoluteValue = function(value) {
2 return value >= 0 ? value : -value;
3 }
4
5 var args = process.argv;
6
7 console.log(getAbsoluteValue(args[2]));
8 console.log(getAbsoluteValue(args[3]));
9 console.log(getAbsoluteValue(args[4]));
10 ...
Function is defined by using a function keyword and () and {} followed.
1. Zero to many parameters are wrapped inside the (). The function getAbsoluteValue
above just have one parameter named value. If you imagine the function as anoven, parameters are the raw materials you put inside it.
2. All the statements wrapped inside the {} is function body. The {} is mandatory evenif you have only one statement as above.
3. Optional return statement can be included in function body. The function getAb-
soluteValue returns the absolute value of the number. But we can also put theconsole.log inside the function so that there is no return statement in it. But in thiscase, the function actually return an undefined value. When the program executioncome to the return statement, the rest of the statements, if there is any, in thefunction body will be ignored.
Notes: The getAbsoluteValue is renamed to printAbsoluteValue to better represent whatit really does.
Function 35
1 var printAbsoluteValue = function(value) {
2 console.log(value >= 0 ? value : -value);
3 }
4
5 var args = process.argv;
6
7 printAbsoluteValue(args[2]);
8 printAbsoluteValue(args[3]);
9 printAbsoluteValue(args[4]);
10 ...
For this style of function definition, the function is like other normal variables. You canassign it to another variablewith different names (multiple keys pointing to the samebox,remember?); print it out; pass it to another function, and so on. In Chapter 6, when wetalk about Functional Programming and Higher Order function, you will see how amazingon passing function as value.
Declaration
Another approach to define a function is called function declaration. Its syntax is similarto expression but you put the name after the function keyword and no need to assign itto another variable. Above example can be revised as below.
1 function printAbsoluteValue(value) {
2 console.log(value >= 0 ? value : -value);
3 }
4
5 var args = process.argv;
6
7 printAbsoluteValue(args[2]);
8 printAbsoluteValue(args[3]);
9 printAbsoluteValue(args[4]);
10 ...
The syntax is a little bit shorter than the first one, but the dramatic difference is as below.
Function 36
1 var args = process.argv;
2
3 printAbsoluteValue(args[2]);
4
5 function printAbsoluteValue(value) {
6 console.log(value >= 0 ? value : -value);
7 }
8 ...
You see, the function can be called even before it’s declared. This is called Hoisting. Wewill talk about it more when introducing the scope concept later in this chapter.
4.2 Parameters
Optional Parameters
As I mentioned in last chapter, JavaScript is a dynamic programming language. Here isanother dynamic area that when you call a function. You are not necessary to pass asmany as parameters as it’s defined in it. For example:
1 function magicLabor(normalPay, tips) {
2 var labor = normalPay;
3 var magicPower = tips || 0
4
5 return labor + magicPower * 2
6 }
7
8 magicLabor(10, 5) // -> 20
9 magicLabor(20) // -> 20
If you place some optional parameters at the end of the parameter list, you can treatthat value as the customization part of your function. When you call the function withoutspecifying that parameter, the program can use a pre-defined value within the function.
Of course, you can pass more parameters to the function as it’s defined. It has no harmbut just looks a little silly if those parameters are not intended to be used in the function.
Arguments Object
Actually, there exists one very special way to get all the parameters passed to a functioneven though you define NO parameters in the function signature. This is a very powerfulbullet. It’s the arguments object. It is only availabe within the function, and you can inspectits content like below.
Function 37
1 function whatWasThrownToMe() {
2 for (var i = 0; i < arguments.length; i++) {
3 console.log(arguments[i])
4 }
5 }
6
7 whatWasThrownToMe(1)
8 // -> 1
9
10 whatWasThrownToMe('A', 'B')
11 // -> A
12 // -> B
Parameters are keys, not boxes
The value of the parameters are passed from the outside world to the function. Butremember, parameters are just variable. The parameters you refer within the functionare just another copies of the keys, not the boxes containing the value. That meanswhen you reassign another value to the parameter, it DOES NOT change the outervariable. Let’s see below example:
1 var bankAccountBalance = 100
2
3 function stealMoney(balance) {
4 balance -= 50
5 }
6
7 console.log(bankAccountBalance)
8 // -> 100
4.3 Scopes
Scope is the concept regarding the visibility of the variables. Let’s start the explanationwith below example.
Function 38
1 // Usage of 'use strict':
2 // 1. Allows the usage of `let`
3 // 2. Forbid using variable `powerLeaking` before declaring it.
4
5 'use strict'
6
7 var sunLightPower = 100;
8
9 function electricPaybackFromMyHouse(outerElectric) {
10 var normalConsumption = 80;
11 var fuelElectricGenerator = 50;
12 var sunlightElectricGenerator = sunLightPower / 4;
13
14 console.log('Checking diff: ' + diff); // -> Checking diff: undefined
15
16 function addKidsPowerConsumption() {
17 var kidsCount = 3;
18
19 for (let i = 1; i <= kidsCount; i++) {
20 normalConsumption += 10;
21 }
22 console.log('Checking i out of for loop: ' + typeof i);
23 // -> Checking i out of for loop: undefined
24 }
25
26 addKidsPowerConsumption();
27 console.log('Checking kidsCount out of function: ' + typeof kidsCount);
28 // -> Checking kidsCount out of function: undefined
29
30 var diff = fuelElectricGenerator + sunlightElectricGenerator
31 - normalConsumption; // Code wrapping here is acceptable
32
33 if (outerElectric) {
34 diff = outerElectric + normalConsumption;
35 }
36
37 // powerLeaking = 10;
38
39 return diff > 0 ? diff : 0;
40 }
41
42 var paybackPower = electricPaybackFromMyHouse(80);
Function 39
43
44 // console.log(powerLeaking); // -> 10
45 console.log('Checking normalConsumption out of function: ' + typeof normalConsum\
46 ption);
47 // Checking normalConsumption out of function: undefined
48 console.log('Checking addKidsPowerConsumption out of function: ' + typeof addKid\
49 sPowerConsumption);
50 // Checking addKidsPowerConsumption out of function: undefined
OK, don’t be panic after seeing such a long function. Follow my explanation steps belowto check one by one. Imagine above code is in a JavaScript file that executed by node.
1. sunLightPower is in the outermost, global, scope. It can be seen within the functionelectricPaybackFromMyHouse.
2. normalConsumption is within the local scope of function electricPaybackFromMyHouse
and can be seen within function addKidsPowerConsumption.3. Similarly, kidsCount is only within the local scope of function addKidsPowerCon-
sumption and addKidsPowerConsumption is only within the local scope of functionelectricPaybackFromMyHouse.
4. i is declared using let. It is new in ES6 and allow us to define a block scope variable.Hence, it’s not available outside of the for statement. But if you switch the let tovar, you can print out the value of i.
5. The commented powerLeaking related statements can be uncommented if youremove 'use strict' statements and the addKidsPowerConsumption part. The pointI want to illustrate is that when we declare a variable but accidently miss the var
keyword, the variable becomes global scope. This is dangerous because it pollutesthe outer environment and might cause unexpected bug.
6. Why diff can be directly used instead of using typeof to check about? Rememberthe Hoistingmentioned before? The function or variables declared will be lifted upto the beginning of the scope (locally within a function or globally) it belongs to.
This is called lexical scoping in JavaScript. The scope of the variable is determined by itslocation in source code and so it’s lexical. In short, the nested functions can refer to theouter scope enclosing it. The outer scope cannot see the inner scope.
4.4 Recursion
By definition in Wiki1:
1https://en.wikipedia.org/wiki/Recursion_
Function 40
Recursion in computer science is a method where the solution to a problemdepends on solutions to smaller instances of the same problem
It is a little bit abstract at first glance. Let’s learn about it through an example. Supposedyou are required to calculate the multiple of the whole number from 1 to 10, that is 1 +
2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10. Of course we can write a loop to do that like:
1 function additionOfWholeNumbersUpTo(max) {
2 var sum = 1;
3
4 for (var i = 1; i <= max; i++) {
5 sum += i;
6 }
7
8 return sum;
9 }
10
11 console.log(additionOfWholeNumbersUpTo(10))
12 // -> 55
How to break this problem down to smaller intances of the same problem? First, we canrevise the expression into this format:
((((((((((1) + 2) + 3) + 4) + 5) + 6) + 7) + 8) + 9) + 10)
I know this forat is even harder to understand at first glance, but actually it should helpsyou see the pattern more clearly. Speaking in human lanuage, this expression meansthat the additionOfWholeNumbersUpTo(10) actually equals to 10 + additionOfWholeNumber-
sUpTo(9) and the additionOfWholeNumbersUpTo(9) equals to 9 + additionOfWholeNumber-
sUpTo(8) and so on and on. Hence, the problem can be abstracted as additionOfWholeNum-bersUpTo(n) can be calculated by n + additionOfWholeNumbersUpTo(n - 1). It’s referring toitself recursively with such fixed pattern. Previous looping can be rewritten in the formof recursion as:
1 function additionOfWholeNumbersUpTo(max) {
2 return max === 1 ? max : max + additionOfWholeNumbersUpTo(max - 1)
3 }
4
5 console.log(additionOfWholeNumbersUpTo(10))
6 // -> 55
Recursion is a special form of loop. Most of the case if not all, we can revise a programin loop form into recursive form or vice versa. But to be very careful to specify the exitcondition when using Recursion as it’s less obvious than loop form. If the exit conditionis incorrect, it might turn to endless loop.
Function 41
4.5 Call Stack
When a pile of objects arranged together by putting one on another, it’s called a stack. InJavaScript, when a function is called, an execution context is created. This is a technicalterm defined in ECMAScript specification. You can simply imagine it’s some space/roomcreated to hold the current context including the executable code, value of variables, etc.
The stack always has at least one execution context at the bottom, it’s the global context.If the program executes to a function, then a new execution context is created for thisfunction and put (pushed) to the top of the stack. Then the currently active context isfor this function. When the function execution is completed, the context for it is taken(popped) out of the stack.
Let’s see how the execution context and the stack behaves when using the recursion tocalculate the Factorial2
1 function factorial(n) {
2 return n === 0 ? 1 : n * factorial(n - 1);
3 }
4
5 // factorial(10)
6 // => 3628800
7 // factorial(32768)
8 // => Uncaught RangeError: Maximum call stack size exceeded
Factorial Stack Change Flow
You can see that if we put a very small number 3 for the function and execute it, the sizeof the stack is four already. That is why if we put a very large number to the function, aJavaScript Error occurs. Basically if you see this error, it means there exists an endlessrecursion.
2https://en.wikipedia.org/wiki/Factorial
Function 42
4.6 Exercises
Any Sum
Define a function, sum, that accepts any number of number type parameters and returnthe sum of all the numbers.
Hints: arguments, Loop
Directory Printing
This exercise involves some of the knowledge introduced in next chapter. If the samplecode I provided cannot help you solve the problem, you can come back to this exerciseafter learning the next chapter about Object and Array.
Please implement a function to use the data provided here3 and print it out in belowformat. The data provided virtually represents the tree data structure of the directory inoperation system. Using recursion is a common way to traverse it.
1 // Program output
2 - [Root]
3 - [Sub-Dir A]
4 - [Sub-Sub-Dir C]
5 - [Sub-Sub-Sub-Dir D]
6 File D-1
7 File A-1
8 File A-2
9 - [Sub-Dir B]
10 File B-1
11 File Root-1
12 File Root-2
Helper Code:
3https://github.com/kenspirit/tasting-javascript-exercise/blob/master/04-function/directory-printing-fixture.js
Function 43
1 // You can check the type of the object through `xxx.type`
2 if (fileSystemObj.type === 'D') {
3 // Do whatever you need
4 }
5
6 // You can get the childs of the object through `xxx.childs`
7 var childObjs = fileSystemObj.childs
8
9 // Use `xxx.length` and loop to access each child
10 for (var i = 0; i < childObjs.length; i++) {
11 var child = childObjs[i]
12
13 // Do whatever you need, say print the name
14 console.log(child.name)
15 }
Hints: Recursion, Loop, console.log
5. Data StructureAlgorithms + Data Structures = Programs– book written by Niklaus Wirth
Data Structures are the ways we organize the data together for processing in program.Different structures have different ways of access.
For a stack of heavy boxes, the bottom one is laid down first and the top one is put last.While you have to take the one at the top first and the bottomone last. For a pile of bags ofcandy in auto machine, the first one put into the machine is taken first. These structuresare different although they are both accessed in sequence. Some other structures areaccessed by names or positions, like the books in the shelf.
5.1 Object
Primitive data types number, string, boolean are the fundamental data storage unit inJavaScript. However, they are so simple and limited that it’s difficult for us to purely usethem to represent the real world.
Remember I have ever used a variable to represent a bank acount balance? In reality,a bank acount at least consists two more information: account number and owner. Canwe group these two information together with balance instead of separately creating 3different variables?
Object in JavaScript comes to rescue. This data type helps us to organize related datatogether so that we can pass them around and process as a single unit. We caneven attach object with behaviors which is part of the concept of Object OrientedProgramming.
Properties
Properties are the characteristics of the object. Below object definition can be used tomodel the bank account object with three properties, the account number, owner andbalance.
Data Structure 45
1 var bankAccount = {
2 accountNumber: '000001',
3 owner: {
4 firstName: 'Ken',
5 lastName: 'Chen'
6 },
7 balance: 621
8 };
9
10 console.log(bankAccount.balance);
That is it. Defining an Object just need a pair of {} and specify zero to many propertiesin it. And the property value is not limited to primitive data type. The owner property isalso an object. When we want to access the property of the Object, we use the objectvariable, followed by a dot and the property name.
Now you know the meaning of arguments.length and console.log we used before. lengthproperty of the arguments object represents the number of arguments passed into thefunction. log is a behavior property of console object which we will talk soon below.
Property name actually is a String value. Quotes must be included if the property namecontains non-alphanumeric characters. But using special characters as property namesis not recommended.
1 var bankAccount = {
2 "#": '000001',
3 "owner": 'Ken Chen'
4 };
Method
Object can also have behaviors. A bank account should allow us to check balance, depositand withdraw. How can we assign behaviors to object then? If the property value of theobject is a function, we call it the method of the object. And the method is the behavior ofthe object.
Data Structure 46
1 var bankAccount = {
2 accountNumber: '000001',
3 owner: {
4 firstName: 'Ken',
5 lastName: 'Chen'
6 },
7 balance: 621,
8 checkBalance: function() {
9 return this.balance;
10 },
11 deposit: function(amount) {
12 if (amount <= 0) {
13 console.log('Invalid deposit amount');
14 return;
15 }
16
17 this.balance += amount;
18 },
19 withdraw: function(amount) {
20 if (amount <= 0) {
21 console.log('Invalid withdraw amount');
22 return;
23 }
24 if (amount > this.balance) {
25 console.log('Not enough balance.');
26 return;
27 }
28
29 this.balance -= amount;
30 }
31 };
32
33 bankAccount.withdraw(21);
34 console.log(bankAccount.checkBalance());
35 // -> 600
There is a special usage here, this. The this keyword here refers to object itself so thatwe can use it to reference the other object properties in method.
The special this
We have seen the normal usage of this in object method above. However, the behaviorof below code is very confusing for many people and often used as interview question
Data Structure 47
as well.
1 balance = 100;
2
3 var bankAccount = {
4 balance: 200,
5 checkBalance: function() {
6 return this.balance;
7 }
8 }
9
10 var checkBalance = bankAccount.checkBalance;
11 console.log(checkBalance());
12 // -> 100
13 console.log(bankAccount.checkBalance());
14 // -> 200
Although the checkBalance and bankAccount.checkBalance are pointing at the same func-tion (two keys to the same box), executing the function in different forms actually refersto different objects. The calling on checkBalance() refers to the global object and so itsbalance property is the one defined globally (Note that there is no var in front of it) outsideof the function with value 100. If there is no such variable, it prints out undefined instead.
Actually, we can provide the desired object to a function as the object this point to duringfunction execution.
1 function speak() {
2 console.log(this.sound)
3 }
4
5 var dog = {
6 sound: 'Woof',
7 speak: speak
8 }
9
10 var cat = {
11 sound: 'Meow'
12 }
13
14 dog.speak()
15 // -> 'Woof'
16
17 var catSpeak = speak.bind(cat)
Data Structure 48
18 catSpeak()
19 // -> 'Meow'
20
21 speak.call(cat)
22 // -> 'Meow'
23
24 speak.apply(cat)
25 // -> 'Meow'
The method bind, call, apply can be used to specify the object to be used as this infunction. bind method returns a new function that it remembers the object to be used.While call and apply must be called immediately on the object. More explanation andusage will be discussed in chapter 6 when we learn functional programming.
Enumerate Properties
What if someone else pass us an object but we don’t know what properties it contain? Isthere anyway we can inspect the properties it have? The for ... in statement can helpus to check.
1 var bankAccount = {
2 accountNumber: '000001',
3 owner: {
4 firstName: 'Ken',
5 lastName: 'Chen'
6 },
7 balance: 621,
8 checkBalance: function() {
9 return this.balance;
10 }
11 };
12
13 for (var key in bankAccount) {
14 console.log('Property name: ' + key);
15 console.log('Property value: ' + bankAccount[key]);
16 }
17
18 // -> Property name: accountNumber
19 // -> Property value: 000001
20 // -> Property name: owner
21 // -> Property value: [object Object]
22 // -> Property name: balance
Data Structure 49
23 // -> Property value: 621
24 // -> Property name: checkBalance
25 // -> Property value: function () {
26 // -> return this.balance;
27 // -> }
There are two special output value:
1. Object value of property owner is printed as [object Object] instead of a pretty printformat contains all its property.
2. Function value of property checkBalance is printed out the code
Why is that? Actually, when we use console.log to print out a string concatenated withan object (function is a special object), the internal toString() method, owned by everyobject, is called. Its behavior differs among different object types and so the valuesprinted out are different.
Mutability
Whenwe are playing around the primitive data types number, string or boolean, we cannotmutate the value. Reassignment is actually making the variable to point to a new value.Hence, those primitive data type are all immutable. But for object, it’s different.
1 var num = 2;
2
3 var obj = {
4 num: 4
5 };
6 var anotherObj = obj;
7
8 function mutate(num, obj) {
9 num = 8;
10 obj.num = 16;
11
12 obj = {num: 20};
13 }
14
15 mutate(num, obj);
16
17 console.log(num); // -> 2
18 console.log(obj.num); // -> 16
19 console.log(anotherObj.num); // -> 16
Data Structure 50
Reassignment to a variable with object value {num: 4} to another object {num: 20} doesn’tchange the original object. However, if the reassignment is performed on the propertyof the object, the object is changed. If there is other variables, anotherObj above, pointingto the same object, it also sees the change after modification.
5.2 Array
Object data type is good at grouping different values together if they belongs to a singleunit to better represent a real world object. However, it’s not suitable to represent abatch or collection of the data, such as a sequence of numbers.
To facilitate data processing on sequences of value, JavaScript provides a special datatype, called Array. Let’s see how it’s defined and used.
1 var sequenceOfNumbers = [1, 2, 3, 4, 5, 6];
2
3 console.log(sequenceOfNumbers); // -> [ 1, 2, 3, 4, 5, 6 ]
4 console.log(typeof sequenceOfNumbers); // -> object
5
6 for (var index = 0; index < sequenceOfNumbers.length; index++) {
7 console.log(sequenceOfNumbers[index]);
8 }
Array is defined by [] and we put items into it separated by comma. Actually, we haveseen Array beforewhenwe use process.argv to obtain values passed from command line.The length property indicate how many items in it. The numeric index is the position ofthe value in the array. But the numeric index starts from 0 instead of 1. It is a bit odd ifyou have no experience on other programming languages before.
As Array is also Object, when we use typeof operator to check on an array, it return object
as well. In order to detect whether a variable holds reference to an array, we need to useArray.isArray() to check.
Methods
Array is a widely used and important data type. Hence, there are many useful methodsprovided for Array in JavaScript. Some of them aremutation operations whichmeans thechange is applied on the array itself. While some of them are non-mutation operationswhich means the original array is not updated but the method returns the new value.
Non-Mutation Operations:
• indexOf and lastIndexOf can let you know a particular value’s position in Array.
Data Structure 51
• concat enables you to merge two Array together.• join concatenates all the items into a String.• slicemakes a shallow copy of the array by specifying a start index and an optionalend index.
1 var numbers = [6, 1, 2, 1];
2
3 console.log(numbers.indexOf(1)) // -> 1
4 console.log(numbers.lastIndexOf(1)) // -> 3
5
6 console.log(numbers.join(',')); // -> 6,1,2,1
7 console.log(numbers); // -> [6, 1, 2, 1]
8
9 console.log(numbers.concat([8, 2])); // -> [6, 1, 2, 1, 8, 2]
10 console.log(numbers); // -> [6, 1, 2, 1]
11
12 console.log(numbers.slice(1, 3)); // -> [1, 2]
Mutation Operations:
• push and pop allows you add a value to the end of the array or take it out.• unshift and shift allows you to add a value to the front of the array or take it out.• splice helps removing items from array.• sort and reverse, as the name suggested, sorts the array or reverse it.
1 var numbers = [6, 2, 1];
2
3 numbers.push(9);
4 console.log(numbers); // -> [6, 2, 1, 9]
5
6 console.log(numbers.pop()); // -> 9
7 console.log(numbers); // -> [6, 2, 1]
8
9 numbers.unshift(8);
10 console.log(numbers); // -> [8, 6, 2, 1]
11
12 console.log(numbers.shift()); // -> 8
13 console.log(numbers); // -> [6, 2, 1]
14
Data Structure 52
15 numbers.sort();
16 console.log(numbers); // -> [1, 2, 6]
17
18 numbers.reverse();
19 console.log(numbers); // -> [6, 2, 1]
20
21 // Remove 2 items starting from index 1
22 console.log(numbers.splice(1, 2)); // -> [2, 1]
23 console.log(numbers); // -> [6]
5.3 Map
Map is a common data structure in many programming languages. It have differentnames, like Dictionary, or Associative Array. It’s a collection of key value pair. Unlikenormal Array, whose value is retrieved through numeric index, Map value access is bykey. Each key appears only once in Map.
Object as Map
I am not sure if Object immediately pop up on your mind after above description. InJavaScript, Object is widely used as a Map with the property as the key. When we useObject as Map, dynamically adding or removing value is what we usually do instead ofspecifying it during creation.
1 var colors = {};
2
3 colors.red = 'Red';
4 colors['blue'] = 'Blue'; // another form
5
6 delete colors.blue;
You may wonder what the difference is between using dot and square bracket. Thisexample can clearly tells.
Data Structure 53
1 var colors = {
2 red: 'Red'
3 };
4
5 var colorToPick = 'red'
6 console.log(colors[colorToPick]);
7 // -> 'Red'
The square bracket usage on accessing object property allows you to dynamically accessany property based on a variable, whose value is resolved to be property name.
Native Map
In ES6, a new data type, Map, is introduced so that you don’t need to use Object tosimulate a Map. There are some benefits of using Map over Object:
1. The properties of Objectmust be String or Symbols (Symbol is not widely supportedand so not discussed in this book). But for Map, it can be anything.
2. It’s very easy to get the size from a Map, while you have to manually keep track ofit for Object.
Notes: Be aware of the for ... of usage here. It can only be used on iterable objects likeArray, Map, Set, etc.
1 function happy() {
2 console.log('Happy everyday.');
3 }
4
5 var map = new Map();
6
7 map.set(NaN, 'I am not a Number');
8 map.set(621, 'My birthday');
9 map.set(happy, 'Happy Function');
10
11 console.log(map.get(NaN)); // -> 'I am not a Number'
12 console.log(map.size); // -> 3
13
14 for (var key of map.keys()) {
15 console.log(key);
16 }
17 // -> NaN
Data Structure 54
18 // -> 621
19 // -> [Function: happy]
20
21 for (var value of map.values()) {
22 console.log(value);
23 }
24 // -> 'I am not a Number'
25 // -> 'My birthday'
26 // -> 'Happy Function'
5.4 Set
Set is similar to Array, but it has a very special feature. The values stored in it are unique.Even if you try adding same value (primitive value or object reference), it only keeps one.
1 var uniqueData = new Set();
2
3 uniqueData.add(621);
4 uniqueData.add('Ken');
5 uniqueData.add(621);
6 uniqueData.add({name: 'Tasting JavaScript'});
7
8 console.log(uniqueData.size); // -> 3
9
10 for (var value of uniqueData.values()) { // uniqueData.keys() has same effect
11 console.log(value);
12 }
13 // -> 621
14 // -> Ken
15 // -> { name: 'Tasting JavaScript' }
5.5 String
Interpolation
When we learned about String in chapter 2, we knew that + can be used as concatenationoperator to combine multiple strings together, especially there are some dynamic textswhose values are from variables.
In ES6, there is one more elegant way to do so. By using String interpolation, you justneed to define a text template, and the variable value can be automatically resolvedwithin it.
Data Structure 55
1 var name = {
2 firstName: 'Ken',
3 lastName: 'Chen'
4 };
5
6 var birthday = '21, Jun';
7
8 console.log(`I am ${name.firstName} ${name.lastName}. My birthday is ${birthday\
9 }.`);
10 // -> I am Ken Chen. My birthday is 21, Jun.
Notes: the String Interpolation is using backtick \‘ instead of quotes.
Methods
Besides concatenation, there are some common operations for String. As String isimmutable, all belowmethods do notmodify the original string but returns themassagednew value.
1. trim helps to remove all the whitespace characters (space, tab, etc.) and lineterminator characters (LF, CR, etc.) from both ends of a string.
2. toUpperCase and toLowerCase converts the string to all in upper case or lower case.3. split method splits a string into array based on some the seperator provided. If
you still remember, Array data type has counterpart method join to join an arrayinto String.
4. indexOf and lastIndexOf tells the index position of the first occurrence of thespecified sub-string in the string to search from. -1 indicates that sub-string is notfound.
5. charAt obtains the char in specified index from a string6. substring extracts a subset of a string between the starting index position and
optionally the end index.7. substr extracts a subset of a string beginning at the specified index position with
an optionally provided length of the sub-string.
Data Structure 56
1 var stringMassaged = '';
2
3 var stringWithSpace = ' I am too verbose. \t ';
4 stringMassaged = stringWithSpace.trim();
5
6 console.log(stringWithSpace.length); // -> 21
7 console.log(stringMassaged); // -> I am too verbose.
8 console.log(stringMassaged.length); // -> 17
9
10 var mixCases = 'mIx AnD mAtCh';
11 console.log(mixCases.toUpperCase()); // -> MIX AND MATCH
12 console.log(mixCases.toLowerCase()); // -> mix and match
13
14 var numbers = '1,2,3,4,5';
15 console.log(numbers.split(',')) // -> [ '1', '2', '3', '4', '5']
16
17 var sentence = 'I am not a great programmer; I am just a good programmer with gr\
18 eat habits. - Kent Beck';
19
20 console.log(sentence.indexOf('program')); // -> 17
21 console.log(sentence.lastIndexOf('programmer')); // -> 46
22 console.log(sentence.lastIndexOf('coder')); // -> -1
23
24 console.log('JavaScript'.charAt(4)); // -> S
25 console.log(sentence.substring(0, sentence.indexOf(';')))
26 // -> I am not a great programmer
27 console.log(sentence.substr(sentence.lastIndexOf('I'), 45))
28 // -> I am just a good programmer with great habits
5.6 Exercises
Month Name Resolver
Please write a function getMonthNamewhich takes a numericmonth value and then returnsthe month name based on the array of month data provided as below.
Data Structure 57
1 var months = [
2 {month: 1, name: 'Jan'}, {month: 2, name: 'Feb'},
3 {month: 3, name: 'Mar'}, {month: 4, name: 'Apr'},
4 {month: 5, name: 'May'}, {month: 6, name: 'Jun'},
5 {month: 7, name: 'Jul'}, {month: 8, name: 'Aug'},
6 {month: 9, name: 'Sep'}, {month: 10, name: 'Oct'},
7 {month: 11, name: 'Nov'}, {month: 12, name: 'Dec'}
8 ];
Array to Map
Sometimes, we need to convert an array to map in order to quickly lookup some value.For example, above month name resolver exercise requires us to loop through thearray and check which value matches the numeric month provided. However, if we takeadvantage of the concept of Object as Map, it’s easier to lookup the month name.
Please write a function arrayToMap(data, keyProperty, valueProperty) by taking threeparameters so that calling arrayToMap(months, 'month', 'name') can provide result asbelow. And also rewrite the getMonthName function to use it.
1 { '1': 'Jan',
2 '2': 'Feb',
3 '3': 'Mar',
4 '4': 'Apr',
5 '5': 'May',
6 '6': 'Jun',
7 '7': 'Jul',
8 '8': 'Aug',
9 '9': 'Sep',
10 '10': 'Oct',
11 '11': 'Nov',
12 '12': 'Dec' }
Matrix Convertor
There is no data type in JavaScript to exactly represent a matrix. As matrix is also acollection of data, many people use Array to represent a matrix.
If we directly use flat array [1, 2, 3, 4, 5, 6, 7, 8, 9], then there is no obvious signthat it’s matrix. Hence, the matrix structure must be represented somewhere else.
Another way to use array to represent a matrix is embedding array into array: [[1, 2,
3], [4, 5, 6], [7, 8, 9]].
Pleasewrite two functions: matrixArrayToMatrixObj(matrixArray) and matrixObjToMatrixAr-
ray(matrixObj). Their functionalities are as below:
Data Structure 58
1 var matrixObj = {
2 rows: 2,
3 columns: 4,
4 data: [1, 2, 3, 4, 5, 6, 7, 8]
5 };
6
7 var matrixArray = [[1, 2, 3, 4], [5, 6, 7, 8]];
8
9 // Should produce result in same structure as matrixObj
10 matrixArrayToMatrixObj(matrixArray);
11
12 // Should produce result in same structure as matrixArray
13 matrixObjToMatrixArray(matrixObj);
6. Object-orientedObject-oriented programming, whose essence is nothingmore than program-ming using data with associated behaviors, is a powerful idea. It truly is. Butit’s not always the best idea. � Sometimes data is just data and functions arejust functions. – Rob Pike
When entering the world of programming language, you will see many terms of pro-gramming paradigm, such as object-oriented programming, functional programming,declarative programming, imperative programming. Don’t let these words scare youaway. We just need to pick the most basic and important concepts at the beginning andyou will gradually see the difference especially when you learned different kinds of thelanguage.
The huge wave of hype for Object-oriented programming starts around 1990. OOPconcepts and design patterns became hottest interview questions for a long time. Somelanguages are considered “pure” OO language because everything in it is modeled as anobject, such as Smalltalk, Ruby, Scala. Some languages are not pure but mainly use OOparadigm, such as Java, Objective C, C++, C#.
OOP is not the only way to program but it’s good to know what it is and in which scenarioit’s best used.
6.1 Core concepts
Simply put, it’s based on “object” modeling. An object contains attributes and behaviors.The object model is easy for human to understand. For example, if we model the caras an object, it has attributes like color, number of seats, volume of oil tank, etc. Start,run and stop are its behavior. It’s composed of sub-objects too, a body, an engine, fourwheels, etc. The sub-objects can also have their own attributes and behaviors too.
Below are three core concepts of OOP.
• Encapsulation• Inheritance• Polymorphism
Object-oriented 60
6.2 Encapsulation
Encapsulation is usually used for describing below two notions.
1. Object structure that bundles attributes and behaviors together.2. Mechanism for restricting direct access to some of the object attributes.
If we want to model a book shelf in JavaScript, we can use an array and each object in itis a book as below.
1 var bookShelf = []
2
3 bookShelf.push({
4 name: 'Tasting JavaScript',
5 author: 'Ken Chen'
6 })
7 bookShelf.push({
8 name: 'Rework',
9 author: 'Jason Fried and David Heinemeier Hansson'
10 })
11 bookShelf.push({
12 name: 'Structure and Interpretation of Computer Programs',
13 author: 'Harold Abelson and Gerald Jay Sussman'
14 })
However, there are some issues here:
1. The bookShelf is an raw array and each book is just simple object. There is no specificobject type information with it.
2. The bookShelf can bemanipulated easily with any arraymethods. How can I restrictit to only have some specialized methods, like addBook, removeBook, etc.
These questions are what encapsulation can help to address.
Constructor
Constructor can help us to create a object instance for a particular object type, like atemplate or mold. Constructor is just a JavaScript function, but we call it differently witha new keyword. As convention, if a function is intended to be used as a constructor, itsname should be capitalized.We can define two Constructors BookShelf and Book as below.
Object-oriented 61
1 function BookShelf() {
2 this.books = []
3
4 this.addBook = function(book) {
5 this.books.push(book)
6 }
7
8 this.removeBook = function(name) {
9 for (var i = 0; i < this.books.length; i++) {
10 if (this.books[i].name === name) {
11 this.books.splice(i, 1)
12 break
13 }
14 }
15 }
16 }
17
18 function Book(name, author) {
19 this.name = name
20 this.author = author
21 }
22
23 var bookShelf = new BookShelf()
24 var book1 = new Book('Tasting JavaScript', 'Ken')
25 var book2 = new Book('Rework', 'Jason Fried and David Heinemeier Hansson')
26 var book3 = new Book(
27 'Structure and Interpretation of Computer Programs',
28 'Harold Abelson and Gerald Jay Sussman'
29 )
30
31 bookShelf.addBook(book1)
32 bookShelf.addBook(book2)
33 bookShelf.addBook(book3)
34 console.log(bookShelf.books.length)
35 // -> 3
36
37 bookShelf.removeBook('Tasting JavaScript')
38 console.log(bookShelf.books.length)
39 // -> 2
40
41 console.log(bookShelf instanceof BookShelf)
42 // -> true
Object-oriented 62
43 console.log(bookShelf)
44 // -> BookShelf {
45 // -> books:
46 // -> [ Book {
47 // -> name: 'Rework',
48 // -> author: 'Jason Fried and David Heinemeier Hansson' },
49 // -> Book {
50 // -> name: 'Structure and Interpretation of Computer Programs',
51 // -> author: 'Harold Abelson and Gerald Jay Sussman' } ],
52 // -> addBook: [Function],
53 // -> removeBook: [Function] }
There are a couple of advantages here as you can see:
1. It’s easier to see that there are two object type BookShelf and Book and whatattributes or behaviors each object type has.
2. Customized behaviors addBook and removeBook now can be supported.3. When we print out the object, it’s more user-friendly with the constructor name at
the beginning.
Here we also introduced a new operator instanceof which tells you whether an object isa particular type.
However, we can still freely access the books attribute of bookShelf and use the arraymethods to change it. How can we hide it from the outside? We can change the BookShelf
constructor as below.
1 function BookShelf() {
2 var books = [] // Define a local variable instead of using this
3
4 this.addBook = function(book) {
5 books.push(book)
6 }
7
8 this.removeBook = function(name) {
9 for (var i = 0; i < books.length; i++) {
10 if (books[i].name === name) {
11 books.splice(i, 1)
12 break
13 }
14 }
15 }
Object-oriented 63
16
17 this.bookCount = function() {
18 return books.length
19 }
20 }
21
22 // Omitted Book constructor and adding books' statements for brevity
23
24 console.log(bookShelf.books)
25 // -> undefined
26 console.log(bookShelf.bookCount())
27 // -> 3
Getter and Setter
Besides using constructor, there is one simpler way to implement encapsulation if youhave special logics on the object property, say it cannot be changed or is computed basedon other properties.
1 var person = {
2 firstName: 'Ken',
3 lastName: 'Chen',
4 get fullName() {
5 return this.firstName + ' ' + this.lastName
6 },
7 set fullName(fullName) {
8 var names = fullName.split(' ')
9
10 this.firstName = names[0]
11 this.lastName = names[1]
12 }
13 }
14
15 console.log(person.fullName)
16 // -> Ken Chen
17
18 person.fullName = 'Winnie Su'
19
20 console.log(person.firstName)
21 // -> Winnie
22 console.log(person.lastName)
23 // -> Su
Object-oriented 64
We can also use Object.definemethod to add getter and setter to existing object. But tobe noted that if we don’t provide a setter with the getter definition together, trying toupdate the property takes no effect.
1 var person = {
2 firstName: 'Ken',
3 lastName: 'Chen'
4 }
5
6 Object.defineProperty(person, 'fullName', {
7 get: function() {
8 return this.firstName + ' ' + this.lastName
9 }
10 })
11
12 console.log(person.fullName)
13 // -> Ken Chen
14
15 person.fullName = 'Winnie Su'
16 console.log(person.fullName)
17 // -> Ken Chen
Enumerable vs Non-enumerable
In chapter 5, we learned about how to enumerate the object properties by using for ...
in ... statement. Let’s see what we got on object person.
1 var person = {
2 firstName: 'Ken',
3 lastName: 'Chen'
4 }
5
6 Object.defineProperty(person, 'fullName', {
7 get: function() {
8 return this.firstName + ' ' + this.lastName
9 }
10 })
11
12 for (var i in person) {
13 console.log(i)
14 }
15 // -> firstName
16 // -> lastName
Object-oriented 65
Why the fullName is not listed? Actually, each property of the object have an enumerable
attribute which defines whether it should be listed when using for ... in ... loop. Thedefault value of that attribute is false if it’s not explicitly specified. We can set it as belowto make it enumerable.
1 var person = {
2 firstName: 'Ken',
3 lastName: 'Chen'
4 }
5
6 Object.defineProperty(person, 'fullName', {
7 get: function() {
8 return this.firstName + ' ' + this.lastName
9 },
10 enumerable: true
11 })
12
13 for (var i in person) {
14 console.log(i)
15 }
16 // -> firstName
17 // -> lastName
18 // -> fullName
6.3 Inheritance
Inheritance is that one object inherit same behaviors or attributes from another object.It’s a mechanism for code reuse and the inheritance relationship also forms a hierarchy.The IS-A relationship is often used by people to evaluate whether two objects have validinheritance relationship.
For example, cat is an animal; dog is also an animal. Hence, we can build an inheritancerelationship model from cat to animal and from dog to animal. Cat and dog objects arechild; animal object are parent in the inheritance hierarchy.
Prototype
In JavaScript, the root of the hierarchy is Object. Its toStringmethod is accessible for anyobject.
Object-oriented 66
1 var obj = {}
2
3 console.log(obj.toString)
4 // -> [Function: toString]
5 console.log(obj instanceof Object)
6 // -> true
How can we access a property which is not defined in the object? It’s through thePrototype Chain which implements the inheritance in JavaScript. Let’s have a visual ideaon how it looks like through below diagram and sample code.
JS prototype chain
1 function Bird(type) {
2 this.type = type
3 }
4
5 Bird.prototype.fly = function() {
6 console.log('Flying with two wings')
7 }
8
9 // Bird Instance
10 var hummingbird = new Bird('Hummingbird')
11 var swan = new Bird('Swan')
12
13 console.log(hummingbird.type)
14 // -> Hummingbird
15 console.log(swan.type)
16 // -> Swan
17
18 // Search up the chain and find in Bird.prototype
Object-oriented 67
19 hummingbird.fly()
20 // -> Flying with two wings
21
22 console.log(hummingbird.fly === swan.fly)
23 // -> true
24
25 // Search up the chain and find in Object.prototype
26 console.log(hummingbird.valueOf())
27 // -> Bird { type: 'Hummingbird' }
1. Property type is defined per Bird instance base as usual.2. The new usage is that the fly method is defined on the property prototype of Bird
constructor. Actually, every function has a prototype object property. The propertiesdefined on it are shared across all object instances created by this constructor.
3. When we access the flymethod of hummingbird, it doesn’t exist in the instance itself.However, JavaScript will check on the implicit __proto__ property of hummingbird andthis property actually points to the prototype property of Bird constructor.When fly
method is located, the search ends.4. Same mechanism applies on searching the valueOf method and it ends on the
prototype property of Object constructor.
This is how JavaScript implements the inheritance mechanism and why it’s calledPrototype Chain. The implicit __proto__ property in object instances and the prototype
objects forms the Prototype Chain from the bottom till the top in Object.
Override
I am so naive that I originally thought that all birds can fly. However, I found out later thatsome birds cannot, such as penguins. How can I override the default behavior inheritedfrom Bird prototype?
As we share before, the properties are looked up through Prototype Chain, hence we cansimply override the default behavior by assigning a new property with the same nameto the object itself. The original one in prototype will not be affected.
Object-oriented 68
1 function Bird(type) {
2 this.type = type
3 }
4
5 Bird.prototype.fly = function() {
6 console.log('Flying with two wings.')
7 }
8
9 var penguin = new Bird('Penguin')
10 var swan = new Bird('Swan')
11
12 penguin.fly = function() {
13 console.log('Flying with two wings in the water but not sky.')
14 }
15
16 penguin.fly()
17 // -> Flying with two wings in the water but not sky.
18 swan.fly()
19 // -> Flying with two wings.
Assigning new properties to object will never change the one in its constructor’s proto-type. However, modifying the property is different. As the properties of the prototype areshared across all instances, if they are object or array type, modifying them can affectsall the instances immediately.
1 function Bird(type) {
2 this.type = type
3 }
4
5 Bird.prototype.colors = []
6
7 var swan = new Bird('Swan')
8 swan.colors.push('Black')
9
10 var parrot = new Bird('Parrot')
11 parrot.colors.push('Purple')
12
13 console.log(swan.colors)
14 // -> [ 'Black', 'Purple' ]
As you can see, now the swan object instance is also changed. swan has color Purple too.Hence, the thumb of rule is that if the property is most likely changed for every instance,
Object-oriented 69
it should be placed inside the constructor using this to initialize it. Only those commonlyshared but less-likely changed properties should be placed in prototype especially whenit’s object type.
Self-defined inheritance relationship
Previously, we only have two levels of inheritance from object instance to constructor,to Object. Can we expand the inheritance hierarchy to have more levels? For example,I want to define three constructors: Swan and Penguin as the child of Bird; BlackSwan asthe child of Swan. In this way, I don’t have to specify same information repeatedly whencreating new instances for each type of the bird.
We can achieve that by explicitly setting the prototype object for each constructor.
1 function Bird(type) {
2 this.type = type
3 }
4
5 function Penguin() {
6 Bird.call(this, 'Penguin')
7 }
8 Penguin.prototype = Object.create(Bird.prototype)
9
10 function Swan(color) {
11 this.color = color || 'White'
12 Bird.call(this, 'Swan')
13 }
14 Swan.prototype = Object.create(Bird.prototype)
15
16 function BlackSwan() {
17 Swan.call(this, 'Black')
18 }
19 BlackSwan.prototype = Object.create(Swan.prototype)
20
21 var penguin = new Penguin()
22 var swan = new Swan()
23 var blackSwan = new BlackSwan()
24
25 console.log(penguin)
26 // -> Bird { type: 'Penguin' }
27 console.log(swan)
28 // -> Bird { color: 'White', type: 'Swan' }
29 console.log(blackSwan)
Object-oriented 70
30 // -> Bird { color: 'Black', type: 'Swan' }
31 console.log(penguin instanceof Bird)
32 // -> true
33 console.log(blackSwan instanceof Bird)
34 // -> true
35 console.log(blackSwan instanceof Swan)
36 // -> true
Two new things that deserved some explanation here:
1. In child constructor Penguin, Swan and BlackSwan, the corresponding parent con-structor need to be called using the form xxx.call(this, 'yyy'). The this identifierrefers to the Penguin, Swan and BlackSwan object instance when created using new. Sowhen it’s provided to the parent constructor call, the properties initialized in Bird
or BlackSwan are set to that object accordingly. And other necessary parameters canalso be passed accordingly.
2. The prototype of the child constructor need to be derived from the prototype
property of the desired constructor instead of Object. Here, we use Object.create
to clone a new object based on the provided prototype.
6.4 Polymorphism
When different objects have same interface but different behaviors, and an operationsupports any object as long as it has the expected interface, this operation is a polymor-phic type and this technique is called Polymorphism.
console.log is a very good example of Polymorphism. When you pass a function, arrayor object to it, it works without any change. Function, Array and Object all have its ownvalueOf interface but behaves differently so that the result printed on screen is different.SubTyping through Inheritance is a very typical approach in Polymorphism.
We can see below Bird example on their common fly interface.
1 function Bird(type) {
2 this.type = type
3 }
4 Bird.prototype.fly = function() {
5 console.log(this.type + ': Flying with two wings in the immense sky.')
6 }
7
8 function Penguin() {
9 Bird.call(this, 'Penguin')
Object-oriented 71
10 }
11 Penguin.prototype = Object.create(Bird.prototype)
12 Penguin.prototype.fly = function() {
13 console.log(this.type + ': What? You don't know I cannot fly?')
14 }
15
16 function Swan(color) {
17 Bird.call(this, 'Swan')
18 }
19 Swan.prototype = Object.create(Bird.prototype)
20
21 function flyingContest(candidates) {
22 for (var i = 0; i < candidates.length; i++) {
23 candidates[i].fly()
24 }
25 }
26
27 var penguin = new Penguin()
28 var swan = new Swan()
29
30 flyingContest([swan, penguin])
31 // -> Swan: Flying with two wings in the immense sky.
32 // -> Penguin: What? You don't know I cannot fly?
6.5 Exercises
Whac-A-Mole
My son likes to play this game very much when he’s four-years’ old. So let’s write asimplified command-line version of this game.
Object-oriented 72
Whac-A-Mole
As this is a command line program. How can we simulate this game? To make it simple,we can display a table as below. The O are the holes while the M is the mole shown. Afterthe table is shown, you have five seconds to input the X-Y coordinate position of themoleand press Enter (In the format of X,Y and 2,3 is for below case). If it’s correct, then youget one score. After five seconds, the mole shows in another hole.
Object-oriented 73
1 | O | O | O | O |
2 | O | M | O | O |
3 | O | O | O | O |
4 | O | O | O | O |
If we consider this game is an Object, what attributes and behaviors it should have? Ithink it should at least have below attributes and behaviors (You can add any assistanceattributes or behaviors as you need to facilitate your logic):
• Attributes:
1. score: Used to keep track of how many moles you hit.• Behaviors:
1. printResult: Prints out the exit message with score when the program isterminated (Ctrl+C is pressed).
2. showHoles: Prints out the table with a randomly shown mole.3. isMoleHit: Checks if you hit the right mole and increase the score if needed.
So few attributes? That is also the beauty of Encapsulation. You should only provide publicinterface and leave all implementation detail inside.
1 'use strict'
2
3 // Creates your WhacAMole constructor with attributes and behaviors
4 function WhacAMole(width, height) {
5
6 }
7
8 // You should have no need to modify below code
9 var obj = new WhacAMole()
10
11 obj.showHoles()
12
13 var i = setInterval(function() {
14 obj.showHoles()
15 }, 5000) // 5000 milliseconds which means 5 seconds
16
17 process.stdin.setEncoding('utf8')
18
19 process.stdin.on('readable', () => {
Object-oriented 74
20 var chunk = process.stdin.read()
21 if (chunk !== null) {
22 chunk = chunk.toString()
23 // isMoleHit accepts your input after pressing Enter
24 obj.isMoleHit(chunk.substring(0, chunk.length - 1))
25 }
26 })
27
28 process.on('SIGINT', function() {
29 clearInterval(i)
30 // Prints out the exit message and score
31 obj.printScore()
32
33 process.exit(0)
34 })
35
36 function getRandomIntInclusive(min, max) {
37 return Math.floor(Math.random() * (max - min + 1)) + min
38 }
Hints: String’s split method; two dimensional array ([[‘X’, ‘X’], [‘X’, ‘X’]]) can be used tosimulate the table.
7. Functional ProgrammingAll problems in computer science can be solved by another level of indirec-tion. – David Wheeler
During the hype of Object-oriented programming, many people claims it’s the only wayand the right way to program. However, it’s not the case. If we program and model theworld all based on Object, it introduces unnecesary complexity. And that is why manypeople complain about the verbosity of Java, unneeded layers introduced by designpatterns.
Functional Programming rises and gains more popularity after that because it has greatadvantages on parallel computing and distributed systems.
7.1 Core concepts
The two most important concepts in Functional Programming are:
• First-class & Higher-order Function• Pure Function
First-class & Higher-order Function
What does it mean? Basically, they are similar. Higher-order functions, more from amathematical concept, describe the functions that can operate on other functions. WhileFirst-class functions, more from computer science concept, means the language allowsthe functions to be used anywhere in the program like other first-class entities, such asnumbers, strings. They can be arguments to other functions or as other functions’ returnvalue.
Let’s see an example. To deal with Array-like objects, we normally use a for loop to iterateon each item in it and do some operation. Can we extract the for loop from the actualwork so that we don’t need to write that every time?
Functional Programming 76
1 var numbers = [1, 2, 3, 4, 5];
2
3 function double(num) {
4 return num * 2;
5 }
6
7 function square(num) {
8 return num * num;
9 }
10
11 function map(data, fn) {
12 var results = [];
13
14 for (var i = 0; i < data.length; i++) {
15 results.push(fn(data[i]));
16 }
17
18 return results;
19 }
20
21 console.log(map(numbers, double));
22 // -> [ 2, 4, 6, 8, 10 ]
23 console.log(map(numbers, square));
24 // -> [ 1, 4, 9, 16, 25 ]
As you can see, double and square functions are treated as normal parameter. Functionmap, as Higher-order function, is responsible for iterating the array items and call thepassed-in fn function parameter to process each item in the array.
I will show you the example of using function as a return value in sectionCurrying below.
Pure Function
In what condition, we can say that a function is pure?
1. Returns the same result value given the same argument value.The function should not depend on hidden state of the evironment, anything mightchange during different executions or input from I/O devices
2. Causes no side effects.The function doesn’t mutate external objects, state or output to I/O devices.
Making a function pure has many benefits:
Functional Programming 77
1. Its behavior is expectable and less error-prone because its result is constant giventhe same arguments.
2. If a function is pure, and its return result is not used, it can be removed safely.3. Pure functions should not interfere each other and their execution order can be
freely arranged or performed in parallel if they don’t need to use each other’s resultas parameter.
7.2 Composition
As what I emphasized in chapter 4 that composing clear and concise functions togetherto accomplish one thing is better than writing a single large one. Composition helps tomake the program more expressive. For example, I want to write programs to filter outfrom a sequence of numbers which are even, or divisible by 6.
1 var seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
2 var filtered = [];
3 for (var i = 0; i <= seq.length; i++) {
4 if (seq[i] % 2 == 0) {
5 filtered.push(i);
6 }
7 }
8 console.log(filtered)
9 // -> [ 0, 2, 4, 6, 8, 10 ]
10
11 seq = [2, 4, 6, 8, 10, 12, 14, 16, 18];
12 filtered = [];
13 for (var i = 0; i <= seq.length; i++) {
14 if (seq[i] % 6 == 0) {
15 filtered.push(i);
16 }
17 }
18 console.log(filtered)
19 // -> [ 6, 12, 18 ]
How many common patterns you can see from above two program segments? I think atleast two there.
1. Number sequence generation2. Filtering logic.
If we refactor the code as below, what do you think?
Functional Programming 78
1 function sequence(max, min, step) {
2 var seq = []
3 for (var i = min || 0; i <= max; i = i + (step || 1)) {
4 seq.push(i)
5 }
6 return seq
7 }
8
9 function filter(seq, cond) {
10 var filtered = []
11 for (var i = 0; i <= seq.length; i++) {
12 if (cond(seq[i])) {
13 filtered.push(seq[i])
14 }
15 }
16 return filtered
17 }
18
19 function isEven(num) {
20 return num % 2 == 0
21 }
22
23 filter(sequence(10), isEven)
24
25 filter(sequence(18, 2, 2), function(num) {
26 return num % 6 == 0
27 })
At first glance, you may think I have doubled the program size and made the codeunnecessary complex. However, there are couples of advantages here:
1. Three function units sequence, filter and isEven are common abstractions that areallowed to be individually reused for other scenarios.
2. Composition makes the code more expressive at first glance, seven lines of codevs filter(sequence(10), isEven).
7.3 Functional behaviors on Array
Actually, the map and filter function I defined above are standard methods on arrays.Those programs can be revised as below.
Functional Programming 79
1 // function sequence, isEven, double, square are omitted here for brevity.
2
3 console.log(sequence(5, 1).map(double))
4 // -> [ 2, 4, 6, 8, 10 ]
5 console.log(sequence(5, 1).map(square))
6 // -> [ 1, 4, 9, 16, 25 ]
7
8 console.log(sequence(10).filter(isEven))
9 // -> [ 0, 2, 4, 6, 8, 10 ]
10 console.log(sequence(18, 2, 2).filter(function(num) {
11 return num % 6 == 0
12 }))
13 // -> [ 6, 12, 18 ]
Two more standard methods defined on arrays, forEach and reduce, are very useful.forEach is similar to map, but it just executes the function on each item in array withoutreturning a processed value.
For method forEach, filter and map, the function we provided to it can actually take threearguments: currentValue, index, array. You can access the index of current value in arrayand the array itself in the function as well.
1 sequence(10).forEach(function(value, idx, array) {
2 console.log(value + ' is at position ' + idx)
3 })
4 // -> 0 is at position 0
5 // -> 1 is at position 1
6 // -> ...
7 // -> 10 is at position 10
For method reduce, it’s a little bit different. The return value for method forEach, filter,map is always an array, while reduce can return anything. The return value of reduce can bea single primitive value or an object. It emphasize more on the value as a whole insteadof a collection of each processed value.
Let’s use it to sum a sequence of numbers.
Functional Programming 80
1 var sum = sequence(10).reduce(
2 function(previousValue, currentValue, currentIndex, array) {
3 return previousValue + currentValue
4 }, 0)
5
6 console.log(sum)
7 // -> 55
The reduce method takes two parameters, the first one is the function and the secondone is the initial value. Although the initial value is optional, I strongly recommendedthat it’s provided. Because you can clearly tell what the expected output data structureis and the function behavior is more expectable.
As demonstrated above, the function provided to reduce method takes four argumentsalthoughwenormally use the first two.When the loop starts at index 0, the previousValueis the initial value and currentValue is the first element in the array. Then the loopscontinues at index 1, the previousValue becomes the result return from last functionexecution, which in this case is the sum of the initial value and element at index 0, andthe currentValue becomes the second element in the array. So on and so forth. Themostimportant thing to pay attention is that you must always return a value in the functionas the previousValue for next loop.
Now you learn these basic methods for Array, let’s see how expressive it can be.
1 var result = sequence(10)
2 .map(square)
3 .filter(function(num) {
4 return num % 5 == 0
5 })
6 .reduce(function(sum, value, index) {
7 sum[index] = value
8 return sum
9 }, {})
10
11 console.log(result)
12 // -> { '0': 0, '1': 25, '2': 100 }
7.4 Closure
This topic should be the most frequently asked questions during interview for junior or2∼3 years experienced JavaScript developers. Hence, this is an important concept andit’s a little bit abstract and difficult. It’s OK if you don’t completely understand at the firsttime. You can revisit it from time to time.
Functional Programming 81
Let’s first see what does Closuremeans. Below is the defintion from MDN1:
Closures are functions that refer to independent (free) variables (variablesthat are used locally, but defined in an enclosing scope). In other words, thesefunctions ‘remember’ the environment in which they were created.
As usual, the concept is very abstract. But I have highlighted the key points. Let’s see howit’s used in some practical examples so that you can understand it better.
Common Examples
Markdown2 allows us to use plain text with some simple syntax to create structural HTMLand is widely used in article, blog, and book writing, including this one.
In the Markdown syntax, if you start a line with #, it’s the first-level header; If you start aline with ##, it’s the second-level header. So on and so forth to header level 6.
So, if I want to have some functions to convert text to different header, we can write itthis way:
1 function h1(text) {
2 return '# ' + (text ? text : '')
3 }
4
5 function h2(text) {
6 return '## ' + (text ? text : '')
7 }
8
9 function h3(text) {
10 return '### ' + (text ? text : '')
11 }
But you see, there are some duplicate empty text checking code and the only differencethere is the markdown syntax prefix part. We can refactor in this way normally.
1https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures2https://daringfireball.net/projects/markdown/
Functional Programming 82
1 function defaultText(text) {
2 return (text ? text : '')
3 }
4
5 function h1(text) {
6 return '# ' + defaultText(text)
7 }
8
9 function h2(text) {
10 return '## ' + defaultText(text)
11 }
12
13 function h3(text) {
14 return '### ' + defaultText(text)
15 }
However, there is still one duplicate part ‘ + defaultText(text)‘. Can we make a stepfurther? Let’s see the magic below.
1 function defaultText(text) {
2 return (text ? text : '')
3 }
4
5 function markdownHeader(prefix) {
6 return function(text) {
7 return prefix + ' ' + defaultText(text)
8 }
9 }
10
11 var h1 = markdownHeader('#')
12 var h2 = markdownHeader('##')
13 var h3 = markdownHeader('###')
14
15 console.log(h1('One')) // -> '# One'
16 console.log(h2('Two')) // -> '# Two'
17 console.log(h3('Three')) // -> '# Three'
The h1, h2 and h3 are closures. They are the functions created by function markdownHeader.This actually is one design pattern, called Factory Pattern, but our focus is not on thisnow. The point is the inner function in markdownHeader is partially provided requiredinformation for later processing and so h1, h2 and h3 remembers the prefix we passedin when they are created. Partial Application is a commonly used scenario of closures.
There is one more approach for Partial Application without using closures.
Functional Programming 83
1 function defaultText(text) {
2 return (text ? text : '')
3 }
4
5 function markdownHeader(prefix, text) {
6 return prefix + ' ' + defaultText(text)
7 }
8
9 var h1 = markdownHeader.bind(null, '#')
10 var h2 = markdownHeader.bind(null, '##')
11 var h3 = markdownHeader.bind(null, '###')
12
13 console.log(h1('One')) // -> '# One'
14 console.log(h2('Two')) // -> '# Two'
15 console.log(h3('Three')) // -> '# Three'
As we saw before, the bind method allows us to specify the object to be used if thefunction refers this in it. In the case above, we don’t care about the this object and sowe bind a null to it. But bindmethod also allows us to preset any number of parametersfor the function.
Let’s see another example on using closure. Sometimes, we would like to measure howmuch time one JavaScript function takes to run. A simple way is to measure as below.
1 function seemsSlow(times, msg) {
2 var start = new Date()
3
4 // Some work to do here.
5 for (var i = 0; i < times; i++) {
6 }
7
8 return msg
9
10 var end = new Date()
11 var duration = ((end - start) / 1000)
12
13 console.log(`This function runs for ${duration} second.`)
14 }
15
16 console.log(seemsSlow(1000000000, 'Done'))
17 // -> This function runs for 0.556 second.
18 // -> Done
Functional Programming 84
However, if we have to modify the function source code in order to measure it, it isinefficient and error-prone. Can we abstract that out?
1 function measure(fn) {
2 return function() {
3 var start = new Date()
4
5 var result = fn.apply(null, arguments)
6
7 var end = new Date()
8 var duration = ((end - start) / 1000)
9
10 console.log(`This function runs for ${duration} second.`)
11
12 return result
13 }
14 }
15
16 function seemsSlow(times, msg) {
17 for (var i = 0; i < times; i++) {
18 }
19
20 return msg
21 }
22
23 var wrappedFn = measure(seemsSlow)
24 console.log(wrappedFn(1000000000, 'Done'))
25 // -> This function runs for 0.528 second.
26 // -> Done
Function measure wraps the fn into a new function with timing capability. One thing to benoticed is that if we simply use fn() instead of the statement var result = fn.apply(this,
arguments), we lost the capabilities of accepting arguments and returning value of the fn.Method apply of Function is helpful in this case. It accepts two arguments: the first oneas the this value; the second one, an Array-like objects, as the arguments for the fn. Itsreturn value is the return value of original fn function.
Common Mistake
Let’s check below code and can you tell what the output is?
Functional Programming 85
1 var headerFns = [
2 {methodName: 'h1', headerPrefix: '#'},
3 {methodName: 'h2', headerPrefix: '##'},
4 {methodName: 'h3', headerPrefix: '###'}
5 ]
6
7 var markdownHeader = {}
8 for (var i = 0; i < headerFns.length; i++) {
9 var fn = headerFns[i]
10 markdownHeader[fn.methodName] = function(text) {
11 return fn.headerPrefix + ' ' + text
12 }
13 }
14
15 console.log(markdownHeader.h1('One'))
16 console.log(markdownHeader.h2('Two'))
17 console.log(markdownHeader.h3('Three'))
18 // -> ??
It’s not this of course since I asked.
1 # One
2 ## Two
3 ### Three
It’s like this:
1 ### One
2 ### Two
3 ### Three
var is not used for scope level variable declaration. Above code actually is same as below.The variable fn is lifted up.
Functional Programming 86
1 var headerFns = [
2 {methodName: 'h1', headerPrefix: '#'},
3 {methodName: 'h2', headerPrefix: '##'},
4 {methodName: 'h3', headerPrefix: '###'}
5 ]
6
7 var markdownHeader = {}
8 var fn
9
10 for (var i = 0; i < headerFns.length; i++) {
11 fn = headerFns[i]
12 markdownHeader[fn.methodName] = function(text) {
13 return fn.headerPrefix + ' ' + text
14 }
15 }
16
17 console.log(markdownHeader.h1('One'))
18 console.log(markdownHeader.h2('Two'))
19 console.log(markdownHeader.h3('Three'))
Hence, the for loop keeps changing on the value fn points to. All three closures createdpoints to the same object in the last position of the array. One simple way to fix this issueis replacing the var to let.
However, if the environment doesn’t support let, what can we do? We need to createanother level of closure.
1 function markdownHeaderFn(headerObj) {
2 return function(text) {
3 return headerObj.headerPrefix + ' ' + text
4 }
5 }
6
7 var headerFns = [
8 {methodName: 'h1', headerPrefix: '#'},
9 {methodName: 'h2', headerPrefix: '##'},
10 {methodName: 'h3', headerPrefix: '###'}
11 ]
12
13 var markdownHeader = {}
14 for (var i = 0; i < headerFns.length; i++) {
15 var fn = headerFns[i]
16 markdownHeader[fn.methodName] = markdownHeaderFn(fn)
Functional Programming 87
17 }
18
19 console.log(markdownHeader.h1('One'))
20 console.log(markdownHeader.h2('Two'))
21 console.log(markdownHeader.h3('Three'))
7.5 Exercises
Array to Map
In previous chapter, we requested you to write a function with such signature ar-
rayToMap(array, keyProperty, valueProperty) to convert an array to object map. Pleaserewrite it with such signature arrayToMap(array, iteratee). Object keys are the results ofrunning each element of array through iteratee. The corresponding value of each key isthe last element generated that key. The iteratee is invoked with three arguments, value,index, array.
Trampoline
What is Trampoline3?
a trampoline is a loop that iteratively invokes thunk-returning functions(continuation-passing style).�Programmers can use trampolined functions to implement tail-recursivefunction calls in stack-oriented programming languages.
The simplest form of trampoline is like below:
1 function trampoline(fn) {
2 var op = fn;
3 while (op != null && typeof op === 'function') {
4 op = op();
5 }
6 }
What is its usage? Remember in chapter 4, section Call Stack, if we pass a very largenumber to recursion function to calculate Factorial number, stack overflow error occurs.With Trampoline, we can eliminate such error.
Please fill in the logic for thunkedFactorial so that below code snippet works.
3https://en.wikipedia.org/wiki/Trampoline_
Functional Programming 88
1 // For reference
2 function factorial(n) {
3 return n === 0 ? 1 : n * factorial(n - 1);
4 }
5
6 function thunkedFactorial(n, cb) {
7 // Fill in the actual code here.
8 }
9
10 trampoline(thunkedFactorial.bind(this, 10, console.log.bind(console)))
11 // => 3628800
12
13 trampoline(thunkedFactorial.bind(this, 1000000, console.log.bind(console)))
14 // => Infinity
Notes: As the trampoline function detects return value of op for terminal condition, wehave to rewrite the factorial to accept a function cb to get the result instead of directlyreturning it.
Hints: bind, Partial Application
Currying
Notes: this is a bonus exercise. Don’t feel bad if you cannot work it out as it’s really reallyhard. Just want you to know how amazing JavaScript and functional programming canbe.
From wiki4:
currying is the technique of translating the evaluation of a function that takesmultiple arguments (or a tuple of arguments) into evaluating a sequence offunctions, each with a single argument.
Currying and partial function application are often conflated. One of thesignificant differences between the two is that a call to a partially appliedfunction returns the result right away, not another function down the curryingchain; this distinction can be illustrated clearly for functions whose arity isgreater than two.
4https://en.wikipedia.org/wiki/Currying
Functional Programming 89
1 // Original function
2 function addThreeNumber(a, b, c) {
3 return a + b + c
4 }
5
6 // Magic curry function
7 function curry3(fun){
8 return function(one){
9 return function(two){
10 return function (three){
11 return fun(one, two, three)
12 }
13 }
14 }
15 }
16
17 // Usage
18 var addThreeNumberCurried = curry3(addThreeNumber)
19 console.log(addThreeNumber(1, 2, 3))
20 console.log(addThreeNumberCurried(1)(2)(3))
The exercise here is that can you write a curryN function which works on functions withany number of arguments?
1 // Original functions
2 function addThreeNumber(a, b, c) {
3 return a + b + c
4 }
5 function mirror(input) {
6 return input
7 }
8 function random() {
9 return Math.random()
10 }
11
12 // Magic curryN function
13 function curryN(fn, n) {
14 // Fill in your code here
15 }
16
17 // Usage
18 var addThreeNumberCurried = curryN(addThreeNumber)
Functional Programming 90
19 console.log(addThreeNumber(1, 2, 3))
20 console.log(addThreeNumberCurried(1)(2)(3))
21
22 var mirrorCurried = curryN(mirror)
23 console.log(mirror('Fantastic'))
24 console.log(mirrorCurried('Fantastic'))
25
26 var randomCurried = curryN(random)
27 console.log(random())
28 console.log(randomCurried())
Hints: bind, Partial Application, fn.length gives arguments number of a function