Skip to content
frecturesPublic

About

JavaScript

Resources

Stars

2 stars

Watchers

1 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JavaScript Basics

Douglas Crockford: “JavaScript is the only language (that I'm aware of) where people feel they don't need to learn it before using it.”

History

  • Main influences:
    • Scheme (Functions)
    • Self (Prototypes)
    • Java (Syntax)
    • Perl (Regex)
  • Initial version “Mocha”
    • written during 10 days in May 1995
    • by Brendan Eich @ Netscape
         Mocha
          LiveScript
           JavaScript
           | (JScript)
           |  |   ES1 ES2  ES3                                     ES5                    ES6
           |  |   |   |    |                                       |                      |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---
93  94  95  96  97  98  99  00  01  02  03  04  05  06  07  08  09  10  11  12  13  14  15
  |   |   |             |       |         |   | |     |       |   |               |       |
  |   |   Internet      |       JSON      |   | Ajax  jQuery  |   Node.js     Electron.js |
  |   |   Explorer      XMLHTTP           |   |               |                           |
  |   |                                   |   Mozilla         Google                      Microsoft
  |   Netscape                            |   Firefox         Chrome                      Edge
  |   Navigator                           |
  |                                       Apple
  Mosaic                                  Safari
  • Yearly releases since ECMAScript 2016 (ES7)

Platforms

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">

<link rel="stylesheet" href="pretty.css">

<script defer           src="bundle.js"></script>

</head>
<body>

<div style="display: flex">
   <textarea id="output" style="flex: 0px" rows="50"></textarea>
</div>

</body>
<script>

// ...
let response = await fetch("https://raw.githubusercontent.com/frectures/js/master/README.md");
let text     = await response.text();

document.getElementById("output").value = text;
// ...

</script>
</html>
  • Browser (frontend)
    • HTML (HyperText Markup Language)
    • CSS (Cascading Style Sheets)
    • JS (JavaScript)
    • DOM (Document Object Model)
  • Node.js (backend)
    • JavaScript runtime environment built on Chrome's V8 JavaScript engine
    • High-performance, asynchronous server code
  • Electron.js = Chromium + Node.js (cross-platform desktop applications)
    • Atom
    • Discord
    • Microsoft Teams
    • Skype
    • Visual Studio Code

Type system

  • Dynamically typed
  • Primitive types
    1. boolean
      • false
      • true
    2. number (double precision floating point)
      • 0.1 + 0.2 === 0.30000000000000004
      • 1.0 + 2.0 === 3.0
      • Contains all integers up to 253 = 9_007_199_254_740_992
    3. bigint (arbitrary precision integer)
      • 123n
      • BigInt(456)
    4. string (UTF-16)
      • "Ain't no sunshine"
      • 'The cow says: "Moo"'
      • `Frantic fans: "It's great!"`
      • "🤨".length === 2
        • UTF-16: 55358, 56616 (Java, C#, JavaScript)
        • UTF-8: 240, 159, 164, 168 (Go, Rust, Swift)
    5. undefined (mostly bugs)
      • uninitialized variables
      • function calls without return value
      • missing function arguments
      • missing object properties
      • array index out of bounds
    6. null (intentional absence)
  • Reference types
    • Object
      • Function
      • Array
      • ...

Exercise:

log(typeof false);
log(typeof 3.14);
log(typeof 123n);
log(typeof "hello");
log(typeof "hello"[0]);
log(typeof undefined);
log(typeof null);
log(typeof { name: "Brendan Eich", year: 1961, nerd: true });
log(typeof Math.sin);
log(typeof [ 9, 10, 11, 12 ]);

Martin Fowler: “Choosing JavaScript was deeply ironic for me, as many readers may know, I'm not a fan of it. It has too many awkward edge cases and clunky idioms. But the compelling reason for choosing it over Java is that JavaScript isn't wholly centered on classes. There are top-level functions, and use of first-class functions is common. This makes it much easier to show refactoring out of the context of classes.”

Variables

  • var variables have function scope:
function f() {
    log(x); // undefined
    {
        log(x); // undefined

        var x = 42;

        log(x); // 42
    }
    log(x); // 42
}
  • let variables have block scope:
function f() {
    log(x); // ReferenceError: x is not defined
    {
        log(x); // ReferenceError: x is not defined

        let x = 42;

        log(x); // 42
    }
    log(x); // ReferenceError: x is not defined
}
  • const requires initialization and forbids mutation of the variable:
function f() {
    const uninitialized;     // SyntaxError: Missing initializer in const declaration

    const x = 42;
    x = 97;                  //   TypeError: Assignment to constant variable

    const account = new Account();
    account.deposit(42);     //        okay: mutates referenced object, not account variable

    account = new Account(); //   TypeError: Assignment to constant variable
}
  • Type-safe equality via === and !== (compares type and value)

  • Type-unsafe equality via == and != (confusing type coercions)

  • There is no universal equals method for object equality

Control flow

////////
function releaseYearOfEcmaScript(version) {

    if (typeof version !== "number") throw new Error(`expected number, not ${typeof version}`);

    if (!Number.isInteger(version)) throw new Error(`expected integer, not fraction ${version}`);

    if (version < 1) throw new Error(`non-positive ECMAScript version ${version}`);
    //               /////

    switch (version) {
    //////
        case 1: return 1997;
        case 2: return 1998;
        case 3: return 1999;
        case 4: throw new Error("abandoned ECMAScript version 4");
        case 5: return 2009;

        default: return (version >= 2015) ? version : 2009 + version;
    }            //////                  ///       ///
}

Loops

const s = "racecar";
for (let i = 0; i < s.length; ++i) {
    log(s[i]);
}


// find the smallest integer without odd successor

let x = 1;
while (x + 1 > x) {
    x *= 2;
    log(x);
}


// do-while loops are extremely rare
// example transcribed from C to JavaScript

let r = rand();   // 0 .. 32767

let x = r % 1000; // 0 ..   999

// Problem: 0 .. 767 occurs 33 times
//        768 .. 999 occurs 32 times
// 
// Solution: discard values of r >= 32000

let r;
do {
    r = rand();
} while (r >= 32000);

let x = r % 1000; // evenly distributed
  • Traditionally, C rand() has only 15 bits
  • JavaScript Math.random() has 53 bits
    • bias still exists, but much less severe

Exercise:

  • Start a loop at x = 27 and transform x once per iteration:
    • If x is even, divide x by 2
    • If x is odd, multiply x by 3 and increment
    • log x after every change
  • Stop the loop when x reaches 1
  • How many numbers were logged in total?

Functions

Arguments

  • Every parameter is initialized with its corresponding argument:
function join(array, separator, prefix, suffix) {

    return prefix + array[0] + separator + array[1] + suffix;
}

const treats = [ "peanuts", "chocolate", "pretzels" ];

join(treats, " and ", "Yummy: ", "!");

Default arguments

  • Missing arguments are undefined:
// 2 indistinguishable calls:
join(treats, " and ");
join(treats, " and ", undefined, undefined);

// 2 indistinguishable calls:
join(treats);
join(treats, undefined, undefined, undefined);
  • Manually replace undefined with sensible defaults:
function join(array, separator, prefix, suffix) {

    if (separator === undefined) {
        separator = ", ";  // replace undefined 
    }

    prefix = prefix ?? ""; // replace null or undefined

    return prefix + array[0] + separator + array[1] + suffix;
}
  • Automatically replace undefined since ES2015:
                               //////         ////         ////
function join(array, separator = ", ", prefix = "", suffix = "") {

    return prefix + array[0] + separator + array[1] + suffix;
}

Exercise:

  • So far, join always joins 2 array elements
    • Fix join so it joins array.length elements
    • Does it work if array.length is 1 or 0?
  • 🏆 Are you worried that str = str + something in a loop may have quadratic complexity?
    • Create a string of length 1 million via repeated concatenation of 1 character
    • Measure the elapsed milliseconds with 2 Date.now() calls (before and after)
    • Repeat the experiment with length 10 million
    • Is it ~10 times slower? linear complexity
    • Is it ~100 times slower? quadratic complexity

Higher-order functions

  • Higher-order functions accept (or return) other functions
  • For example, array.map(f) applies f to all array elements:
const primes = [ 2, 3, 5, 7 ];

function square(x) {
    return x * x;
}

primes.map(square);        // [ 4, 9, 25, 49 ]

primes.map(function (x) {  // [ 4, 9, 25, 49 ]
    return x * x;
});

primes.map((x) => x * x);  // [ 4, 9, 25, 49 ]

primes.map( x  => x * x);  // [ 4, 9, 25, 49 ]
  • A custom map implementation could look like this:
                   ///
function map(array, f) {
    const result = [];

    for (let i = 0; i < array.length; ++i) {
        result.push(f(array[i], i, array));
    }               /////////////////////

    return result;
}

map([ 2, 3, 5, 7 ], x => x * x); // [ 4, 9, 25, 49 ]
  • Note how map passes 3 arguments to f
    • element, index, array
  • But x => x * x defines only 1 parameter
    • JavaScript ignores extraneous arguments
  • Other popular higher-order functions are filter and reduce:
const primes = [ 11, 2, 3, 13, 5, 7, 17, 19 ];


function smallerThanTen(x) {
    return x < 10;
}

primes.filter(smallerThanTen); // [ 2, 3, 5, 7 ]

primes.filter(x => x < 10);    // [ 2, 3, 5, 7 ]


function plus(x, y) {
    return x + y;
}

primes.reduce(plus, 0);          // 0 + 11 + 2 + 3 + 13 + 5 + 7 + 17 + 19 = 77

primes.reduce((sumSoFar, currentNumber) => sumSoFar + currentNumber, 0); // 77

Exercise:

  • Implement the custom filter function below
  • Implement the custom reduce function below
function filter(array, predicate) {
    const result = [];

    // ...

    return result;
}

filter([ 11, 2, 3, 13, 5, 7, 17, 19 ], x => x < 10); // [ 2, 3, 5, 7 ]


function reduce(array, update, result) {
    // ...
}

reduce([ 11, 2, 3, 13, 5, 7, 17, 19 ], (x, y) => x + y, 0); // 77

Objects

  • In practice, you will encounter two “kinds” of objects:
    1. Object literals / JSON (JavaScript Object Notation)
    2. Class objects
  • Technically, there is only 1 kind of object, but the whole truth is quite complicated

Object literals / JSON

  • A JavaScript object is essentially a java.util.Map<String, Object>
// Object literal
const inventor = { forename: "Brandon", surename: "Eich" };

// read properties
inventor.forename              // 'Brandon'
inventor["surename"]           // 'Eich'

// write properties
inventor.forename = "Brendan"; // { forename: 'Brendan', surename: 'Eich' }
inventor["year"]  =  1961;     // { forename: 'Brendan', surename: 'Eich', year: 1961 }

// delete properties
delete inventor.forename;      // { surename: 'Eich', year: 1961 }
// JSON
const str  = JSON.stringify(inventor); // '{"surename":"Eich","year":1961}'
const twin = JSON.parse(str);          //  { surename: 'Eich', year: 1961 }
  • Quotation marks around keys are:
    • optional in literals
    • mandatory in JSON

Arrays

const primes = [ 2, 3, 5, 7 ];
typeof primes                       // 'object'
Object.getOwnPropertyNames(primes)  // [ '0', '1', '2', '3', 'length' ]

primes.length  // 4
primes[0]      // 2
primes[4]      // undefined

primes[4] = 11;      // [ 2, 3, 5, 7, 11 ]
primes.push(13);     // [ 2, 3, 5, 7, 11, 13 ]
primes.push(17, 19); // [ 2, 3, 5, 7, 11, 13, 17, 19 ]

primes[24] = 97;     // [ 2, 3, 5, 7, 11, 13, 17, 19, <16 empty items>, 97 ]
primes.length = 5;   // [ 2, 3, 5, 7, 11 ]
primes.pop();        // [ 2, 3, 5, 7 ]
  • JavaScript arrays are JavaScript objects with a special length property
    • The keys are stringified numbers
  • The (mutable!) length property is the largest index +1
  • There are no “array index out of bounds” errors:
    • Reading from such an index gives undefined
    • Writing to such an index grows the array

Iteration

const primes = [ 2, 3, 5, 7 ];


for (let i = 0; i < primes.length; ++i) {
    log(primes[i]);
}

                 //
for (const value of primes) {
    log(value);  // 2, 3, 5, 7
}

                 //
for (const index in primes) {
    log(index);  // '0', '1', '2', '3'
}


primes.forEach(function (value, index, array) {
    array[index] = -value;
});

Sorting

  • Sort strings alphabetically:
const inventors = ["Guido", "Bjarne", "James", "Brendan", "Anders"];

inventors.sort( );

["Anders",
 "Bjarne",
 "Brendan",
 "Guido",
 "James"]
  • Sort strings by length:
const inventors = ["Guido", "Bjarne", "James", "Brendan", "Anders"];

inventors.sort( (a, b) => a.length - b.length );

["Guido",
 "James",
 "Bjarne",
 "Anders",
 "Brendan"]
(a, b) => ... Order
negative a belongs before b
positive a belongs after b
zero a is equivalent to b
  • Since ES2019, equivalent elements retain their order

Promises

  • Appetizer: Which log statement runs first?
button.addEventListener("click", (event) => log("button clicked"));

log("callback registered");
  • The same principle applies to network calls:
const url = "https://api.chucknorris.io/jokes/random";

fetch(url).then(response => log("response header arrived"));

log("callback registered");
  • ⚠️ If you replace the last line with an infinite loop, the callback never runs
    • All your JavaScript code runs within a single thread
    • JavaScript is asynchronous, not concurrent or parallel (barring Web Workers)
  • Easier syntax since 2017, borrowed from C#:
ECMAScript 2015 ECMAScript 2017
function logRandomJoke() {
    return fetch(url)                   // Promise<Response>
    .then(response => response.text())  // Promise<string>
    .then(str      => JSON.parse(str))  // Promise<object>
    .then(joke     => log(joke));       // Promise<undefined>
}
async function logRandomJoke() {
    const response = await fetch(url);      // Response
    const str      = await response.text(); // string
    const joke     =       JSON.parse(str); // object
    log(joke);                              // undefined
}
  • fetch(url) resolves when the response header arrives
    • The response body may still be downloading
  • response.text() resolves when the body is:
    • downloaded
    • unzipped
    • converted to string
  • response.json() resolves when the body is:
    • downloaded
    • unzipped
    • converted to object
    • 🚀 no intermediate string:
ECMAScript 2015 ECMAScript 2017
function logRandomJoke() {
    return fetch(url)                   // Promise<Response>
    .then(response => response.json())  // Promise<object>
    .then(joke     => log(joke));       // Promise<undefined>
}
async function logRandomJoke() {
    const response = await fetch(url);       // Response
    const joke     = await response.json();  // object
    log(joke);                               // undefined
}
  • 📝 async functions implicitly return Promise objects

Exercise:

  • Fetch all joke categories from https://api.chucknorris.io/jokes/categories
  • Ignore these controversial categories:
    • explicit
    • political
    • religion
  • Fetch 1 joke per category from https://api.chucknorris.io/jokes/random?category=insertCategoryHere
  • Sort the joke values alphabetically
  • Sort the joke values by length
  • 🏆 Fetch the jokes in parallel with Promise.all
async function chuckNorris() {
    // fetch categories

    // ignore controversional categories

    // fetch jokes

    // sort alphabetically

    // sort by length
}

chuckNorris().catch(log); // log async errors

About

JavaScript

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages