This note serves as a reminder of the book's content, including additional research on the mentioned topics. It is not a substitute for the book. Most images are sourced from the book or referenced.
All notes in this series
Infor & Preface
- Not related with Java at all, not just a script but a programming language.
- "Java" → attract mostlty Java programmers, "Script" → light weight.
- 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".
- 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.
- 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.
- 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 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 new 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.
- "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.
1// New
2if (something) {
3 let x = 3; // "let" was added in ES6 (2015)
4 console.log(x);
5} else {
6 let x = 4; // "let" → block scope
7 console.log(x);
8}
1// Old
2var x$0, x$1; // different variables
3if (something) {
4 x$0 = 3; // diferent variables
5 console.log(x$0);
6} else {
7 x$1 = 4;
8 console.log(x$1);
9}
1// NEW: .finally() ← ES2019
2// getSomeRecords() returns us a promise for some // data it will fetch
3var pr = getSomeRecords();
4// show the UI spinner while we get the data
5startSpinner();
6pr.then(renderRecords) // render if successful
7 .catch(showError) // show an error if not
8 .finally(hideSpinner) // always hide the spinner
1// OLD:
2// prevents running on engines already has this API
3if (!Promise.prototype.finally) {
4 // define new for old engines
5 Promise.prototype.finally = function f(fn){
6 return this.then(
7 function t(v){
8 return Promise.resolve( fn() )
9 .then(function t(){
10 return v; });
11 }, function c(e){}
12 );
13 };
14}
- To clearify JS is interpreted or compiled → see how errors are handled.
- Historically, scripted/interpreted languages were executed a top-down, line-by-line
- Parsing whole process 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:
- 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.
- 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
1// only whitespace and comments are allowed
2// before the use-strict pragma
3"use strict";
4// the rest of the file runs in strict mode
1// Per function scope
2// Used when you wanna convert non-strict to strict programs
3function someOperations() {
4 // whitespace and comments are fine here
5 "use strict";
6 // all this code will run in strict mode
7}
- Cannot be default → otherwise, it will break "backward compatibility" rule.
- Virtually, all transpiled codes (codes in production) ends up in strict mode.
- 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.
- 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.
- 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.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 value symbol
, eg.const a = Symbol("meaning of life")
← Symbols are mostly used in low-level code such as in libraries and frameworks.
1// Also can use backtick ``
2console.log("My name is ${name}.") // Output: My name is ${name}.
3console.log(`My name is ${name}.`) // Output: My name is Thi.
This is called an interpolation
1console.log(null === undefined) // false (not the same type)
2console.log(null == undefined) // true (but the "same value")
3console.log(null === null) // true (both type and value are the same)
4console.log(undefined === undefined) // true
5
6console.log(typeof null) // 'object'
7console.log(typeof undefined) // 'undefined'
- Arrays:
1names = ["France", 1, null]
2names.length // 3
3names[0] // France
4typeof names // "object" ← yep!
5
6// array can contains a function
7const func = () → true;
8arr = [func, 1]
9typeof func // "function"
Fact: JS array indices are 0-based (eg.
a[0]
)- Objects: an unordered, keyed collection of any various values
1name = {
2 first: "Thi",
3 last: "Dinh",
4 age: 30,
5 specialties: [ "JS", "Drawing" ]
6};
7console.log(`My name is ${ name.first }.`); // My name is Thi.
- Value Type Determination:
1typeof 42; // "number"
2typeof "abc"; // "string"
3typeof true; // "boolean"
4typeof undefined; // "undefined"
5typeof null; // "object" ← yep!
6typeof { "a": 1 }; // "object"
7typeof [1,2,3]; // "object" ← yep!
8typeof function hello(){}; // "function"
var
vslet
1var adult = true;
2if (adult) {
3 var name = "Thi"; // ← "var" says "this variable will be seen by a wider scope"
4 let age = 30; // ← "let" limit access to "block scope"
5}
6console.log(name) // Thi
7console.log(age) // Error!
Note:
var
should be avoided in favor of let
(or const
) → prevent confusing in scoping behaviors.let
vsconst
← must giveconst
an initial value and cannot re-assign.
1const somethingToBeAssignedLater; // Error!
2const myBirthday = true;
3let age;
4age = 30;
5if (myBirthday) {
6 age = age + 1; // OK!
7 myBirthday = false; // Error!
8}
- However,
1const odds = [1, 3, 5];
2odds[1] = 7; // OK :(
3odds = []; // 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.
- In JS, the word "functions" takes a broader meaning of "procedure" — a collection of statements can be invoked many times.
- Different types,
1// Function declaration ← appear as a statement by itself
2function functionName(coolThings) {
3 // ...
4 return returnedValue;
5}
6// Association between "functionName" and "returnedValue" happens during
7// the compile phase, before the code is executed.
1// Function as an expression
2// Could be "let", "var"
3const functionName = function(coolThings) {
4 // ...
5 return returnedValue;
6}
7// (Diff from func declaration) Function expression is not associated with
8// 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
1var whatToSay = {
2 greeting() { console.log("Hello!"); },
3 question() { console.log("What's your name?"); },
4 answer() { console.log("My name is Thi."); }
5};
6whatToSay.greeting(); // Hello!
- Check more in “Appendix A - So many function forms”.
- 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) ===
is lying (not really "strict"),===
isn't a structural equality but identity equality for object values ← In JS, all object values are held by reference (Check more in section “Appendix A - Values vs References”)- "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.
13 === 3.0; // true
2"yes" === "yes"; // true
3null === null; // true
4false === false; // true
5
642 === "42"; // false
7"hello" === "Hello" // false
8true === 1; // fasle
90 === null; // fasle
10"" === null; // fasle
11null === undefined; // fasle
1NaN === NaN; // false
2Number.isNaN(NaN) === Number.isNaN(NaN) // true
3
40 === -0; // true
5Object.is(0, -0) // false ← should use it, like an "===="
1[ 1, 2, 3 ] === [ 1, 2, 3 ] // false
2{ a: 42 } === { a: 42 } // false
3( x → x * 2) === ( x → x * 2 ) // false
1var x = [ 1, 2, 3 ];
2var y = x;
3y === x; // true (Both point to the same "reference")
4y === [ 1, 2, 3] // false
5x === [ 1, 2, 3] // false
- Coercive Comparisons
- Coercion means a value of one type being converted to its respective representation in another type.
- If 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! - It’s still pretty likely you’re going to run into a case where the types may differ.
- Check more in section “Appendix A - Coercive Conditional Comparison”.
142 == "42"; // true ("42" is converted to number)
21 == true; // true
1// Allowed but avoid to use
2"" == 0; // true
30 == false; // true
1var arr = [ "1", "10", "100", "1000" ];
2for (let i = 0; i < arr.length && arr[i] < 500; i++) {
3 // will run 3 times
4}
1// Watch out
2var x = "10"
3var y = "9"
4var z = 9
5x < y // true (use alphabetical comparison of string instead)
6x < z // false
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! - Behaviors (methods) can be only called by instance, not the classes, eg.
mathNotes.addPage()
.
1class Page {
2 constructor(text) {
3 this.text = text;
4 }
5
6 print() {
7 console.log(this.text);
8 }
9}
10
11class Notebook {
12 constructor() {
13 this.pages = [];
14 }
15
16 addPage(text) {
17 var page = new Page(text);
18 this.pages.push(page);
19 }
20
21 print() {
22 for (let page of this.pages) {
23 page.print();
24 }
25 }
26}
27
28var mathNotes = new Notebook();
29mathNotes.addPage("Arithmetic: + - * / ...");
30mathNotes.addPage("Trigonometry: sin cos tan ...");
31mathNotes.print();
32// ..
- Class Inheritance
super()
delegates to parent's constructor for its initialization work.- Parent's
print()
and child'sprint()
can have the same name and co-exits → called polymorphism!
1// Base class
2class Publication {
3 constructor(title, author, pubDate) {
4 this.title = title;
5 this.author = author;
6 this.pubDate = pubDate;
7 }
8
9 print() {
10 console.log(`Title: ${this.title}, By: ${this.author}, On: ${this.pubDate}`);
11 }
12}
1// Extended classes
2class Book extends Publication {
3 constructor(bookDetails) {
4 super(bookDetails.title, bookDetails.author, bookDetails.pubishedOn);
5 this.publisher = bookDetails.publisher;
6 this.ISBN = bookDetails.ISBN;
7 }
8
9 print() { // overrides parent's print()
10 super.print(); // call (again) parent's print()
11 console.log(`Publisher: ${this.publisher}, ISBN: ${this.ISBN}.`);
12 }
13}
14
15class BlogPost extends Publication {
16 constructor(title,author,pubDate,URL) {
17 super(title,author,pubDate);
18 this.URL = URL;
19 }
20
21 print() {
22 super.print();
23 console.log(this.URL);
24 }
25}
1var YDKJS = new Book({
2 title: "You Don't Know JS",
3 author: "Kyle Simpson",
4 publishedOn: "June 2014",
5 publisher: "O'Reilly",
6 ISBN: "123456-789"
7});
8YDKJS.print();
9// Title: You Don't Know JS, By: Kyle Simpson, On: June 2014
10// Publisher: O'Reilly, ISBN: 123456-789
11
12var forAgainstLet = new BlogPost(
13 "For and against let",
14 "Kyle Simpson",
15 "October 27, 2014",
16 "<https://davidwalsh.name/for-and-against-let>"
17);
18forAgainstLet.print();
19// Title: For and against let, By: Kyle Simpson, On: October 27, 2014
20// <https://davidwalsh.name/for-and-against-let>
- Modules
- Like classes, modules can "include" or "access" the data 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
- In classes, data and methods are accessed with
this.
while modules, they're accessed as identifier variables in scope. - The only difference is with/without
new
! - 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,
1function Publication(title,author,pubDate) {
2 var publicAPI = {
3 print() {
4 console.log(`Title: ${this.title}, By: ${this.author}, On: ${this.pubDate}`);
5 }
6 };
7 return publicAPI;
8}
9
10function Book(bookDetails) {
11 var pub = Publication(
12 bookDetails.title,
13 bookDetails.author,
14 bookDetails.publishedOn
15 );
16 var publicAPI = {
17 print() {
18 pub.print();
19 console.log(`Publisher: ${this.publisher}, ISBN: ${this.ISBN}.`);
20 }
21 };
22 return publicAPI;
23}
24
25function BlogPost(title,author,pubDate,URL) {
26 var pub = Publication(title,author,pubDate);
27 var publicAPI = {
28 print() {
29 pub.print();
30 console.log(URL);
31 }
32 };
33 return publicAPI;
34}
1// Their usage
2var YDKJS = Book({...});
3YDKJS.print();
4
5var forAgainstLet = BlogPost();
6forAgainstLet.print();
1// publication.js
2function printDetails(title,author,pubDate) {
3 console.log(`...`);
4}
5
6export function create(title,author,pubDate) {
7 var publicAPI = {
8 print() {
9 printDetails(title,author,pubDate);
10 }
11 };
12 return publicAPI;
13}
1// blogpost.js
2import { create as createPub } from "publication.js";
3
4function printDetails(pub,URL) {
5 pub.print();
6 console.log(URL);
7}
8
9export function create(title,author,pubDate,URL) {
10 var pub = createPub(title,author,pubDate);
11 var publicAPI = {
12 print() {
13 printDetails(pub,URL);
14 }
15 };
16 return publicAPI;
17}
1// main.js
2import { create as newBlogPost } from "blogpost.js";
3
4var forAgainstLet = newBlogPost(...);
5forAgainstLet.print();
- 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.
- Goal: shifts to some of the lower-level root characteristics of JS.
- A “standardized” approach to consuming data from a source one chunk at a time.
- ES6 standardized a specific protocol for the iterator pattern directly in the language.
next()
method whose return is an object called an iterator result.- The object has
value
anddone
properties, where done is a boolean that isfalse
until the iteration over the underlying data source is complete.
- Consuming Iterators
for..of
- Spread form (
…
operator)
1// given an iterator of some data source:
2var it = /* .. */;
1// loop over its results one at a time
2for (let val of it) {
3 console.log(`Iterator value: ${ val }`);
4}
5// Iterator value: ..
6// Iterator value: ..
7// ..
1// An array spread
2var vals = [ ...it ];
3
4// A functrion call spread
5doSomethingUseful( ...it );
- Iterables
- ES6 defined structure/collection types as iterables: strings, arrays, maps, sets and others.
- All built-in iterables in JS have 3 iterator forms: keys-only (
keys()
), values-only (values()
), entries (entries()
).
1// Array
2// an array is iterable
3var arr = [ 10, 20, 30 ];
4
5// shallow copy an array using iterator consumption
6var arrCopy = [ ...arr ];
1// String
2// iterate the characters in a string
3var greeting = "Hello world!";
4var chars = [ ...greeting ];
5// [ "H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!" ]
1// Map
2// given two DOM elements, `btn1` and `btn2`
3var buttonNames = new Map();
4buttonNames.set(btn1,"Button 1");
5buttonNames.set(btn2,"Button 2");
6
7// The [btn,btnName] syntax is called “array destructuring”.
8for (let [btn,btnName] of buttonNames) {
9 btn.addEventListener("click",function onClick(){
10 console.log(`Clicked ${ btnName }`);
11 });
12}
1for (let btnName of buttonNames.values()) {
2 console.log(btnName);
3}
4// Button 1
5// Button 2
1for (let [idx,val] of arr.entries()) {
2 console.log(`[${ idx }]: ${ val }`);
3}
4// [0]: 10
5// [1]: 20
6// [2]: 30
- Closure might be as important to understand as variables or loops.
- The presence or lack of closure is sometimes the cause of bugs (or even the cause of performance issues).
- Closure is part of the nature of a function. Objects don’t get closures, functions do.
- To observe a closure, you must execute a function in a different scope than where that function was originally defined.
1function greeting(msg) {
2 return function who(name) {
3 console.log(`${ msg }, ${ name }!`);
4 };
5}
6
7var hello = greeting("Hello");
8var howdy = greeting("Howdy");
9
10hello("Kyle"); // Hello, Kyle!
11hello("Thi"); // Hello, Thi!
12howdy("Grant"); // Howdy, Grant!
When
greeting()
finishes running, are its variables msg
removed from memory? → No! It’s because the closure. Since the inner function instances who
are still alive (assigned to hello
and howdy
, respectively), their closures are still preserving the msg
variables.1function counter(step = 1) {
2 var count = 0;
3 return function increaseCount(){
4 count = count + step;
5 return count;
6 };
7}
8
9var incBy3 = counter(3);
10incBy3(); // 3
11incBy3(); // 6
count
is preserved after each invocation of the inner function.- Closure is most common when working with asynchronous code, such as with callbacks.
1function getSomeData(url) {
2 ajax(url,function onResponse(resp){
3 console.log(
4 `Response (from ${ url }): ${ resp }`
5 );
6 });
7}
8
9getSomeData("https://some.url/wherever");
10// Response (from https://some.url/wherever): ...
onResponse(..)
is closed over url
. Even though getSomeData(..)
finishes right away, the url
parameter variable is kept alive.- No need the outer scope to be a function, just at least one variable in an outer scope accessed from an inner function
1for (let [idx,btn] of buttons.entries()) {
2 btn.addEventListener("click",function onClick(){
3 console.log(`Clicked on button (${ idx })!`);
4 });
5}
The inner function closes over
idx
, preserving for it for as long as the click handler is set on the btn
. Remember: this closure is not over the value (like 1
or 3
), but over the variable idx
itself.- One of JS’s most powerful mechanisms and also most misunderstood.
- Misconceptions:
this
refers to the function itself orthis
points to the instance that a method belongs to. ← Both are incorrect!
- (From previous section) When a function is defined, it is attached to its enclosing scope via closure.
- Function has another characteristic - execution context which is exposed to the function via
this
.
- Scope is static but execution context is dynamic, entirely dependent on how it is called.
1function classroom(teacher) {
2 return function study() {
3 console.log( `${ teacher } says to study ${ this.topic }`);
4 };
5}
6var assignment = classroom("Kyle");
7
8assignment(); // Kyle says to study undefined
No execution context provided →
this.topic
prefers window
in the browser but there is no global variable named “topic” → undefined
.1var homework = { topic: "JS", assignment: assignment };
2homework.assignment(); // Kyle says to study JS
this
prefers homework
.1var otherHomework = { topic: "Math" };
2assignment.call(otherHomework); // Kyle says to study Math
A third way to invoke a function is to use
.call()
method.- Benefit of
this
-aware functions: more flexibly re-use a single function with data from different objects.
- A prototype is a characteristic of an object.
- Think about a prototype as a linkage between two objects. It’s hidden but there are ways to expose and observe it.
- Delegation: access props of
A
from the its prototype linkageB
.
- A series of objects linked together via prototypes is called the “prototype chain”
1var homework = { topic: "JS" };
2homework.toString(); // [object object]
homework
has only property topic
but its default prototype linkage to Object.prototype
which has toString()
, valueOf()
,…- Object Linkage
- To define an object prototype linkage, use
Object.create()
var noLinkedObject = Object.create(null)
creates an object that is not prototype linked anywhere!
1var homework = { topic: "JS" };
2var otherHomework = Object.create(homework);
3otherHomework.topic; // "JS"
1homework.topic; // "JS"
2otherHomework.topic; // "JS"
3
4otherHomework.topic = "Math";
5otherHomework.topic; // "Math"
6homework.topic; // "JS" -- not "Math"
The assignment creates prop "topic" directly on
otherHomework
, not in prototype.- Read more in the section “Appendix A - Prototypal Classes”.
this
revisited
1var homework = {
2 study() {
3 console.log(`Please study ${ this.topic }`);
4 }
5};
6
7var jsHomework = Object.create(homework);
8jsHomework.topic = "JS";
9jsHomework.study(); // Please study JS
10
11var mathHomework = Object.create(homework);
12mathHomework.topic = "Math";
13mathHomework.study(); // Please study Math
Unlike other languages, JS’s
this
is dynamic (it’s not resolved to homework
but jsHomework
and mathHomework
)Two objects linked to a common parent.
Asking the right questions is a critical skill of becoming a better developer.
This final chapter divides JS languages into 3 main pillars.
- Scopes are like buckets, and variables are like marbles you put into those buckets.
- Scopes nest inside each other. Variables at that level of scope (or higher/outer) are accessible. Lower/inner variables are hidden and inaccessible. ← lexical scope.
- The scope is determined at the time the program is parsed (compiled).
- JS is lexically scoped because of 2 characteristics:
- Hoisting: variables declared anywhere are treated as they’red declared at the beginning of the scope.
var
-declared variables are function scoped.
- Closure: When a function makes reference to variables from an outer scope, and that function is passed around as a value and executed in other scopes, it maintains access to its original scope variables ← Reading: You Don't Know JS Yet 2 - Scope & Closures (Chap 1 — Chap 5)
- JS is one of very few language where you have the option to create objets directly and explicitly without first defining their structure in a class.
- Power of prototype system: the ability for 2 objects to connect with each other and cooperate dynamically through
this
. Classes are just one pattern on top of such power. We can see in another approach where we just use objects with prototype chain.
- Check to see “classes aren’t the only way to use objects”.
- Developers should learn more about how JS manages type conversions and also type-aware tools like TypeScript or Flow (”static typing” approaches).
- Don’t conclude that jS’s type mechanism is bad!
- Check . Don’t skip over this topic just because you heard that we should use
===
and forget about the rest.
- The grain of how most people approach and use JS. This series don’t presence opinion as fact or vice versa. Just follow the specification. Don’t argue with the opinion or misconception of you or of the others.
- The grain you really should pay attention is of how JS works, at the language level.
- The most important grain to recognize is how the existing program(s) you’re working on, and developers you’re working with, do stuff.
- Learn to write more readable code (for your teamates or yourself in the future).
- If you assign/pass a value itself, the value is copied. Primitives are held by values.
1var myName = "Kyle";
2var yourName = myName;
3myName = "Thi";
4console.log(myName); // Thi
5console.log(yourName); // Kyle
- References 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.
1var myAddress = {
2 street: "123 JS Blvd".
3 city: "Austin",
4 state: "TX"
5}
6var yourAddress = myAddress;
7
8myAddress.street = "456 TS Ave";
9console.log(yourAddress.street); // 456 TS Ave
- Named function expression
1// Could be "let" or "var"
2const awesomeFunc = function someName(arg) {
3 // ...
4 return amzingStuff;
5}
6awesomeFunc.name; // "someName"
7// "awesomeFunc" and "someName" are only linked at the runtime
8// 👌 They should have the same name!
9
- 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)
1// generator function declaration
2function *two() { .. }
3
4// async function declaration
5async function three() { .. }
6
7// async generator function declaration
8async function *four() { .. }
9
10// named function export declaration (ES6 modules)
11export function five() { .. }
12
1// IIFE (Immediately Invoked Function Expression)
2(function(){ .. })();
3(function namedIIFE(){ .. })();
4
5// asynchronous IIFE
6(async function(){ .. })();
7(async function namedAIIFE(){ .. })();
8
- Arrow function expression
- "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."
1var f;
2f = () → 42;
3f = x → x * 2;
4f = (x) → x * 2;
5f = (x,y) → x * y;
6f = x → ({ x: x * 2 });
7f = x → { return x * 2; };
8f = async x → {
9 var y = await doSomethingAsync(x);
10 return y * 2;
11};
12someOperation( x → x * 2 );
13
- As methods in classes
1class SomethingKindaGreat {
2 // class methods
3 coolMethod() { .. } // no commas!
4 boringMethod() { .. }
5}
6var EntirelyDifferent = {
7 // object methods
8 coolMethod() { .. }, // commas!
9 boringMethod() { .. },
10 // (anonymous) function expression property
11 oldSchool: function() { .. }
12};
13
1var x = 1;
2
3if (x) {
4 // will run!
5}
6
7// you may think it's while (x == true)
8while (x) {
9 // will run, once! x = false;
10}
1// But?
2var x = "hello";
3
4if (x) {
5 // will run!
6}
7
8if (x == true) {
9 // won't run :(
10}
Before the comparison, a coercion occurs, from whatever type
x
currently is, to boolean
.1var x = "hello";
2
3if (Boolean(x) == true) {
4 // will run
5}
6
7// which is the same as:
8if (Boolean(x) === true) {
9 // will run
10}
1var Classroom = {
2 welcome() {
3 console.log("Welcome, students!");
4 }
5};
6
7var mathClass = Object.create(Classroom);
8
9mathClass.welcome(); // Welcome, students!
A
mathClass
object is linked via its prototype to a Classroom
object. mathClass.welcome()
is delegated to the method defined on Classroom
.1function Classroom() {
2 // ..
3}
4
5Classroom.prototype.welcome = function hello() {
6 console.log("Welcome, students!");
7};
8
9var mathClass = new Classroom();
10
11mathClass.welcome(); // Welcome, students!
Despite the confusing naming, this is not the function’s
prototype
(where the function is prototype linked to), but rather the prototype object to link to when other objects are created by calling the function with new
.☝️This “prototypal class” pattern is now strongly discouraged, use ES6’s
class
instead:1class Classroom {
2 constructor() {
3 // ..
4 }
5
6 welcome() {
7 console.log("Welcome, students!");
8 }
9}
10
11var mathClass = new Classroom();
12
13mathClass.welcome(); // Welcome, students!
Check the book to see excercies and solutions. There are 3 topics - comparisons, closure and prototypes.