Note: This note is for those who have already read the book. The book is really compact, this note cannot replace it (and I do not want to). The book is not short, neither is this note!
π My github repository.
π Other notes in this book series.
Infor & Preface
- Full name: You Don't Know JS Yet (2nd edition)
- Author: Kyle Simpson
- getify/You-Dont-Know-JS on Github
- It's not just for starters, but for all developers who want to understand deeper.
- "The primary point of the title βYou Donβt Know JS Yetβ is to point out that most JS developers donβt take the time to really understand how the code that they write works."
- "My suggestion is you take your time going through YDKJSY. Take one chapter, read it completely through start to finish, and then go back and re-read it section by section. Stop in between each section, and practice the code or ideas from that section. For larger concepts, it probably is a good idea to expect to spend several days digesting, re-reading, practicing, then digesting some more."
Chap 1 β What is JS?
What's With That Name?
- Not related with Java at all, not just a script but a programming language.
- "Java" β attract mostlty Java programmers, "Script" β lighweight.
- Official name specified by TC39 as "ECMAScript" (ES).
- JS in browsers or Node.js is an implementation of ES2019 standard.
- Hosted by ECMA.
- Don't use "JS6" or "ES8", use "ES20xx" or "JS".
Language Specification
- Who decides a new version of JS? β TC39 (~50-100 members) by votes via 5 stages.
- There is just one JS in the wild (not multiple versions).
- Environments run JS: browsers, servers, robots, lightbulbs,....
- Not all are JS, eg.
alert("Hello, JS!")
orconsole.log()
β they're just APIs of JS environments.- There are many "JS-looking" APIs:
fetch()
,getCurrentLocation()
,getUserMedia()
,... - They follow JS rules but just "guests", not official JS specifications.
- There are many "JS-looking" APIs:
- Complain "JS is so inconsistent!" β it's because the environment hehaviors work, not because of JS itself!
- Developer Tools (Inspect Element in Chrome, for example) are... tools for developers. They're NOT JS environment!
- Something works in Dev Tool, doesn't mean JS compiler will understand it.
Many Faces
- Paradigm-level code categories
- Procedural: organizes codes in a top-down, linear progression. β eg. C
- Object-oriented (OO/classes): organizes codes into classes. β eg. Java/C++
- Functional (FP): organizes codes into functions. β eg. Haskell
- JS is a multi-paradigm language. β "meaning the syntax and capabilities allow a developer to mix and match (and bend and reshape!) concepts from various major paradigms"
Backwards & Forwards
Backwards compatibility:
- Code from the past should still work today β "we don't break the web" (TC39)
- Idea: JS developer can write code with confidence β their code won't stop working in new released versions.
- Once itβs in JS, it canβt be taken out because it might break programs, even if weβd
really, really like to remove it! - My idea to remember: old codes work with new engines but old engines may not work with old codes.
Forward compatibility:
- Code from future don't break the web today.
- CSS & HTML is forward, not backward!
- Codes from the past may not work / work the same today.
- Feature from 2019 in a browser 2010 β page isn't broken! Unrecognized things will be skipped!
- My idea to remember: old engines work with new code but old codes may not work with new engine.
JS is backwards compatibility + not forward compability
- Codes written today, will work in future JS engines.
- Codes written today may be broken in old JS engines.
Why?
- "Markup" (HTML) / "Styling" (CSS) languages β easier to "skip over".
- "Programming language" (JS) β cannot skip something it doesn't understand (the rest may be effected!)
Fill the gaps?
JS has "forward-compability problems" (FC) (not compatible with old engines)
How today codes can be used in an old engine? β use transpiling (using a tool to convert a source code of a program from one form to another)
FC problems related syntax β use a transpiler (eg. Babel) β convert "new" JS syntax to "older" syntax.
// New
if (something) {
let x = 3; // "let" was added in ES6 (2015)
console.log(x);
} else {
let x = 4; // "let" β block scope
console.log(x);
}// Old
var x$0, x$1; // different variables
if (something) {
x$0 = 3; // diferent variables
console.log(x$0);
} else {
x$1 = 4;
console.log(x$1);
}"Itβs strongly recommended that developers use the latest version of JS so that their code is clean and communicates its ideas most effectively." β Let the tools take care of converting.
FC problems related to missing API method β use polyfill (aka "shim"). β Normally, a transpiler like Babel will detect and add it automatically.
// NEW: .finally() β ES2019
// getSomeRecords() returns us a promise for some // data it will fetch
var pr = getSomeRecords();
// show the UI spinner while we get the data
startSpinner();
pr.then(renderRecords) // render if successful
.catch(showError) // show an error if not
.finally(hideSpinner) // always hide the spinner// OLD:
if (!Promise.prototype.finally) { // prevents running on engines already has this API
Promise.prototype.finally = function f(fn){ // define new for old engines
return this.then(
function t(v){
return Promise.resolve( fn() )
.then(function t(){
return v; });
}, function c(e){}
);
};
}
What's in an Interpretation?
To clearify JS is interpreted or compiled β see how errors are handled.
Historically, scripted/interpreted languages were executed a top-down, line-by-line
Interpreted./Scripted Execution. Lines 1-4 must be executed before finding an error in line 5.Parsing whole process before any execution,
Parsing + Compilation + Execution. An error in line 5 would be caught in parsing phase before any execution."Parsed" language π€ "compiled" language: All compiled are parsed.
JS code is parsed before it's executed. β "early errors" β JS is a parsed language
JS is closer to compiled than interpreted (but not clearly a compiled or clearly a interpreted) β "meaning the tools (including the JS engine) process and verify a program (reporting any errors!) before it executes."
Flow of a JS source program:
Parsing, Compiling, and Executing JS.- Program leaves IDE β transpiled by Babel β packed by Webpack β ... β form1 β delivered to a JS engine.
- JS engine parses the code to an AST (Abstract Syntax Tree) β subsequent execution: form1 > AST > executable form.
- Engine convert that AST to a kind-of byte code β then to JIT (just in time) compiler.
- JS VM executes the program.
Web Assembly (WASM) β augments what the web (including JS) can accomplish.
- 2013, "ASM.js" (a subset of JS lang, transpiled from C) was introduced (by Mozilla) to demonstrate the performance of JS engine where it can run an Unreal 3 game at full 60fps. β ASM.js is just a transpiled language (not for coding).
- After ASM.js > another group (also Mozilla's) released Web Assembly (WASM) β provide a path for non-JS program (like C) to be converted to a form that could run in the JS engine.
- WASM's format is entirely unlike JS β skipping the parsing/compilation JS engine normally does
- codes β WASM parsing/compilation β binary packed (easier for JS engine to understand) > JS engine execute them.
- Ex: Go program has threaded programming β WASM convert it β JS engine can understand (JS no need to have something like threads feature)
- π‘TC39 aren't stressed to add more features (from other "concurrent" languages) β just keep their rules, WASM will make the bridge.
- WASM isn't only for the web, also isn't JS.
Strictly Speaking
ES5 (2009) β "strict mode" β encourage better JS programs.
Why?
- Not a restriction but rather a "guide" so that JS engine can optimize and effectciently run the code.
- Prevent some "stupid" coding ways when working in group, for example.
In form of "early errors" β ex: disallows naming 2 function parameters the same.
Some examples
// only whitespace and comments are allowed
// before the use-strict pragma
"use strict";
// the rest of the file runs in strict mode// Per function scope
// Used when you wanna convert non-strict to strict programs
function someOperations() {
// whitespace and comments are fine here "use strict";
// all this code will run in strict mode
}Cannot be default β otherwise, it will break "backward compatibility" rule.
Virtually, all transpiled codes (codes in production) ends up in strict mode.
Chap 2 β Surveying JS
- The best way to learn JS is to start writing JS.
- Goal: get a better feel for it, so that we can move forward writing our own programs with more confidence.
Each File is a Program
- In JS, each standalone file is its own separate program. β for error handling.
- How multiple files talk together? β Only way: sharing their state + use "global scope".
- ES6 β module (also a file-based) β files are imported to module and be considered as a single module.
- JS does still treat each module separately
- A JS file: either standardlone or module.
Value
Values come in 2 forms in JS: primitive and object
Primitive:
string
,number
,boolean
,undefined
,null
,symbol
literals:
string
,number
,boolean
string
literals, eg:const name = "Thi."
β Using""
or''
is optional but should pick one and to use it consistently throughout the program.// Also can use backtick ``
console.log("My name is ${name}.") // Output: My name is ${name}.
console.log(`My name is ${name}.`) // Output: My name is Thi.This is called an interpolation
number
, eg.3.14
orMath.PI
boolean
:true
,false
.
The "emptiness": undefined, null (They're not the same!) β itβs safest and best to use only
undefined
as the single empty valuesymbol
, eg.const a = Symbol("meaning of life")
β Symbols are mostly used in low-level code such as in libraries and frameworks.
Arrays:
names = ["France", 1, null]
names.length // 3
names[0] // France
typeof names // "object" β yep!
// array can contains a function
const func = () β true;
arr = [func, 1]
typeof func // "function"Fact: JS array indices are 0-based (eg.
a[0]
)Objects: an unordered, keyed collection of any various values
name = {
first: "Thi",
last: "Dinh",
age: 30,
specialties: [ "JS", "Drawing" ]
};
console.log(`My name is ${ name.first }.`); // My name is Thi.Value Type Determination:
typeof 42; // "number"
typeof "abc"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" β yep!
typeof { "a": 1 }; // "object"
typeof [1,2,3]; // "object" β yep!
typeof function hello(){}; // "function"
Declaring and Using Variables
var
vslet
var adult = true;
if (adult) {
var name = "Thi"; // β "var" says "this variable will be seen by a wider scope"
let age = 30; // β "let" limit access to "block scope"
}
console.log(name) // Thi
console.log(age) // Error!Note:
var
should be avoided in favor oflet
(orconst
) β prevent confusing in scoping behaviors.let
vsconst
β must giveconst
an initial value and cannot re-assign.const somethingToBeAssignedLater; // Error!
const myBirthday = true;
let age;
age = 30;
if (myBirthday) {
age=age+1; //OK!
myBirthday = false; // Error!
}However,
const odds = [1, 3, 5];
odds[1] = 7; // OK :(
odds = []; // Error!π‘ Use
const
when you need a meaningful variable likemyBirthDay
instead of justtrue
. Also, with primitive values,const
helps avoid confusion due to reassignment problems.
Functions
In JS, the word "functions" takes a broader meaning of "procedure" β a collection of statements can be invoked many times.
Different types,
// Function declaration β appear as a statement by itself
function functionName(coolThings) {
// ...
return returnedValue;
}
// Association between "functionName" and "returnedValue" happens during
// the compile phase, before the code is executed.// Function as an expression
// Could be "let", "var"
const functionName = function(coolThings) {
// ...
return returnedValue;
}
// (Diff from func declaration) Function expression is not associated with
// its identifier until that statement during runtime.Function are values that can be assigned and passed as an argument. It's a special type of object.
Functions can be assigned as properties of objects
var whatToSay = {
greeting() { console.log("Hello!"); },
question() { console.log("What's your name?"); },
answer() { console.log("My name is Kyle."); }
};
whatToSay.greeting(); // Hello!Appendix A β So many function forms
Named function expression
// Could be "let" or "var"
const awesomeFunc = function someName(arg) {
// ...
return amzingStuff;
}
awesomeFunc.name; // "someName"
// "awesomeFunc" and "someName" are only linked at the runtime
// π They should have the same name!Should a function have a name? β "In my opinion [Kyle's], if a function exists in your program, it has a purpose; otherwise, take it out! And if it has a purpose, it has a natural name that describes that purpose."
Some more forms (early 2020, maybe more)
// generator function declaration
function *two() { .. }
// async function declaration
async function three() { .. }
// async generator function declaration
async function *four() { .. }
// named function export declaration (ES6 modules)
export function five() { .. }// IIFE
(function(){ .. })();
(function namedIIFE(){ .. })();
// asynchronous IIFE
(async function(){ .. })();
(async function namedAIIFE(){ .. })();Arrow function expression
var f;
f = () β 42;
f = x β x * 2;
f = (x) β x * 2;
f = (x,y) β x * y;
f = x β ({ x: x * 2 });
f = x β { return x * 2; };
f = async x β {
var y = await doSomethingAsync(x);
return y * 2;
};
someOperation( x β x * 2 );- "Since I donβt think anonymous functions are a good idea to use frequently in your programs, Iβm not a fan of using the
β
arrow function form."
- "Since I donβt think anonymous functions are a good idea to use frequently in your programs, Iβm not a fan of using the
As methods in classes
class SomethingKindaGreat {
// class methods
coolMethod() { .. } // no commas!
boringMethod() { .. }
}
var EntirelyDifferent = {
// object methods
coolMethod() { .. }, // commas!
boringMethod() { .. },
// (anonymous) function expression property
oldSchool: function() { .. }
};
Comparisons
Equal...ish
We must be aware of the differences between an equality and equivalence comparisons.
"Triple equal"
===
β Checking both the value and the type (in fact, all comparisons in JS, not just===
, does consider the type but===
disallow any kind of conversion while others do)3 === 3.0; // true
"yes" === "yes"; // true
null === null; // true
false === false; // true
42 === "42"; // false
"hello" === "Hello" // false
true === 1; // fasle
0 === null; // fasle
"" === null; // fasle
null === undefined; // fasle===
is lying (not really "strict"),NaN === NaN; // false
Number.isNaN(NaN) === Number.isNaN(NaN) // true
0 === -0; // true
Object.is(0, -0) // false β should use it, like an "===="===
isn't a structural equality but identity equality for object values β In JS, all object values are held by reference= (cf. App. A β Values vs References)[ 1, 2, 3 ] === [ 1, 2, 3 ] // false
{ a: 42 } === { a: 42 } // false
( x β x * 2) === ( x β x * 2 ) // falsevar x = [ 1, 2, 3 ];
var y = x;
y === x; // true (Both point to the same "reference")
y === [ 1, 2, 3] // false
x === [ 1, 2, 3] // false"Deep comparison" for "structural equality" is more complicated than you think (even if you stringify them and then compare, it's not always correct). That's why JS doesn't give any mechanism for this.
Coercive Comparisons
Coercion means a value of one type being converted to its respective representation in another type.
42 == "42"; // true ("42" is converted to number)
1 == true; // true// Allowed but avoid to use
"" == 0; // true
0 == false; // trueIf the comparison is between the same value type, both
==
and===
do exactly the same thing, no difference whatsoever.Why not just use
===
? β Because>
,<
,>=
,β
use coercive also!var arr = [ "1", "10", "100", "1000" ];
for (let i = 0; i < arr.length && arr[i] < 500; i++) {
// will run 3 times
}// Watch out
var x = "10"
var y = "9"
var x = 9
x < y // true (use alphabetical comparison of string instead)
x < z // false- Itβs still pretty likely youβre going to run into a case where the types may differ.
How We Organize in JS
Two major patterns: Classes and Modules.
Classes
- A class in a program is a definition of a "type" of custom data structure that includes both data and behaviors that operate on that data.
- Classes define how data structure works but not themselves concrete values. β to get a concrete value of a class, use
new
to instantiate it!
class Page {
constructor(text) {
this.text = text;
}
print() {
console.log(this.text);
}
}
class Notebook {
constructor() {
this.pages = [];
}
addPage(text) {
var page = new Page(text);
this.pages.push(page);
}
print() {
for (let page of this.pages) {
page.print();
}
}
}
var mathNotes = new Notebook();
mathNotes.addPage("Arithmetic: + - * / ...");
mathNotes.addPage("Trigonometry: sin cos tan ...");
mathNotes.print();
// ..- Behaviors (methods) can be only called by instance, not the classes, eg.
mathNotes.addPage()
.
Class Inheritance
// Base class
class Publication {
constructor(title, author, pubDate) {
this.title = title;
this.author = author;
this.pubDate = pubDate;
}
print() {
console.log(`Title: ${this.title}, By: ${this.author}, On: ${this.pubDate}`);
}
}// Extended classes
class Book extends Publication {
constructor(bookDetails) {
super(bookDetails.title, bookDetails.author, bookDetails.pubishedOn);
this.publisher = bookDetails.publisher;
this.ISBN = bookDetails.ISBN;
}
print() { // overrides parent's print()
super.print(); // call (again) parent's print()
console.log(`Publisher: ${this.publisher}, ISBN: ${this.ISBN}.`);
}
}
class BlogPost extends Publication {
constructor(title,author,pubDate,URL) {
super(title,author,pubDate);
this.URL = URL;
}
print() {
super.print();
console.log(this.URL);
}
}super()
delegates to parent's constructor for its initial- ization work.- Parent's
print()
and child'sprint()
can have the same name and co-exits β called polymorphism!
var YDKJS = new Book({
title: "You Don't Know JS",
author: "Kyle Simpson",
publishedOn: "June 2014",
publisher: "O'Reilly",
ISBN: "123456-789"
});
YDKJS.print();
// Title: You Don't Know JS, By: Kyle Simpson, On: June 2014
// Publisher: O'Reilly, ISBN: 123456-789
var forAgainstLet = new BlogPost(
"For and against let",
"Kyle Simpson",
"October 27, 2014",
"https://davidwalsh.name/for-and-against-let"
);
forAgainstLet.print();
// Title: For and against let, By: Kyle Simpson, On: October 27, 2014
// https://davidwalsh.name/for-and-against-letModules
Like classes, modules can "include" or "access" the adata and behaviors of other modules.
From the early days of JS, modules was an important and common pattern, even without a dedicated syntax.
Classical module
function Publication(title,author,pubDate) {
var publicAPI = {
print() {
console.log(`Title: ${this.title}, By: ${this.author}, On: ${this.pubDate}`);
}
};
return publicAPI;
}
function Book(bookDetails) {
var pub = Publication(
bookDetails.title,
bookDetails.author,
bookDetails.publishedOn
);
var publicAPI = {
print() {
pub.print();
console.log(`Publisher: ${this.publisher}, ISBN: ${this.ISBN}.`);
}
};
return publicAPI;
}
function BlogPost(title,author,pubDate,URL) {
var pub = Publication(title,author,pubDate);
var publicAPI = {
print() {
pub.print();
console.log(URL);
}
};
return publicAPI;
}- In classes, data and methods are accessed with
this.
while modules, they're accessed as identifier variables in scope.
// Their usage
var YDKJS = Book({...});
YDKJS.print();
var forAgainstLet = BlogPost();
forAgainstLet.print();- The only difference is with/without
new
!
- In classes, data and methods are accessed with
ES Modules (ESM).
3 different things compared to the classicial modules,
- No need to define a wrapper function, ESMs are always file-based, one file one module.
- Whenever we wanna make an API public, use
export
, otherwise, we cannot call this API from another module. - We don't "instantitate" an ESM, use
import
instead.
Rewrite above publication module as,
// publication.js
function printDetails(title,author,pubDate) {
console.log(`...`);
}
export function create(title,author,pubDate) {
var publicAPI = {
print() {
printDetails(title,author,pubDate);
}
};
return publicAPI;
}// blogpost.js
import { create as createPub } from "publication.js";
function printDetails(pub,URL) {
pub.print();
console.log(URL);
}
export function create(title,author,pubDate,URL) {
var pub = createPub(title,author,pubDate);
var publicAPI = {
print() {
printDetails(pub,URL);
}
};
return publicAPI;
}// main.js
import { create as newBlogPost } from "blogpost.js";
var forAgainstLet = newBlogPost(...);
forAgainstLet.print();
The Rabbit Hole Deepens
- Recall, this chapter is just like a "brief" of JS world.
- "I'm serious when I suggest: re-read this chapter, maybe several times."
- Next chapters, we dig more.
Chap 3 β Digging to the roots of JS
Chap 4 β The bigger picture
Appendix A β Exploring further
Values vs References
If you assign/pass a value itself, the value is copied. Primitives are held by values.
var myName = "Kyle";
var yourName = myName;
myName = "Thi";
console.log(myName); // Thi
console.log(yourName); // KyleReferences are the idea that two or more variables are pointing at the same value. Edit one, others change. In JS, only object values (arrays, objects, functions,...) are treated as references.
var myAddress = {
street: "123 JS Blvd".
city: "Austin",
state: "TX"
}
var yourAddress = myAddress;
myAddress.street = "456 TS Ave";
console.log(yourAddress.street); // 456 TS Ave
π¬ Comments