JavaScript Programming
Introduction to JavaScript
History of JavaScript
- Brendan Eich developed at Netscape - it was called Mocha
- 1995 - Released as LiveScript
- 1995 - Renamed to JavaScript (to ride on the popularity of Java)
- 1996 - Microsoft JScript
- 1997 - First edition of ECMAScript standard (European Computer Manufacturers Association)
- 1998 - Second edition
- 1999 - Third edition
- 4th edition never completed
- 2009 - 5th edition
- 2011 - 5.1 edition (part of "HTML5")
- 2012 - TypeScript
- 2015 - 6th edition (ECMAScript6 or ECMAScript 2015)
About JavaScript
- In browser + DOM (Document Object Model)
- On server (Node.js" %}
- Embedded in other applications
JavaScript editors, IDEs
IDEs - Integrated Development Environment
Editors
alert
- alert
- script
- ;
{aside} Probably the simplest way to see some action from JavaScript is to embed an alert() call in an html file. {/aside}
<html>
<head>
<title>alert example</title>
</head>
<body>
<h1>Welcome to JavaScript</h1>
<script language="javascript">
alert("Hello World");
</script>
More HTML
</body>
</html>
- Not really used any more
- always put ; at the end of statements
Document.write
- document.write
First line
<script>
document.write("<h1>Hello World</h1>");
</script>
Last line
- script - javascript
- document.write
- document is the object representing the HTML document. The DOM.
- Not really used any more
confirm
- confirm
<h2>Before</h2>
<script>
if (confirm("Shall I print Hello World?")) {
document.write("Hello World");
} else {
document.write("OK, I won't print it.");
}
</script>
<h2>After</h2>
prompt
- prompt
<h2>Before</h2>
<script>
var name = prompt("Your name:", "");
document.write("Hello ", name);
</script>
<h2>After</h2>
console
- console.log
The better way to see messages from the JavaScript running in the browser is to use the console.log() function and open the console.
- See the documentation of console
Open the console!
<script>
console.log("Hello World");
console.debug("A debug message");
console.info("An info message");
console.warn("A warning");
console.error("This is an error message");
</script>
To open the JavaScript console
- Chrome Mac: Command-Option-J
- Chrome Windows: Ctrl-Shift-J
- Firefox Mac: Command-Option-K
- Firefox Windows: Ctrl-Shift-K
- Internet Explorer: F12
- Safari: Command-Option-C
Run it with Node.JS
Run it in the editor
Separate script to its own file
Open the console!
<script src="console.js"></script>
console.log("Hello World");
$ node examples/js/console.js
Comments in JavaScript
- /*
- //
console.log("code 1");
// comment
console.log("code 2")
/* more
comment
*/
console.log("code 3")
// console.log("/* hello */")
console.log("code 4")
- To explain to the next developer why do we do something.
- Explain algorithm.
- Temporarily disable code for debugging.
Bad Comments in JavaScript
It is better to avoid the multiline comments /* */ Especially as most editors support the commenting out of a whole section of lines with just one keystroke.
console.log("code 1");
// comment
console.log("code 2")
/* more
comment
*/
console.log("code 3")
/*
console.log("/* hello */")
*/
console.log("code 4")
SyntaxError: unterminated string literal
.../examples/js/bad_comments.js:8
console.log("/* hello */")
^^
SyntaxError: Unexpected token ILLEGAL
at exports.runInThisContext (vm.js:73:16)
at Module._compile (module.js:443:25)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
Literal values in JavaScript (numbers, strings, booleans, etc.)
- Numbers (42, 2.3, NaN, Infinity, -Infinity)
- Strings ("", '')
- Booleans (true, false)
- null
- undefined
- Objects (incl. Arrays, Functions)
console.log(42);
console.log(2.3);
console.log("hello");
console.log('world');
console.log(undefined);
console.log(null);
console.log(NaN);
console.log(Infinity);
console.log(-Infinity);
console.log(true);
console.log(false);
- Numbers in JavaScript is 64-bit floating point (double)
- Regular arithmetic issues 0.1+0.2 is not exactly 0.3
Examples for generating Infinite and NaN (not a number)
- NaN
- Infinite
console.log(2/0); // Infinity
console.log(-2/0); // -Infinity
console.log(2/-0); // -Infinity
console.log(2/0 - 2/0); // NaN
var - variables in JavaScript
- use strict
- var
"use strict";
var user_name = 'Foo';
console.log(user_name); // "Foo"
var age = 42;
console.log(age); // 42
age = 23;
console.log(age); // 23
user_name = 1;
console.log(user_name); // 1
var email;
console.log(email); // undefined
email = 'foo@bar.com';
console.log(email); // foo@bar.com
- "use strict"; (later explained)
- Declare variables using 'var'
- Variable names start with letters, underscore (_), or the dollar sign.
- Variable names can contain letters, underscore (_), the dollar sign, and digits.
- camelCase is the recommended style in JavaScript, though I think long_names separated with underscores are nicer.
Variables without var
- Works, variable are global here too.
- What if we make a typo in one of the variable names? No one might notice, but the code will misbehave.
colour = "green";
console.log(colour); // green
paint_house();
console.log(colour); // green
// There are lots of lines of code here.
// So many lines that the next part is on another page:
function paint_house() {
color = "blue";
}
use strict
- use strict
"use strict";
colour = "green";
console.log(colour); // green
paint_house();
console.log(colour); // green
// There are lots of lines of code here.
// So many lines that the next part is on another page:
function paint_house() {
color = "blue";
}
{% embed include file="src/examples/js/no_var_strict.txt)
use strict + var
"use strict";
var colour = "green";
console.log(colour); // green
paint_house();
console.log(colour); // green
// There are lots of lines of code here.
// So many lines that the next part is on another page:
function paint_house() {
color = "blue";
}
{% embed include file="src/examples/js/var_strict.txt)
Scope of variables
<script src="scope_bare.js"></script>
<script src="scope_var.js"></script>
<script src="scope_let.js"></script>
<script src="scope_const.js"></script>
x = 1;
{
console.log(`inside before: ${x}`);
x = 2;
console.log(`inside after: ${x}`);
}
console.log(`outside: ${x}`);
var x = 3;
{
console.log(`inside before: ${x}`);
var x = 4;
console.log(`inside after: ${x}`);
}
console.log(`outside: ${x}`);
let x = 5;
console.log(`before: ${x}`);
{
//console.log(`inside before: ${x}`); // Uncaught ReferenceError: Cannot access 'x' before initialization
let x = 6;
console.log(`inside after: ${x}`);
x = 7;
console.log(`inside end: ${x}`);
}
console.log(`outside: ${x}`);
const z = 10;
console.log(`before: ${x}`);
{
console.log(`inside before: ${x}`);
const z = 11;
console.log(`inside after: ${x}`);
z = 12; // Uncaught TypeError:
// console.log(`inside end: ${x}`);
}
console.log(`outside: ${x}`);
- var
- let
- const
Exercise: Set up environment for web browser
- Install either Chrome or Firefox if you don't have them yet.
- Using Notepad or any other text editor create and .html file that will show and 'alert' and try it in your browser.
- (Suggestion: Start by creating a directory in c:\ in Windows or in ~/ on Unix/Linux and put your files in that directory)
Exercise: Set up environment for command line
- Install Node.js
- Using Notepad or any other text editor (re-)create the console.js file and run it on the command line using node.
- (On Windows you need to open Start/Run: cmd)
Exercise: Set up development environment
If you prefer to use another Editor/IDE, that's ok. Then insted of these steps, make sure you have similar capabilities there.
- Install Atom
- Open the directory where you've already saved the .html and .js files earlier using File/Open Project Folder
- Install the Ternjs (atom-ternjs) package for JavaScript intelligence and check if it works properly. (In your .js file type in a string followed by a .)
- Install the 'script' package to be able to run code from editor. Try to run the .js file.
Exercise: Hello World
- Using the new Editor/IDE - open the console.js and run it from the editor
- Create the console.html file too and open it with a browser
- Create a hello.html that loads hello.js that ask the user for her name and then displays the result in the browser and on the console as well.
JavaScript basics
Numerical Operators
- autoincrement ++, autodecrement --
- shorthand +=, /=, *=, -=
"use strict";
console.log(19 + 23); // 42
console.log(23 - 19); // 4
console.log(23 * 19); // 437
console.log(23 / 19); // 1.2105263157894737
console.log(123 % 19); // 9 (modulus)
var x = 19;
var y = 23;
console.log(x + y); // 42
x++;
console.log(x); // 20
y--;
console.log(y); // 22
x += 7; // x = x + 7;
console.log(x); // 27
x /= 3; // x = x / 3;
console.log(x); // 9
-
-
-
- %
- ++
- --
- +=
String operations
- trim
- length
- charAt
- \t
- \n
- \
"use strict";
console.log("a" + "b"); // ab
console.log("abc".length); // 3
console.log('<' + " a b c " + '>'); // < a b c >
console.log('<' + " a b c ".trim() + '>'); // <a b c>
console.log("this is a long string".charAt(3)); // s
console.log("this is a long string".charAt(0)); // t
var x = "Hello ";
var y = "World";
console.log(x + y); // Hello World
- length is an attribute
- trim() is a method
\t - tab
\n - newline
\" - to escape a quote
\\ - to escpae a backslash
Note: + acts both on numbers and on strings
String index and slice
- length
- slice
- charAt
"use strict";
var str = "Hello World";
console.log(str[0]); // H
console.log(str[1]); // e
console.log(str.charAt(0)); // H
console.log('----');
var i;
for (i = 0; i < str.length; i++) {
console.log(str[i]);
}
console.log(str.slice(3, 7)); // lo W
indexOf, lastIndexOf
- indexOf
- lastIndexOf
"use strict";
var str = "The black cat climbed the green tree.";
console.log(str.indexOf('a')); // 6
console.log(str.indexOf('a', 7)); // 11
console.log(str.lastIndexOf('t')); // 32
console.log(str.lastIndexOf('t', 31)); // 22
console.log(str.indexOf('cat')); // 10
console.log(str.indexOf('dog')); // -1
substr, slice, and substring
-
substring
-
substr
-
substr - (from, length?)
-
slice - (from, to?)
-
substring - (from, to?) (Use slice instead!)
"use strict";
var str = "The black cat climbed the green tree.";
console.log(str.substr(5,7)); // lack ca
console.log(str.slice(5,7)); // la
console.log(str.slice(22)); // the green tree.
console.log(str.substr(22)); // the green tree.
console.log(str.slice(10, -10)); // cat climbed the g
console.log(str.substr(10, -10)); // (empty string)
console.log(str.slice(-11, -6)); // green
console.log(str.substr(-11, -6)); // (empty string)
console.log(str.slice(-11, 31)); // green
console.log(str.slice(-11, 50)); // green tree.
console.log(str.substr(-11, 31)); // green tree.
Concatenate strings
-
- concat
"use strict";
var name = 'Foo';
var mystr = "Hello " + name + ", how are you?";
console.log(mystr); // Hello Foo, how are you?
console.log(name); // Foo
var full = name.concat("Bar");
console.log(name); // Foo
console.log(full); // FooBar
Replace substring
- replace
"use strict";
var str = "The black cat climbed the green tree.";
var dog = str.replace("cat", "dog");
console.log(str); // The black cat climbed the green tree.
console.log(dog); // The black dog climbed the green tree.
var str = str.replace("cat", "tiger");
console.log(str); // The black tiger climbed the green tree.
var str = str.replace("black", "");
console.log(str); // The tiger climbed the green tree.
var str = str.replace(" ", "");
console.log(str); // The tiger climbed the green tree.
We'll see a more powerful version of this in the chapter about Regular expressions.
Mixing numbers and strings
"use strict";
var a = "23";
var b = "19";
console.log(a + b); // "2319"
var x = 23;
var y = "19";
console.log(x + y); // "2319"
var p = 23;
var q = 19;
console.log(p + q); // 42
In a nutshell: Don't do that!
Converting between numbers and strings
"use strict";
var a = "23";
var b = "19";
console.log(a + b); // "2319"
console.log(parseInt(a) + parseInt(b)); // 42
var p = 23;
var q = 19;
console.log(p + q); // 42
console.log(p.toString() + q.toString()); // "2319"
console.log(String(p) + String(q)); // "2319"
console.log(parseInt("23.42")); // 23
console.log(parseFloat("23.42")); // 23.42
console.log(parseInt("2x3")); // 2
console.log(parseInt("6.7x3")); // 6
console.log(parseFloat("4.5x6")); // 4.5
console.log(Number("23")); // 23
console.log(Number("23.12")); // 23.12
console.log(Number("2x3")); // NaN
console.log(Number("6.7x3")); // NaN
console.log(Number("4.5x6")); // NaN
- parseInt
- parseFloat
- Number
- toString
- String
Convert octal, hexa
"use strict";
console.log(parseInt("0x11")); // 17
console.log(parseInt("011")); // 11 or 9 (depends on implementation)
console.log(parseInt("011", 10)); // 11
console.log(parseInt("011", 8)); // 9
console.log(parseInt("011", 16)); // 17
- parseInt
- octal
- hex
Converting decimal to hexa
- toString
- toUpperCase
"use strict";
var a = 16;
console.log(a.toString(16)); // 10
var b = 129;
console.log(b.toString(16)); // 81
var a = 190;
console.log(a.toString(16)); // be
var a = 190;
console.log(a.toString(16).toUpperCase()); // BE
Browser IO (HTML)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Browser IO</title>
</head>
<body>
<input id="first_name">
<input id="last_name">
<button id="go">Say hi!</button>
<div id="result"></div>
<script src="browser-io.js"></script>
</body>
</html>
Browser IO (JavaScript)
function say_hi() {
var fname = document.getElementById('first_name').value;
var lname = document.getElementById('last_name').value;
var html = 'Hello <b>' + fname + '</b> ' + lname;
document.getElementById('result').innerHTML = html;
return false;
}
document.getElementById('go').addEventListener('click', say_hi);
Exercise: Hello World on pressing button
Create an HTML page with a button on it. When the user presses the button display the text. "Hello World"
Solution: Hello World on pressing button
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Hello World</title>
</head>
<body>
<button id="go">Say hello</button>
<div id="result"></div>
<script src="hello_world.js"></script>
</body>
</html>
"use strict";
function greet() {
document.getElementById('result').innerHTML = 'Hello World';
return false;
}
document.getElementById('go').addEventListener('click', greet);
Exercise: Add two numbers
Create a web form that has two input fields and a button. The user can type js two numbers and when she clicks on a button, the page will show the sum of the numbers.
Solution: Add two numbers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Add numbers</title>
</head>
<body>
<input id="x">
<input id="y">
<button id="go">+</button>
<div id="result"></div>
<script src="add_numbers.js"></script>
</body>
</html>
"use strict";
function add() {
var x = Number(document.getElementById('x').value);
var y = Number(document.getElementById('y').value);
document.getElementById('result').innerHTML = x+y;
return false;
}
document.getElementById('go').addEventListener('click', add);
Variable definition
// "use strict";
a = 1;
console.log(a); // 1
var b = 2;
console.log(b); // 2
var c;
console.log(c); // undefined
console.log(typeof c); // "undefined"
d = undefined;
console.log(d); // undefined
console.log(typeof d); // "undefined"
console.log(e); // undefined
console.log(typeof e); // "undefined"
var e = 3;
console.log(x); // ReferenceError: x is not defined
Conditionals in JavaScript
if statement
- if
"use strict";
var y = 42;
if (y === 42) {
console.log('y is 42');
}
// y is 42
Double equal (==) issues in JavaScript
- ==
"use strict";
console.log(NaN == NaN); // false
console.log(0 == '0'); // true
console.log(false == '0'); // true
console.log(null == undefined); // true
console.log(' \t\r\n ' == 0); // true
console.log('' == '0'); // false
console.log(0 == ''); // true
Compare values using == in JavaScript
- ==
- !=
"use strict";
console.log(2 == 2); // true
console.log(2 == 3); // false
console.log(2 == null); // false
console.log(2 == undefined); // false
console.log(undefined == undefined); // true
console.log(null == null); // true
console.log(false == 'false'); // false
console.log(false == undefined); // false
console.log(false == null); // false
Compare values using === in JavaScript
- ===
- !==
"use strict";
console.log(2 === 2); // true
console.log(2 === 3); // false
console.log(2 === null); // false
console.log(2 === undefined); // false
console.log(undefined === undefined); // true
console.log(null === null); // true
console.log(NaN === NaN); // false !!!
console.log('' === '0'); // false
console.log(0 === ''); // false
console.log(0 === '0'); // false
console.log(false === 'false'); // false
console.log(false === '0'); // false
console.log(false === undefined); // false
console.log(false === null); // false
console.log(null === undefined); // false
console.log(' \t\r\n ' === 0); // false
Comparision operators
- ==
- !=
- ===
- !==
- >
- >=
- <
- <=
Comparision operators - examples
- >
- >=
- <
- <=
"use strict";
console.log(2 < 3); // true
console.log('2' < '3'); // true
console.log('a' < 'b'); // true
console.log('c' < 'b'); // false
console.log('-----');
console.log(12 > 3); // true
console.log(12 > '3'); // true
console.log('12' > 3); // true
console.log('12' > '3'); // false
console.log('-----');
console.log('a' > '3'); // true
console.log('a' > 3); // false
console.log('-----');
console.log(null < null); // false
console.log(null <= null); // true
console.log('-----');
console.log(null < undefined); // false
console.log(null <= undefined); // false
console.log(null > undefined); // false
console.log(null >= undefined); // false
Booleans: true and false
- true
- false
"use strict";
console.log(true); // true
console.log(false); // false
console.log(2 > 1); // true
console.log(2 < 1); // false
var x = 42;
if (x) {
console.log("42 is true"); // 42 is true
}
Falsy values in JavaScript
- false
Falsy:
false
null
undefined
'' (The empty string)
0 (The number)
NaN
"use strict";
console.log('start');
if (false) {
console.log('false');
}
if (null) {
console.log('null');
}
if (undefined) {
console.log('undefined');
}
if ('') {
console.log('empty string ""');
}
if (0) {
console.log('The number 0');
}
if (NaN) {
console.log('Nan');
}
console.log('end');
Truthy values in JavaScript
- true
Everything else is considered true:
The string " " with a single space
Infinity
The string "0"
The string "false"
"use strict";
console.log('start');
if (42) {
console.log('The number 42');
}
if (' ') {
console.log('The string " " with a single space');
}
if (Infinity) {
console.log('Infinity');
}
if ('0') {
console.log('The string "0"');
}
if ('false') {
console.log('The string "false"');
}
console.log('end');
Logical operators
- &&
- ||
- !
"use strict";
var x = true;
var y = true;
var z = false;
var q = false;
console.log(x && y); // true
console.log(x && z); // false
console.log('-----');
console.log(x || y); // true
console.log(x || z); // true
console.log(z || x); // true
console.log(z || q); // false
console.log('-----');
console.log(! x); // false
console.log(! z); // true
Toggle boolean
- true
- false
console.log('Start');
var b = true;
console.log(b); // true
b = ! b;
console.log(b); // false
b = ! b;
console.log(b); // true
console.log('End');
Convert Truthy and Falsy values to boolean
- !!
"use strict";
var x = undefined;
console.log(x); // undefined
var y = !! x;
console.log(y); // false
x = "hello";
console.log(x); // hello
y = !! x;
console.log(y); // true
Short circuit
"use strict";
var salary = 8000;
var money = 100000;
function check_standard_of_living() {
if (money > 1000000 || salary++ > 10000) {
console.log('I can live well.');
}
console.log('I have ' + money + ' in the bank and I get ' + salary + ' as salary.')
}
check_standard_of_living();
check_standard_of_living();
check_standard_of_living();
money = 2000000;
check_standard_of_living();
check_standard_of_living();
check_standard_of_living();
I have 100000 in the bank and I get 8001 as salary.
I have 100000 in the bank and I get 8002 as salary.
I have 100000 in the bank and I get 8003 as salary.
I can live well.
I have 2000000 in the bank and I get 8003 as salary.
I can live well.
I have 2000000 in the bank and I get 8003 as salary.
I can live well.
I have 2000000 in the bank and I get 8003 as salary.
Better not to use ++, -- auto-increment and auto-decrement inside other expressions.
if - else
- if
- else
"use strict";
var x = 23;
var y = 42;
if (x < y) {
console.log(x + ' is smaller than ' + y);
} else {
console.log(x + ' is NOT smaller than ' + y);
}
// 23 is smaller than 42
if () {
...
} else {
if () {
...
} else {
if () {
...
}
}
}
else if
- else if
- ===
"use strict";
var x = 23;
var y = 42;
if (x === y) {
console.log("equal");
} else if (x < y) {
console.log("x is smaller than y");
} else {
console.log("x is bigger than y");
}
// x is smaller than y
Switch (case) in JavaScript
- switch
- case
- default
- break
"use strict";
var n = 42;
switch (n) {
case 1: {
console.log(1);
break;
};
case 42: {
console.log(42);
};
case 'other': {
console.log('other');
};
default: {
console.log('default')
};
}
// 42
// other
// default
- You'd better call "break" at the end of each "case" statement or this will fall through and will execute all the lower cases without checking their condition.
- Switch uses === for comparision.
- We can put variables instead of the fixed values in the 'case' statements.
- If the same value appears in more than one 'case', only the first one will match.
Math.round
- Math
- round
"use strict";
var v = "3.14159265359";
console.log(v); // 3.14159265359
console.log(100 * v); // 314.159265359
console.log(Math.round(v)); // 3
console.log(Math.round(100 * v)/100); // 3.14
console.log(Math.round(3.9)); // 4
console.log(Math.round(3.5)); // 4
console.log(Math.round(3.49999999)); // 3
Rounding up a float to integer (ceil)
- ceil
let pi = 3.14;
var intvalue = Math.ceil( pi );
Rounding down a float to integer (floor)
- floor
let pi = 3.14;
var intvalue = Math.floor( pi );
Converting float to integer (trunc)
- floor
- round
- trunc
let pi = 3.14;
var intvalue = Math.round( pi );
// `Math.trunc` was added in ECMAScript 6
var intvalue = Math.trunc( pi );
Math.random
- random
"use strict";
var v = Math.random();
console.log(v); // 0.7282267780974507
Throw a dice (random integers 1-6)
- random
- floor
"use strict";
var v = Math.floor( 6 * Math.random() );
console.log(v); // 2
Exercise: Calculator
Create a web page with 3 input fields for 2 numbres and an operator such as +, -, *, or /. When the user clicks on the "Calculate" button, take the 3 values and calculate the result of the expression.
Solution: Calculator
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Calculator</title>
</head>
<body>
x: <input id="x">
operator: <input id="op">
y: <input id="y">
<button id="go">Calculate</button>
<div id="result"></div>
<script src="calculator.js"></script>
</body>
</html>
"use strict";
function calculate() {
var x = Number(document.getElementById('x').value);
var y = Number(document.getElementById('y').value);
var op = document.getElementById('op').value;
var result;
switch (op) {
case '+' : {
result = x+y;
break;
};
case '*' : {
result = x*y;
break;
};
case '-' : {
result = x-y;
break;
};
case '/' : {
result = x/y;
break;
};
};
document.getElementById('result').innerHTML = result;
return false;
}
document.getElementById('go').addEventListener('click', calculate);
Exercise: Guess number
When the page is loaded the computer "thinks" about a random whole number between 1 and 200, and displays an input box and a button "guess". The user types in a number and the computer will check if the given number is "smaller" or "larger" than the number it "though" about, or if they happen to be equal.
Once that's done, change the solution so it will count how many times has the user guessed before the coreect number was found.
Solution: Guess number
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Guess Number</title>
</head>
<body>
<input id="n">
<button id="go">Guess</button>
<div id="result"></div>
<script src="guess_number.js"></script>
</body>
</html>
"use strict";
var hidden = Math.round(Math.random()*200+0.5);
var counter = 0;
console.log(hidden);
function check_guess() {
counter++;
var n = Number(document.getElementById('n').value);
var result = 'equal after ' + counter + ' guesses';
if (n < hidden) {
result = n + ' is less than our number';
}
if (n > hidden) {
result = n + ' is greater than our number';
}
document.getElementById('result').innerHTML = result;
return false;
}
document.getElementById('go').addEventListener('click', check_guess);
Arrays in JavaScript
for-loop
- for
for (INITIALIZE; CONDITION; STEP) {
}
"use strict";
var i,j;
for (i=0; i < 6; i++) {
console.log(i);
}
console.log('---');
for (j=9; j > 6 ; j--) {
console.log(j);
}
while loop
- while
while (CONDITION) {
}
"use strict";
var n = 1;
while (n < 7) {
n++;
console.log(n);
}
2
3
4
5
6
7
Break out from while loop in JavaScript
- break
"use strict";
var n = 1;
while (n < 7) {
n++;
console.log(n);
if (n >= 4) {
break;
}
}
2
3
4
An infinite while loop
"use strict";
while (true) {
...
if (CONDITION) {
break;
}
}
Continue to next in while loop in JavaScript
- continue
"use strict";
var n = 1;
while (n < 7) {
n++;
if (n === 5) {
continue;
}
console.log(n);
}
2
3
4
5
6
7
do-while loop in JavaScript
- do while
The loop is executed at least once.
"use strict";
var n = 1;
do {
n++;
console.log(n);
} while (n < 1);
For example you ask a question and let the user guess. Every time you compare the answer to the expected answer. You will need to get the value from the user before the first compare, and then if it fails, ask the user again. If you use plain "while" then you'll have to read from the user once before the while-loop and then inside the loop too. OTOH if you use the "do-while" loop, it is enought to ask in the do-block.
new Array
- new
- Array
- for
- length
"use strict";
var names = new Array("Foo", "Bar", "Moose");
console.log(names); // [ "Foo", "Bar", "Moose" ]
console.log(names[1]); // "Bar"
var i;
for (i = 0; i < names.length; i++) {
console.log(names[i]);
}
Literal array: []
- []
"use strict";
var fruits = ["apple", "banana", "peach"];
var i;
console.log(fruits); // [ "apple", "banana", "peach" ]
console.log(fruits[1]); // "banana"
for (i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
Enlarge array with elements
"use strict";
var names = ['Foo', 'Bar', 'Baz'];
console.log(names); // ["Foo", "Bar", "Baz"]
console.log(names[0]); // "Foo"
names[3] = 'Moo';
console.log(names[3]); // "Moo"
console.log(names.length); // 4
names[6] = "Other";
console.log(names.length); // 7
console.log(names[4]); // undefined
console.log(names[6]); // "Other"
console.log(names); // [ "Foo", "Bar", "Baz", "Moo", <2 empty slots>, "Other" ]
JavaScript Arrays - pop - push
- pop
- push
"use strict";
var names = [ "foo" ];
console.log(names); // [ "foo" ]
names[1] = "bar";
console.log(names); // [ "foo", "bar" ]
names.push("moo");
console.log(names); // [ "foo", "bar", "moo" ]
names.push("qux", "zorg");
console.log(names); // [ "foo", "bar", "moo", "qux", "zorg" ]
var last = names.pop();
console.log(last); // "zorg"
console.log(names); // [ "foo", "bar", "moo", "qux" ]
JavaScript Arrays - shift - unshift
- shift
- unshift
"use strict";
var names = [ "foo", "bar", "moo", "qux" ];
var first = names.shift();
console.log(first); // "foo"
console.log(names); // [ "bar", "moo", "qux" ]
names.unshift("boo");
console.log(names); // [ "boo", "bar", "moo", "qux" ]
Two dimensional array (matrix)
- matrix
"use strict";
var entries = [
["Foo", 123],
["Bar", 345],
["Moo", 230],
];
var i;
for (i = 0; i < entries.length; i++) {
console.log(entries[i][0] + ' - ' + entries[i][1]);
}
// Foo - 123
// Bar - 345
// Moo - 230
For-in loop on array
- for
- in
We don't have to use the C-style for-loop on arrays. We can use the simpler, for-in construct. It will iterate over the index of the array.
"use strict";
var names = ["Foo", "Bar", "Qux"];
var v;
for (v in names) {
console.log(v);
}
// 0
// 1
// 2
ForEach loop on array
- forEach
Another way to iterate over the elements of an array is to use the forEach method of the array. It gets a function as an argument and it will call that function with each one of the values of the array.
"use strict";
var names = ["Foo", "Bar", "Qux"];
names.forEach(function(v) {
console.log(v);
})
// Foo
// Bar
// Qux
Reverse array
- reverse
"use strict";
var names = ['Foo', 'Bar', 'Morse'];
console.log(names); // [ 'Foo', 'Bar', 'Morse' ]
names.reverse();
console.log(names); // [ 'Morse', 'Bar', 'Foo' ]
Concatenate arrays
- concat
"use strict";
var names = ['Foo', 'Bar', 'Moo'];
var people = ['Joe', 'Mary'];
var joint_list = names.concat(people, 'Morgo', ['Bare', 'Array']);
console.log(names); // [ 'Foo', 'Bar', 'Moo' ]
console.log(people); // [ 'Joe', 'Mary' ]
console.log(joint_list);
// [ 'Foo', 'Bar', 'Moo', 'Joe', 'Mary', 'Morgo', 'Bare', 'Array' ]
Concatenate array with object
"use strict";
var names = ['Foo', 'Bar'];
console.log(names.concat({ x:'y'})); // [ 'Foo', 'Bar', { x: 'y' } ]
console.log(names.concat(/regex/)); // [ 'Foo', 'Bar', /regex/ ]
Concatenate array is shallow
"use strict";
var names = [
['Foo', 23],
['Bar', 17]
];
var other = names.concat('Joe');
console.log(names); // [ [ 'Foo', 23 ], [ 'Bar', 17 ] ]
console.log(other); // [ [ 'Foo', 23 ], [ 'Bar', 17 ], 'Joe' ]
names[0][0] = 'Moooo';
console.log(names); // [ [ 'Moooo', 23 ], [ 'Bar', 17 ] ]
console.log(other); // [ [ 'Moooo', 23 ], [ 'Bar', 17 ], 'Joe' ]
Array indexOf lastIndexOf
- indexOf
- lastIndexOf
"use strict";
var names = ['Foo', 'Bar', 'Morse', 'Foo', 'Bar', 'Luke', 'Lea', 'Han Solo'];
console.log(names.indexOf('Foo')); // 0
console.log(names.indexOf('Foo', 1)); // 3
console.log(names.lastIndexOf('Foo')); // 3
console.log(names.lastIndexOf('Foo', 2)); // 0
console.log(names.indexOf('Vader')); // -1
Array slice (range) or splice
- slice
- splice
slice(from, to) - shallow copy of a range defined by its end points.
splice(from, length) - shallow copy of a range defined by the number of elements in it
"use strict";
var names = ['Foo', 'Bar', 'Morse', 'Luke', 'Lea', 'Han Solo'];
console.log(names.slice(3, 5)); // [ 'Luke', 'Lea']
console.log(names.splice(2, 3)); // ['Morse', 'Luke', 'Lea']
Split string, join array
- split
- join
"use strict";
var names = ['Foo', 'Bar', 'Morse', 'Foo', 'Bar', 'Luke', 'Lea', 'Han Solo'];
var str = names.join(':');
console.log(str); // Foo:Bar:Morse:Foo:Bar:Luke:Lea:Han Solo
var n = str.split(':');
console.log(n);
// [ 'Foo', 'Bar', 'Morse', 'Foo', 'Bar', 'Luke', 'Lea', 'Han Solo' ]
Deep copy with JSON
- JSON
JSON.parse(JSON.stringify(o));
Exercise: Count digits
Given an array of strings in which every string contains numbers separated by spaces, count how many times each digit appears?
"use strict";
var numbers = [
'123 124 56',
'68 23 '
];
...
Expected output:
0 0
1 2
2 3
3 2
4 1
5 1
6 2
7 0
8 1
9 0
Solution: Count digits
"use strict";
var numbers = [
'123 124 56',
'68 23 '
];
var i, j, counter = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
for (i = 0; i < numbers.length; i++ ) {
for (j = 0; j < numbers[i].length; j++) {
if (numbers[i][j] !== ' ') {
counter[ numbers[i][j] ]++;
}
}
}
for (i = 0; i < 10; i++) {
console.log(i, counter[i]);
}
Exercise: Count characters
Given a string count how many time each character appears. See this skeleton:
"use strict";
var string = "hello world";
Solution: Count characters
"use strict";
var string = "hello world";
var i, j;
var counter = [];
var chars = [];
for (i = 0; i < string.length; i++) {
// console.log(string[i]);
var found = false;
for (j = 0; j < chars.length; j++) {
if (chars[j] == string[i]) {
counter[j]++;
found = true;
break;
}
}
if (! found) {
chars.push(string[i]);
counter.push(1);
}
}
for (i = 0; i < chars.length; i++) {
console.log(chars[i], counter[i]);
}
Exercise: Number guessing - history
In the previous chapter we had an exercise in which the computer "thought" a number, and the user had to guess it.
- Show the history of guesses.
- Show if we are getting closer (warm) or farther away (cold) relative to the previous guess.
Solution: Number guessing - history
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Guess Number</title>
</head>
<body>
<input id="n">
<button id="go">Guess</button>
<div id="result"></div>
<script src="guess_number_history.js"></script>
</body>
</html>
"use strict";
var hidden = Math.round(Math.random()*200+0.5);
var guess_history = [];
var i;
console.log(hidden);
function check_guess() {
var n = Number(document.getElementById('n').value);
var result = 'equal after ' + guess_history.length + ' guesses.';
if (n < hidden) {
result = n + ' is less than our number';
}
if (n > hidden) {
result = n + ' is greater than our number';
}
if (guess_history.length > 0 && n !== hidden) {
//console.log(guess_history[ ]);
if (Math.abs(guess_history[guess_history.length - 1] - hidden) < Math.abs(n - hidden)) {
result += ' (<b>cold</b>)';
} else {
result += ' (<b>warm</b>)';
}
}
guess_history.push(n);
result += '<br>';
result += '<b>History:</b><br>'
for (i = guess_history.length-1; i >=0; i--) {
result += guess_history[i] + '<br>';
}
document.getElementById('result').innerHTML = result;
return false;
}
document.getElementById('go').addEventListener('click', check_guess);
Exercise: Number guessing
Once that works, add a new button allowing the user to start a new game once one was finished.
Once that works allow the user to "give up" the current game and start a new one.
Exercise: Reverse the number guessing game
In this version you think about a number between 1-200 and press the "start" button. Then the computer guesses and you have to tell it if the guessed number is the one you thought about, or if it is smaller or larger than what you thought about. For this you might need to add 3 new buttons. If your answer was "smaller" or "bigger", the computer guesses again. How many guesses does your program need?
Solution: Reverse the number guessing game
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Guess Number</title>
</head>
<body>
<button id="go">Start</button>
<button id="bigger">Bigger</button>
<button id="smaller">Smaller</button>
<button id="bingo">Bingo</button>
<div id="result"></div>
<script src="reverse_number_guessing.js"></script>
</body>
</html>
"use strict";
var guess = null;
var min = null;
var max = null;
var lower_limit = 1;
var upper_limit = 200;
function start_guessing() {
min = lower_limit;
max = upper_limit;
show();
}
function guess_smaller() {
max = guess;
show();
}
function guess_bigger() {
min = guess;
show();
}
function bingo() {
document.getElementById('result').innerHTML = "Yeah!";
return false;
}
function show() {
guess = Math.round((max+min)/2);
document.getElementById('result').innerHTML = guess;
return false;
}
document.getElementById('go').addEventListener('click', start_guessing);
document.getElementById('bigger').addEventListener('click', guess_smaller);
document.getElementById('smaller').addEventListener('click', guess_bigger);
document.getElementById('bingo').addEventListener('click', bingo);
JavaScript functions
Function statements
- function
"use strict";
function add(x, y) {
var z = x+y;
return z;
}
console.log(add(2, 3)); // 5
console.log(add(19, 23)); // 42
Function expressions (Anonymous functions)
"use strict";
var add = function (x, y) {
var z = x+y;
return z;
}
console.log(add(2, 3)); // 5
Unknown number of function arguments
- arguments
"use strict";
var sum = function () {
var i, s = 0;
for (i=0; i < arguments.length; i++) {
s += arguments[i];
}
return s;
}
console.log(sum(2, 5, 1)); // 8
Assign function
"use strict";
var add = function (a, b) {
return a+b;
}
var sum = add;
console.log(add(2, 3)); // 5
console.log(sum(3, 4)); // 7
console.log(sum); // [Function]
// function add(a, b) {
// return a+b;
// }
We can assign the name of a function to a variable. Now we have the same function with two names. We can call both using the function_name() notation. We can even print the source code of the function in either of the variables.
Passing functions as parameter of another function
var add = function (a, b) {
return a+b;
}
var subtract = function (a, b) {
return a-b;
}
var handle_data = function (func) {
// get data from user or other external source
var x = 2;
var y = 3;
return func(x, y);
}
console.log(handle_data(add)); // 5
console.log(handle_data(subtract)); // -1
Recursive function
"use strict";
var fact = function (n) {
if (n === 1) {
return 1;
}
return n * fact(n-1);
}
console.log(fact(4)); // 24
Dispatch table
"use strict";
var dispatch_table = {
'+' : function (a, b) {
return a+b;
},
'-' : function (a, b) {
return a-b;
}
};
function run(dt, op, x, y) {
if (! dt[op]) {
console.log("Invalid operator: " + op);
return;
}
return dt[op](x, y);
}
console.log(run(dispatch_table, '-', 2, 3)); // -1
console.log(run(dispatch_table, '+', 2, 3)); // 5
console.log(run(dispatch_table, '*', 2, 3)); // Invalid operator: *
Variable scope
- scope
"use strict";
var x = 42;
var add = function (a, b) {
var x = a + b; // private x
return x;
}
var multiply = function (a, b) {
x = a * b; // global x
return x;
}
console.log(x); // 42
console.log(add(2, 3)); // 5
console.log(x); // 42
console.log(multiply(2, 4)); // 8
console.log(x); // 8
There is global scope and there is local scope inside functions.
Scope in if block
"use strict";
var x = 42;
console.log(x); // 42
if (true) {
var x = 23; // var does not do anything here
console.log(x); // 23
}
console.log(x); // 23
if-blocks, don't create local scopes.
Variable in for loop
"use strict";
// var i = 5;
for (var i = 0; i < 3; i++) {
console.log(i);
}
console.log('--');
console.log(i);
for-oops, don't create local scopes.
Immediate Execution using ()
- ()
"use strict";
var greeting = function () {
return "hi there";
}
console.log(greeting); // [Function]
console.log(greeting()); // hi there
var hw = function () {
return "Hello World";
}();
console.log(hw); // Hello World
Immediate Execution with parameters using ()
- ()
"use strict";
var greeting = function (name) {
return "hi " + name;
}
console.log(greeting); // [Function]
console.log(greeting('Foo')); // hi Foo
var hw = function (name) {
return "Hello " + name;
}('Bar');
console.log(hw); // Hello Bar
Private Global Scope
(function() {
"use strict";
var me = 'Foo';
var greet = function (name) {
return 'Hello ' + name;
}
var res = greet(me);
console.log(res); // Hello Foo
}())
Exercise: Reverse Polish Calculator
A regular "inline" calcularo accespts the operator between two operands: 2 + 3. If we have more operators and operands such a 2 + 3 * 7, we (and the computer) need to know the precedence of the operators.
In a Polish calculator we would write the operator before the operands: + 2 3. In a Reverse Polish Calculator we would write the operator after the operands: 2 3 + or 2 3 7 * +. Once we wrote down an expression the order of computation is clear. (First apply * to 3 and 7 and the apply + to the result and 2)
In this exercise you are requeted to implement a Reverse Polish Calculator that can handle +, -, *, /, and that will remove and display the last value when an = character is entered.
Reverse Polish Calculator in JavaScript
"use strict";
function RPN() {
var stack = [];
var f = function() {
};
f.size = function() {
return stack.length;
}
f.exe = function(v) {
if (v === '+') {
var res = stack.pop() + stack.pop();
stack.push(res);
return;
}
if (v === '*') {
var res = stack.pop() * stack.pop();
stack.push(res);
return;
}
if (v === '-') {
var res = stack.pop() - stack.pop();
stack.push(res);
return;
}
if (v === '/') {
var res = stack.pop() / stack.pop();
stack.push(res);
return;
}
if (v === '=') {
return stack.pop();
}
stack.push(v);
};
return f;
}
var r = RPN();
r.exe(2);
r.exe(3);
r.exe(4);
console.log(r.size()); // 3
r.exe('+');
r.exe('*');
console.log(r.exe('=')); // 14
console.log(r.size()); // 0
r.exe(6);
r.exe(2);
r.exe('-');
console.log(r.exe('=')); // -4
Order
function some_function() {
console.log("in some_function");
}
console.log("before");
some_function();
console.log("after");
export default function fibo(n) {
if (n == 1) {
return [1];
}
if (n == 2) {
return [1, 1];
}
let numbers = [1]
for (let c = 3; c <= n; c++) {
numbers.push(numbers[-1] + numbers[-2]);
}
return numbers;
}
JavaScript Objects
new empty object
"use strict";
var MyThing = function() {
}
var t = new MyThing;
console.log(MyThing); // function MyThing()
console.log(t); // Object { }
t.a = 23;
console.log(t);
t.doit = function() {
console.log('doit');
console.log(this);
console.log(this.a);
}
t.doit();
new Objects
"use strict";
var MyThing = function() {
this.smile = function() {
console.log("This thing can smile!");
}
}
var t = new MyThing;
console.log(MyThing); // function MyThing()
console.log(t); // Object { }
t.smile(); // "This thing can smile!"
new Object with attribute
"use strict";
var MyPerson = function(fname) {
this.first_name = fname;
}
var m = new MyPerson('Foo');
console.log(MyPerson); // function MyPerson()
console.log(m); // Object { }
Literal Objects
- object
- hash
- dictionary
Keys in the object don't need to be quoted, unless they contain special characters, like a dash.
"use strict";
var email = {
"from" : "js@bar.com",
"to" : [ "foo@bar.com", "qux@bar.com" ],
"subject" : "Hello World",
"mime-type" : "text"
};
console.log(email);
Object { from: "js@bar.com", to: Array[2], subject: "Hello World", mime-type: "text" }
Dumping data structures (for debugging)
- JSON
- Dump
"use strict";
var user = {
'name' : 'Foo Bar',
'emails' : [
'foo@bar.com',
'foobar@gmail.com'
]
};
console.log(JSON.stringify(user, undefined, 2));
"{
"name": "Foo Bar",
"emails": [
"foo@bar.com",
"foobar@gmail.com"
]
}"
Access JavaScript object attributes
"use strict";
var email = {
"from" : "js@bar.com",
"to" : [ "foo@bar.com", "qux@bar.com" ],
"subject" : "Hello World",
"mime-type" : "text"
};
console.log(email["from"]); // "js@bar.com"
console.log(email["mime-type"]); // "text"
console.log(email.from); // "js@bar.com"
// console.log(email.mime-type); // ReferenceError: type is not defined
email["text"] = "Content";
console.log(JSON.stringify(email, undefined, 2));
"{
"from": "js@bar.com",
"to": [
"foo@bar.com",
"qux@bar.com"
],
"subject": "Hello World",
"mime-type": "text",
"text": "Content"
}"
Change JavaScript object attributes
"use strict";
var email = {
"from" : "js@bar.com",
"to" : [ "foo@bar.com", "qux@bar.com" ],
"subject" : "Hello World",
"mime-type" : "text"
};
console.log(email);
email["subject"] = "About JavaScript";
console.log(email);
email["text"] = "Content";
console.log(email);
Add JavaScript object attributes
"use strict";
var email = {
"from" : "js@bar.com",
"to" : [ "foo@bar.com", "qux@bar.com" ],
};
console.log(email);
email["text"] = "Content";
console.log(email);
Delete property from object
- delete
"use strict";
var x = {
'a' : 1,
'b' : 2
};
console.log(x); // Object {a: 1, b: 2}
var ret = delete(x['a']);
console.log(x); //Object {b: 2}
console.log(ret); // true
var ret = delete(x['c']);
console.log(x); //Object {b: 2}
console.log(ret); // true
for-in loop on JavaScript object
- for in
- hasOwnProperty
"use strict";
var email = {
"from" : "js@bar.com",
"to" : [ "foo@bar.com", "qux@bar.com" ],
"subject" : "Hello World",
"mime-type" : "text"
};
console.log(email);
var k;
for (k in email) {
if (! email.hasOwnProperty(k)) {
continue;
}
console.log('k:', k);
console.log('v:', email[k]);
}
Count words
"use strict";
var text = 'this is a constructor this is not';
var words = text.split(/\s+/);
var count = {};
var i;
for (i in words) {
var word = words[i];
if (count[word] === undefined) {
count[word] = 0;
}
count[word]++;
}
console.log(count);
// Object { this: 2, is: 2, a: 1, constructor: NaN, not: 1 }
'constructor' needs special treatment as that attribute is already part of the object.
Count words fixed
"use strict";
var text = 'this is a constructor this is not';
var words = text.split(/\s+/);
var count = {};
var i;
for (i in words) {
var word = words[i];
if (typeof count[word] !== 'number') {
count[word] = 0;
}
count[word]++;
}
console.log(count);
// Object { this: 2, is: 2, a: 1, constructor: 1, not: 1 }
keys of an object
- keys
"use strict";
var count = {
'foo': 23,
'bar': 42,
'morse':100
};
console.log(Object.keys(count)); // [ 'foo', 'bar', 'morse' ]
var keys = Object.keys(count);
var i;
for (i = 0; i < keys.length; i++) {
console.log(i, keys[i], count[ keys[i] ]);
}
// 0 'foo' 23
// 1 'bar' 42
// 2 'morse' 100
Advanced JavaScript
Stringify NaN, Infinite, and null
- NaN
- Infinite
- null
"use strict";
var x = 2/0;
var y = -2/0;
var z = x+y;
var n = null;
console.log(JSON.stringify({ "x" : x }, undefined, 2));
console.log(JSON.stringify({ "y" : y }, undefined, 2));
console.log(JSON.stringify({ "z" : z }, undefined, 2));
console.log(JSON.stringify({ "n" : n }, undefined, 2));
{
"x": null
}
{
"y": null
}
{
"z": null
}
{
"n": null
}
JSON parsing NaN, Infinite, and null
<script>
var str = '{ "v" : 42 }';
console.log(JSON.parse(str)); // Object {v: 42}
str = '{ "n" : null }';
console.log(JSON.parse(str)); // Object {n: null}
str = '{ "x" : NaN }';
console.log(JSON.parse(str)); // Uncaught SyntaxError: Unexpected token N
str = '{ "y" : Infinite }';
console.log(JSON.parse(str)); // Uncaught SyntaxError: Unexpected token I
</script>
Object { v: 42 } json_parse_nan.html:3:0
Object { n: null } json_parse_nan.html:6:0
SyntaxError: JSON.parse: unexpected character at line 1 column 9 of the JSON data
JSON parsing NaN, Infinite, and null (Node.js" %}
"use strict";
var str = '{ "v" : 42 }';
console.log(JSON.parse(str)); // Object {v: 42}
str = '{ "n" : null }';
console.log(JSON.parse(str)); // Object {n: null}
str = '{ "x" : NaN }';
console.log(JSON.parse(str)); // Uncaught SyntaxError: Unexpected token N
str = '{ "y" : Infinite }';
console.log(JSON.parse(str)); // Uncaught SyntaxError: Unexpected token I
{ v: 42 }
{ n: null }
undefined:1
{ "x" : NaN }
^
SyntaxError: Unexpected token N
typeof
- typeof
"use strict";
console.log(typeof true); // boolean
console.log(typeof "hello world"); // string
console.log(typeof 42); // number
var answer = 52;
console.log(typeof answer); // number
var name;
console.log(typeof name); // string
console.log(typeof 1/0); // Nan
console.log(typeof undefined); // undefined
console.log(typeof null); // object
typeof and null
"use strict";
check_null(null);
check_null({});
check_null(42);
function check_null(x) {
console.log(typeof x);
console.log(x === null ? "null" : "not null");
console.log(x && typeof x === 'object' ? "object" : "null");
}
Delayed (scheduled) execution - setTimeout
- setTimeout
"use strict";
function later() {
console.log('later');
}
setTimeout(later, 1000);
console.log('now');
Recurring execution - setInterval
- setInterval
"use strict";
function later() {
console.log('later');
}
setInterval(later, 1000);
console.log('now');
Stop recurring execution - clearInterval
- clearInterval
"use strict";
function later() {
console.log('later');
}
function stop() {
console.log('clearing now');
clearInterval(timer);
}
var timer = setInterval(later, 1000);
setTimeout(stop, 6000);
console.log('now');
Dates
- Date
"use strict";
var d = new Date;
console.log(d); // Mon Jul 20 2015 14:46:51 GMT+0300 (IDT)
console.log(Math.abs(new Date() - d)); // 25
setTimeout(show, 10);
d = new Date('1995-12-17T03:24:00');
console.log(d); // Sun Dec 17 1995 05:24:00 GMT+0200 (IST)
function show() {
console.log(Math.abs(new Date() - d)); // 618222446613
}
sort
- sort
- localCompare
"use strict";
var names = ['Foo', 'Bar', 'Moose', 'Zorg', 'Baz', 'Moo'];
console.log(JSON.stringify(names, undefined, 2));
// ['Foo', 'Bar', 'Moose', 'Zorg', 'Baz', 'Moo'];
names.sort();
console.log(JSON.stringify(names, undefined, 2));
// ['Bar', 'Baz', 'Foo', 'Moo', 'Moose', 'Zorg'];
var numbers = [3, 12, 4];
console.log(JSON.stringify(numbers, undefined, 2));
// [3, 12, 4];
numbers.sort();
console.log(JSON.stringify(numbers, undefined, 2));
// [12, 3, 4];
numbers.sort(function(a, b) { return a-b });
console.log(JSON.stringify(numbers, undefined, 2));
// [3, 4, 12];
var names = ['Foo', 'Bar', 'Moose', 'Zorg', 'Baz', 'Moo'];
names.sort(function(a, b) { return a.length - b.length });
console.log(JSON.stringify(names, undefined, 2));
// ['Foo', 'Bar', 'Baz', 'Moo', 'Zorg', 'Moose'];
names.sort(function(a, b) { return a.length - b.length || a.localeCompare(b)});
console.log(JSON.stringify(names, undefined, 2));
// ['Bar', 'Baz', 'Foo', 'Moo', 'Zorg', 'Moose'];
sort object
"use strict";
var people = {
"11" : {
"name" : "Foo",
"age" : 30
},
"20" : {
"name" : "Bar",
"age" : 28
}
};
console.log(JSON.stringify(people, undefined, 2));
for (var i in people) {
console.log(i); // 11 20
}
var ids = [];
for (var i in people) {
ids.push(i);
}
console.log(JSON.stringify(ids, undefined, 2));
ids.sort(function(a, b) { return people[a]['age'] - people[b]['age'] } );
console.log(JSON.stringify(ids, undefined, 2));
var names = ['Foo', 'Bar'];
for (var i in names) {
console.log(i); // 0 1
}
sort datestrings
"use strict";
var dates = ['2013-04-02', '2012-05-06', '2014-10-10'];
console.log(dates);
console.log(dates.sort());
console.log(dates);
var dates = [
{
'd' : '2013-04-02',
'n' : 'one'
},
{
'd' : '2012-05-06',
'n' : 'two'
},
{
'd' : '2014-10-10',
'n' : 'three'
}
];
console.log(JSON.stringify(dates, undefined, 2));
dates.sort(function(a, b) { return a['d'].localeCompare(b['d']) });
console.log(JSON.stringify(dates, undefined, 2));
map
- map
"use strict";
var numbers = [2, 5, 3, 7];
function double(n) {
return n*2;
}
var doubles = numbers.map(double);
console.log(numbers); // [2, 5, 3, 7]
console.log(doubles); // [4, 10, 6, 14]
var triples = numbers.map(function(n) { return n * 3 });
console.log(triples); // [6, 15, 9, 21]
Closures in JavaScript
"use strict";
function create_incrementer(n) {
return function (k) {
return k+n;
}
}
var inc_19 = create_incrementer(19);
var inc_17 = create_incrementer(17);
console.log(inc_19(23)); // 42
console.log(inc_17(23)); // 40
"use strict";
function create_counter(start, step) {
var n;
return function () {
if (n === undefined) {
n = start;
return n;
}
n += step;
return n;
}
}
var count_by_3 = create_counter(1, 3);
var count_by_7 = create_counter(2, 7);
console.log(count_by_3()); // 1
console.log(count_by_3()); // 4
console.log(count_by_3()); // 7
console.log(count_by_7()); // 2
console.log(count_by_7()); // 9
console.log(count_by_7()); // 16
console.log(count_by_3()); // 10
console.log(count_by_7()); // 23
Exception handling
- try
- catch
function add(x, y) {
if (Number(x) !== x) {
throw {
'name': 'TypeError',
'message': 'First argument is not a number'
};
}
if (Number(y) !== y) {
throw {
'name': 'AnyNameCanComeHere',
'message': 'Second argument is not a number'
};
}
return x+y;
}
var arr = [
[2, 3],
['two', 3],
[2, 'three'],
[7, 9]
];
var i, res;
for (i=0; i<arr.length; i++) {
v = arr[i];
console.log(v);
try {
res = add(v[0], v[1]);
} catch(e) {
console.log('exception:', e);
}
console.log(res);
}
NodeJS: command line arguments
- argv
console.log(process.argv);
console.log('--------------');
var i;
for (i = 0; i < process.argv.length; i++) {
console.log(process.argv[i]);
}
$ node examples/js/nodejs_argv.js foo bar
[ 'node',
'/Users/gabor/work/training/javascript/examples/js/nodejs_argv.js',
'foo',
'bar' ]
--------------
node
/Users/gabor/work/training/javascript/examples/js/nodejs_argv.js
foo
bar
NodeJS: prompt on STDIN
- prompt
npm install prompt
var prompt = require('prompt');
prompt.start();
prompt.get(['fname', 'lname'], function (err, result) {
var fname = result.fname;
var lname = result.lname;
console.log('Command-line input received:');
console.log(' fname: ' + fname);
console.log(' lname: ' + lname);
prompt.get(['email'], function (err, result) {
var email = result.email;
console.log('Internal:');
console.log(' fname: ' + fname);
console.log(' lname: ' + lname);
console.log(' email: ' + email);
});
});
Transliterate
Work in progress...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Transliterate</title>
</head>
<body>
Latin letters: <textarea id="latin" rows="10" cols="50"></textarea>
<button id="go">Guess</button>
<div id="result"></div>
<script src="transliterate.js"></script>
</body>
</html>
"use strict";
var hidden = Math.round(Math.random()*200+0.5);
console.log(hidden);
// ramat chaim => 'חיים רמת'
// 'ch' : 'ח'
var tr = {
a: 'א',
b: 'ב',
c: '',
d: 'ד',
e: '',
f: 'פ',
g: 'ג',
h: '',
i: 'י',
j: '',
k: '',
l: 'ל',
m: 'מ',
n: 'נ',
o: 'ו',
p: '',
q: '',
r: 'ר',
s: 'ש',
t: 'ת',
' ': ' '
}
function transliterate() {
var latin = document.getElementById('latin').value;
var result = '';
var i;
for (i = 0; i < latin.length; i++) {
console.log(latin[i]);
if (tr[ latin[i]] ) {
result += tr[ latin[i]];
}
}
document.getElementById('result').innerHTML = result;
return false;
}
document.getElementById('latin').addEventListener('keyup', transliterate);
RegExp - Regular Expressions in JavaScript
RexExp Object
<script>
var text = 'There is a number 42 in this an another number #123 but nothing else.';
console.log(text);
// var p = new RegExp('\\d+');
// var p = new RegExp(/\d+/);
var p = /\d+/;
console.log(p);
var match = p.exec(text);
if (match) {
console.log(match);
console.log(match[0]);
console.log(match['index']);
console.log(match['input']);
}
</script>
Match
<script>
var text = 'There is a number 42 in this an another number #123 but nothing else.';
console.log(text);
var pattern = /\d+/;
var match = pattern.exec(text);
if (match) {
console.log(match[0]);
}
var m = /\d+/.exec(text);
if (m) {
console.log(m[0]);
}
</script>
Substitute
- replace
- /g
Replace the first match of a regex by a string. Replace it globally.
<script>
var text = 'There is a number 42 in this an another number #123 but nothing else.';
console.log(text);
var changed = text.replace(/\d+/, 'NUMBER');
console.log(changed);
console.log(text);
var ch2 = text.replace(/\d+/g, 'NUMBER');
console.log(ch2);
</script>
RegExp cheat sheet
var pattern = new RegExp(pattern, modifiers);
. - match any character (except newline)
Character classes
- [abcde] - match a single character: a, b, c, d, or e
- [^abcde] - match a single character, except of a, b, c, d, and e
- [a-e] - the same as [abcde]
- \d - a single digit (the same as [0-9])
- \w - a single word characer (the same as [a-zA-Z0-9_]
- \s - a single white space
- \D - a single non-digit (the same as [^\d])
Alternation
- apple|banana
Grouping
()
Quantifiers
- ? - 0 or 1
-
-
- 1 or more
-
-
-
- 0 or more (any number)
-
- {n, m} - between n and m times (inclusive)
Modifiers
- i - case insensitive
- g - global
- m - multiline
DOM - Document Object Model
Inject content in HTML
- getElementById
- innerHTML
<div id="display"></div>
<script src="inject_content.js"></script>
"use strict";
document.getElementById('display').innerHTML = 'Hello World';
Add event listener
- addEventListener
<button id="btn">Click me</button>
<div id="display"></div>
<script src="add_event_listener.js"></script>
"use strict";
function clicked() {
document.getElementById('display').innerHTML = 'Hello World';
}
document.getElementById('btn').addEventListener('click', clicked);
Get value of input box
<input id="data" size="30">
<button id="btn">Click me</button>
<div id="display"></div>
<script src="get_value_of_input_box.js"></script>
"use strict";
function clicked() {
var input_value = document.getElementById('data').value;
document.getElementById('display').innerHTML = input_value;
}
document.getElementById('btn').addEventListener('click', clicked);;
Get value of selected option
- selectedIndex
- getElementById
- addEventListener
- click
<select id="id_of_select">
<option></option>
<option value="2">two</option>
<option value="3">three</option>
<option value="4">four</option>
</select>
<button id="btn">Show selected</button>
<div id="display"></div>
<script src="get_selected_option.js"></script>
"use strict";
function show_selected() {
var selector = document.getElementById('id_of_select');
var value = selector[selector.selectedIndex].value;
document.getElementById('display').innerHTML = value;
}
document.getElementById('btn').addEventListener('click', show_selected);;
Update selection box based on other selection
<html>
<head><title>Update form</title>
<script src="update_form.js" type="text/javascript"></script>
</head>
<body onload="fill_continents()">
<form method="POST" name="reg">
<select name="continent" onchange="change_continent()">
<option></option>
</select>
<br />
<select name="country" >
<option><option>
</select>
<br />
<input type="button" value="Next" onclick="submit_form()">
</form>
</body>
</html>
"use strict";
var continents = new Array();
continents['na'] = "North America";
continents['sa'] = "South America";
continents['eu'] = "Europe";
var countries = new Array();
countries['na'] = new Array();
countries['sa'] = new Array();
countries['eu'] = new Array();
countries['na']['usa'] = "USA";
countries['na']['ca'] = "Canada";
countries['eu']['de'] = "Germany";
countries['eu']['at'] = "Austria";
countries['sa']['ch'] = "Chile";
function fill_continents() {
document.reg.continent.length=1;
document.reg.continent[0].value = "";
document.reg.continent[0].text = "";
for (c in continents) {
var i = document.reg.continent.length++;
document.reg.continent[i].value = c;
document.reg.continent[i].text = continents[c];
}
}
function change_continent() {
var continent = document.reg.continent.value;
document.reg.country.length=1;
document.reg.country[0].value = "";
document.reg.country[0].text = "";
for (c in countries[continent]) {
var i = document.reg.country.length++;
document.reg.country[i].value = c;
document.reg.country[i].text = countries[continent][c];
}
}
function countries() {
for(i=0; i<countries.length; i++) {
alert(countries[i]);
}
}
function submit_form() {
//alert(reg.continent.value);
countries();
//alert(countries["us"]);
//alert("done");
}
Autoresizing Grid
<div id="loc"></div>
<style>
table {
width: 100%;
}
td {
border: solid 1px;
}
/*td:first-child {
background-color: red;
}*/
</style>
<script src="grid.js">
var resizer;
function create_table(n) {
var html = '<table>';
var needFirst = true;
for (var j = 0; j < 2; j++) {
html += '<tr>'
//if (needFirst) {
// html += '<td rowspan="2">full</td>';
// needFirst = false;
//}
for (var i=0; i<n; i++) {
html += '<td>' + j + ' ' + i + '</td>';
}
html += '</tr>';
}
html += '</table>';
//console.log(html);
document.getElementById('loc').innerHTML = html;
}
function resize_table() {
console.log(window.innerWidth);
console.log(window.innerHeight);
var trs = document.getElementsByTagName('tr');
for (var t=0; t < trs.length; t++) {
console.log(t);
trs[t].children[0].setAttribute("style", "width: " + ( 130 / n ) + '%');
//trs[t].children[0].setAttribute("style", "background-color: red");
}
}
function resize_table_later() {
if (resizer == null) {
resizer = new Date;
setTimeout(resize_table_later, 1000);
return;
}
if (Math.abs(new Date() - resizer) > 950) {
resizer = null;
console.log('resize');
}
}
var n = 4;
create_table(n);
resize_table()
//window.addEventListener('resize', resize_table_later);
</script>
Local storage - counter
Local storage - counter - reload the page several times
<script>
console.log('Start');
var n = localStorage.getItem('counter');
if (n === null) {
n = 0;
} else {
n++;
}
console.log(n);
localStorage.setItem("counter", n);
console.log('End');
</script>
Remove item from local storage - reset counter
Remove item from Local storage - reset counter
<script>
console.log('Start');
var before = localStorage.getItem('counter');
console.log(before);
localStorage.removeItem('counter');
var after = localStorage.getItem('counter');
console.log(after);
console.log('End');
</script>
Clear local storage
Remove all the data from the local storage
localStorage.clear();
Local storage - boolean
- JSON
In many browsers local storage can only store string. So when we store the boolean true or false, it actually stores the strings "true" or "false". In order to get back the real boolean values, we can use the JSON.parse() method.
Local storage - store boolean
<script>
console.log('Start');
var cond = localStorage.getItem('cond');
if (cond === null) {
console.log('was null setting to false');
cond = false;
} else {
cond = JSON.parse(cond)
}
console.log(cond);
cond = ! cond;
console.log(cond);
localStorage.setItem("cond", cond);
console.log('End');
</script>
Other
Date
<div id="date"></div>
<div id="time"></div>
<script>
function set_time() {
const now = new Date();
const dt = document.getElementById("date");
dt.innerHTML = now.toLocaleDateString("ISO", {
year: '2-digit',
month: '2-digit',
});
}
set_time();
</script>
Try Catch
<input type="number" id="a">
<input type="number" id="b">
<button id="calc">Divide</button>
<div id="result"></div>
<script>
// Apparently dividing by 0 is not an exception in JavaScript
// So I have stupid other
function calc() {
console.log('calc');
const res = div(document.getElementById('a').value, document.getElementById('b').value);
console.log(res);
document.getElementById('result').innerHTML = res;
}
function div(a, b) {
console.log(`div(${a},${b})`);
try {
const c = a / b;
if (c == 0) {
handle_zero();
}
return c;
} catch(error) {
console.log(`Eception: ${error}`);
}
}
document.getElementById('calc').addEventListener('click', calc);
</script>
Application
Install NodeJS
-
NodeJS (There is 18.16.0 LTS and Installing 20.3.0 Current)
-
Create a folder for the application, eg. examples/demo-app
-
Open CMD , cd to the folder of the application
cd examples/demo-app
-
Create project
npm init -y
(The -y will make it use the defaults withot prompting for answers) -
Install webpack by running
npm install --save-dev webpack webpack-cli
-
These will install it in the
node_modules
folder and it will also create the filespackage.json
andpackage-lock.json
Edit the package.json and add
"scripts": {
"build": "webpack --mode development",
"watch": "webpack --mode development --watch"
}
Then running
npm run build
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Getting Started</title>
</head>
<body>
<script src="./main.js"></script>
</body>
</html>
import add from './mymath';
console.log("hello world");
console.log(add(2, 8));
export default function add(x, y) {
return x + y;
}
{
"name": "demo-app",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode development",
"watch": "webpack --mode development --watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^5.86.0",
"webpack-cli": "^5.1.4"
}
}
JavaScript Appendix
Resources
- JavaScript and DHTML Cookbook By Danny Goodman
- JavaScript: The Definitive Guide, Fourth Edition By David Flanagan
- Eloquent JavaScript
- Mozilla Developer Network
- JS Fiddle
- Daily JS
- JavaScript Equality Table
- CodePen
JavaScript Videos
- Doug Crockford: JavaScript: The Good Parts (2009)
- Douglas Crockford: The Better Parts - Nordic.js (2014)
- Crockford on JavaScript - Episode IV: The Metamorphosis of Ajax (2011)
Other
- Douglas Crockford: Which way is forward (2013)
- avaScript You Need to Know for a Job
- [Constructors Considered Mildly Confusing](http://zeekat.nl/articles/constructors-considered-mildly-confusing.html" %}
JavaScript Tools
- JSLint Find common coding problems in JavaScript
- JSLint Errors a site to explain the errors of JSLint and to give suggestions how to fix them.
Reserved words in JavaScript
abstract boolean break byte case catch char class const continue debugger default delete do double else enum export extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var volatile void while with
Awful parts
Based on Appendix A of "JavaScript: The Good Parts"
- Global variables - (window.fname = 'Foo'; fname = 'Foo'; var fname = 'Foo';), declare variables using 'var' inside functions
- Scope only functions create local scope. Declare variable at the top of the functions.
- Semicolon insertion.
- Reserved words. var case = 23; SyntaxError: missing variable name
- Unicode: JavaScript characters are 16 bit (either UCS-2 or UTF-16. Most of them use UTF-16)
- typeof returning 'object' for null and array as well. typeof /a/ can be either 'object' or 'function' depending on browser.
- parseInt (leading 0 might make the number base-8 in some browsers. User radix to ensure base)
-
- can (numeric) add or concatenate strings. It works as 'add' only if both operands are numbers.
- Floating point - is not perfect representation. Use integers.
- NaN - NaN === NaN // false; NaN !== NaN // true; isNaN()
- Phony Arrays
- Falsy values
- hasOwnProperty is not truely reliable as it is a method that can be replaced.
- Objects are never truly empty like a hash in Perl or a dictionary in Python.
<script>
function foo() {
return
{
status: true
};
}
function bar() {
return {
status: true
};
}
var f = foo();
console.log(f); // undefined
var b = bar();
console.log(b); // Object { status: true }
</script>
<script>
var o1 = { name: 'Foo Bar' };
var o2 = { case: 'FB'}
console.log(o1);
console.log(o2);
console.log(o1.name);
console.log(o2.case);
// var case = 23; // SyntaxError: missing variable name
</script>
<script>
function is_object(thing) {
if (thing && typeof thing === 'object' && thing.constructor !== Array) {
return true; // thing is an object or an array!
} else {
return false;
}
}
console.log(is_object(null)); // false
console.log(is_object([])); // false
console.log(is_object({})); // true
</script>
<script>
function is_array(thing) {
if (thing && typeof thing === 'object' && thing.constructor === Array) {
return true; // thing is an object or an array!
} else {
return false;
}
}
console.log(is_array(null)); // false
console.log(is_array([])); // true
console.log(is_array({})); // false
</script>
<script>
console.log(parseInt("16")); // 16
console.log(parseInt("16 little men")); // 16 (no indication of imperfect parse)
console.log(parseInt("011")); // 11
console.log(parseInt("08")); // 8
console.log(parseInt("09")); // 9
console.log(parseInt("011", 8)); // 9
console.log(parseInt("09", 10)); // 9
</script>
Bad Parts
Based on Appendix B of "JavaScript: The Good Parts"
- == and != are bad. Always use === and !==
- with statement - avoid it
- eval - can be dangerous. Don't use it.
- continue - Douglas Crockford recommends to avoid it as you can write code without it.
- switch - Always break. Don't let it fall through.
- Block-less statement - always use {} after 'if'.
- ++ -- - Douglas Crockford recommends to avoid them. I think there are places where they can be used without problems.
- Bitwise operators - usually there is no need for them in JavaScript.
- Function statement vs Function expressions
- Typed Wrappers - avoid new Boolean, new Number, new String, new Array, new Object
- new - name constructor functions with leading capital letter, or even better: avoid using 'new'.
- void - avoid it
function add(x, y) {
...
}
add = function (x, y) {
...
};
A good way to enforce that everything is private is to wrap the whole code in an anonymous function expression, and execute it upon parsing it.
(function() {
...
// No new global variables
})();
The official ECMAScript grammar states, that if the first word is 'function' then it is part of a function statement, but this is a function expression. That why we add a pair of () around the function.
JSLint
Based on Appendix C of "JavaScript: The Good Parts"
- Declare all the variables using 'var'. Use /*global to declare what can be global.
- Members - JSLint will list all the used members.
- Options to JSLint
- Semicolons
- Line Breaking
- Comma - don't use it as operator
- Required Blocks
- Expression Statements
- for x in y - object.hasOwnProperty
- switch
- var
- with
- ...
Deep copy
- JSON
"use strict";
var cases = [
undefined,
null,
42,
"some string",
/some reg/,
['a', 'b'],
[
['Foo', 23],
['Bar', 17]
],
{ a: [ 'b', 'c'] }
];
var j;
var deep_copy = function(o) {
var i;
if (o === undefined || o === null || typeof o === 'number' || typeof o === 'string' || o instanceof RegExp) {
return o;
}
if (typeof o === 'object') {
if (Array.isArray(o)) {
var n = [];
for (i = 0; i < o.length; i++) {
n.push(deep_copy(o[i]));
}
return n;
}
}
var n = {};
var keys = Object.keys(o);
for (i = 0; i < keys.length; i++) {
n[ keys[i] ] = deep_copy(o[ keys[i] ]);
}
return n;
}
for (j = 0; j < cases.length; j++) {
console.log(j);
var dc = deep_copy(cases[j]);
console.log(cases[j]);
console.log(dc);
//console.log(dc === cases[j]);
}
Invocation patterns
-
this
-
Method invocation (this is bound to the object)
-
Function invocation (this is bound to the global object)
-
Constructor invocation
-
Apply invocation (this is bound to what we passed to it)
Apply
- apply
var f = function() {
console.log(this);
}
var x = { fname: 'Gabor'};
f.apply(x, [2, 3]);
getElementsByClassName
- getElementsByClassName
var buttons = document.getElementsByClassName('remove');
for (var i=0; i < buttons.length; i++) {
buttons[i].addEventListener('click', remove);
};