I. Value, Type, Operators
Object, Number, String, Boolean, null, undefined, Symbol
- Symbol: used for global unique value in Map: https://zhuanlan.zhihu.com/p/22652486
typeof
will return lowercase string typetypeof null
=>"object"
typeof undefined
=>"undefined"
typeof 5
=>"number"
typeof Number("4")
=>"number"
typeof NaN
=>"number"
typeof Infinity
=>"number"
typeof [1,2]
=>"object"
comparsion
There is only one value in JavaScript that is not equal to itself, and that is
NaN
(“not a number”).1
console.log(NaN == NaN) // false !!!!
operator used on string / null / number
1
2
3
4
5
6console.log(8 * null) // → 0
console.log("5" - 1) // → 4
console.log("5" + 1) // → "51" !!!
console.log("5" * 1) // → 5
console.log("five" * 2) // → NaN
console.log(false == 0) // → truewhen
null
orundefined
occurs on either side of the operator, it produces true only if both sides are one ofnull
orundefined
.1
2
3
4console.log(null == undefined);
// → true
console.log(null == 0);
// → falseProgram Structure
II. Program Structure
scope: Each variable has a scope, which is the part of the program in which the binding is visible.
- all variables in JS have its own scope, we can use
this
to refer it - useful in Closure
- all variables in JS have its own scope, we can use
variable :are reference, think it as tentscles, not boxes
function (Important List Inside):
optional arguments:
- If you pass too many, the extra ones are ignored.
- If you pass too few, the missing parameters get assigned the value
undefined
.- use default value like this:
function power(base, exponent = 2) {...}
- use default value like this:
- How to call a pass a function with variant num of parameters?:
funName(...arg)
function expression
1
2
3
4// Define f to hold a function value
const f = function(a) {
console.log(a + 2);
};function declaration
1
2
3
4// Declare g to be a function
function g(a, b) {
return a * b * 3.5;
}arrow function: automatically bind its
scope
tothis
inside the function (variables with implicitthis
)1
2// A less verbose function value
let h = a => a % 3;Closure
- What happens to local bindings when the function call that created them is no longer active?
- This feature—being able to reference a specific instance of a local variable in an enclosing scope—is called [closure]
- Local variables inside the outer function are created again for every call
- Different calls can’t trample on one another’s local bindings.
built-in functions
prompt()
can get user’s input using a popup box:let inputName = prompt("please input your name")
console.log()
can also print object,console.log("this obj is", obj)
, instead ofconsole.log("this obj is" + obj)
III. Data Structures: Objects
Property of Object** :
- Almost all JavaScript values have properties. The exceptions are
null
andundefined
. - 2 ways to access properties:
- obj.x
- obj[a] // a = “x”
- Almost all JavaScript values have properties. The exceptions are
Method of Object:
- 2 ways to call methods:
- obj.x
- obj[a] (val) // a = “x”
- 2 ways to call methods:
Mutability:
Tip1: Regardless of whether you use regular or strict equality, object comparisons only evaluate to
true
if you compare the same exact object.Why we need Mutability? - since we want want one obj affect others when we change one of them
1
2
3
4
5let object1 = {value: 10};
let object2 = object1;
object1.value = 15; // we update the value of object1
console.log(object2.value); // the value of object2 also changed, which is not what we want!How to keep Mutability?
1
2
3
4
5let object1 = {value: 10};
let object2 = {...object1};
object1.value = 15; // we update the value of object1
console.log(object2.value); // the value of object2 also changed, which is not what we want!
IV. Data Structures: Array
for...of
vsfor...in
vsforEach()
:for..in
operates on any object; it serves as a way to inspect enumerable properties on this objectfor...of
: mainly used to iterate only iterable object, it serves as a way to inspect all iterable entries of this object; It cannot used on Object, can be only used on Map, Set, Array, String1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// The e.g.
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
let iterable = [3, 5, 7];
iterable.foo = 'hello';
for (let i in iterable) {
console.log(i); // logs 0, 1, 2, "foo", "arrCustom", "objCustom"
}
for (let i in iterable) {
if (iterable.hasOwnProperty(i)) {
console.log(i); // logs 0, 1, 2, "foo"
}
}
for (let i of iterable) {
console.log(i); // logs 3, 5, 7
}forEach()
: is a method only belonging to the royal family of Arrays, including Map, Set (since Map, Set are implemented using Array). It iterate iterable elements in an array. It mainly depends on its callback functionmap.forEach( callback(val, key, map) )
array.forEach( callback(val, index, array) )
set.forEach( callback(val, key, set) )
common methods
includes()
: determines whether an array includes a certain element, returningtrue
orfalse
as appropriate.1
2
3
4
5
6
7
8
9
10
11
12var array1 = [1, 2, 3];
console.log(array1.includes(2));
// expected output: true
var pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));
// expected output: false
forEach()
map()
reduce()
filter()
push()
: push on endpop()
: pop from endshift()
: remove from frontunshift()
: add to frontsort()
reverse()
slice()
: return a new array (sub-array)splice()
: modify current array (current array)join()
: returns a new string by concatenating all of the elements in an array1
2
3
4
5
6
7
8
9
10var elements = ['Fire', 'Wind', 'Rain'];
console.log(elements.join());
// expected output: Fire,Wind,Rain
console.log(elements.join(''));
// expected output: FireWindRain
console.log(elements.join('-'));
// expected output: Fire-Wind-Rain
concat()
indexOf()
lastIndexOf()
find()
: returns the value of the first element in the array that satisfies the provided testing function. Otherwiseundefined
is returned.1
2
3
4
5
6
7
8var array1 = [5, 12, 8, 130, 44];
var found = array1.find(function(element) {
return element > 10;
});
console.log(found);
// expected output: 12
findIndex()
: returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -11
2
3
4
5
6
7
8var array1 = [5, 12, 8, 130, 44];
function findFirstLargeNumber(element) {
return element > 13;
}
console.log(array1.findIndex(findFirstLargeNumber));
// expected output: 3
values()
: returns a new Array Iterator object that contains the values for each index in the array1
2
3
4
5
6const array1 = ['a', 'b', 'c'];
const iterator = array1.values();
for (const value of iterator) {
console.log(value); // expected output: "a" "b" "c"
}
fill()
: fills all the elements of an array from a start index to an end index with a static value. The end index is not included.1
2
3
4
5
6
7
8
9
10
11
12var array1 = [1, 2, 3, 4];
// fill with 0 from position 2 until position 4
console.log(array1.fill(0, 2, 4));
// expected output: [1, 2, 0, 0]
// fill with 5 from position 1
console.log(array1.fill(5, 1));
// expected output: [1, 5, 5, 5]
console.log(array1.fill(6));
// expected output: [6, 6, 6, 6]
flat()
: creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.1
2
3
4
5
6
7
8
9
10
11var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
IV. Data Structures: Map
- internal structure
new Map([iterable])
- iterable: key-value pairs (arrays with two elements, e.g.
[[ 1, 'one' ],[ 2, 'two' ]]
) new Map( [ ["a", 1], ["b", 2], ["c", 3], ["d", 4] ] )
- Map vs Object
- The keys of an
Object
areStrings
andSymbols
, whereas they can be any value for aMap
, including functions, objects, and any primitive. - The keys in Map are ordered while keys added to object are not. Thus, when iterating over it, a Map object returns keys in order of insertion.
- You can get the size of a
Map
easily with thesize
property, while the number of properties in anObject
must be determined manually. - A
Map
is an iterable and can thus be directly iterated, whereas iterating over anObject
requires obtaining its keys in some fashion and iterating over them. - An
Object
has a prototype, so there are default keys in the map that could collide with your keys if you’re not careful. As of ES5 this can be bypassed by usingmap = Object.create(null)
, but this is seldom done. - A
Map
may perform better in scenarios involving frequent addition and removal of key pairs.
- The keys of an
V. Higher-Order Functions
what?
- Functions that operate on other functions, either by taking them as arguments or as return value, are called higher-order functions
why? => handle actions, not just values
eg1 : use function as arguments
1
2
3
4
5
6
7
8
9
10
11function add(obj){ obj += 1 };
function substract(obj){ obj -= 1};
function repeatAction(times, action) {
for (let i = 0; i < times; i++) {
action(i);
}
}
repeatAction(5, add );
repeatAction(5, substract );eg2 : use function as return value
1
2
3
4
5
6
7
8
9
10// utility function: returns another function,
// can check if a number > certain number
function greaterThan(n) {
return m => m > n;
}
let greaterThan10 = greaterThan(10);
console.log(greaterThan10(11));
// → trueeg3:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40// ----------- 1. filter() -----------
function filter(array, test) {
let passed = [];
for (let element of array) {
if (test(element)) {
passed.push(element);
}
}
return passed;
}
console.log(filter(SCRIPTS, script => script.living));
// → [{name: "Adlam", …}, …]
// ----------- 2. map() -----------
function map(array, transform) {
let mapped = [];
for (let element of array) {
mapped.push(transform(element));
}
return mapped;
}
let rtlScripts = SCRIPTS.filter(s => s.direction == "rtl");
console.log(map(rtlScripts, s => s.name));
// → ["Adlam", "Arabic", "Imperial Aramaic", …]
// ----------- 3. reduce() -----------
function reduce(array, combine, start) {
let current = start;
for (let element of array) {
current = combine(current, element);
}
return current;
}
console.log(reduce([1, 2, 3, 4], (a, b) => a + b, 0));
// → 10
Composability [pipeline]
what: we want to compose operations, to make meaningful & readable code
How: create functions, to make a series action into a pipeline
e.g.:
- we start with all scripts,
- filter out the living (or dead) ones,
- take the years from those,
- average them,
- and round the result.
1
2
3
4
5
6function average(array) {
return array.reduce((a, b) => a + b) / array.length;
}
console.log(Math.round(average(
SCRIPTS.filter(s => s.living).map(s => s.year))));
VI. The life of Object
Encapsulation / 封装
What?:
- Internal details of one object cannot be access from outside
- objects communicate via their interface
How?
- private: put an underscore (
_
) character at the start of property names to indicate that those properties are private.
- private: put an underscore (
property
- belongs to that object itself
- or can be traced back up to its prototype
Method
- each function has its own
this
property arrow function: they do not bind their own
this
but can see thethis
binding of the scope around themthis
: every method have this implicit parameter passed into the function. The following are the same1
2
3
4
5
6
7function speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
let whiteRabbit = {type: "white", speak};
whiteRabbit.speak("Burp"); // same effect
speak.call(whiteRabbit, "Burp!"); // same effect
- each function has its own
Polymorphism
overriding derived property
1
2
3
4
5
6
7
8
9
10Rabbit.prototype.teeth = "small";
console.log(killerRabbit.teeth);
// → small
killerRabbit.teeth = "long, sharp, and bloody";
console.log(killerRabbit.teeth);
// → long, sharp, and bloody
console.log(blackRabbit.teeth);
// → small
console.log(Rabbit.prototype.teeth);
// → small
override derived method
1
2
3
4
5
6Rabbit.prototype.toString = function() {
return `a ${this.type} rabbit`;
};
console.log(String(blackRabbit));
// → a black rabbit
Why use Map instead of pure Object?
We certainly didn’t list anybody named toString in our map.
Yet, because plain objects derive from
Object.prototype
, it looks like the property is there.1
2
3
4
5
6
7
8
9
10
11
12
13let ages = {
Boris: 39,
Liang: 22,
Júlia: 62
};
console.log("Is Jack's age known?", "Jack" in ages);
// → Is Jack's age known? true
console.log("Is Jack's age known?", "Jack" in ages);
// → Is Jack's age known? false
console.log("Is toString's age known?", "toString" in ages);
// → Is toString's age known? true
// ====>【what we don't want!!!】
getters, setters, and static
get
: The get syntax binds an object property to a function that will be called when that property is looked up.1
2
3
4
5
6
7
8
9
10
11var obj = {
log: ['a', 'b', 'c'],
get latest() {
if (this.log.length == 0) {
return undefined;
}
return this.log[this.log.length - 1];
}
}
console.log(obj.latest);
set
: The set syntax binds an object property to a function to be called when there is an attempt to set that property.1
2
3
4
5
6
7
8
9
10
11
12var language = {
set current(name) {
this.log.push(name);
},
log: []
}
language.current = 'EN';
language.current = 'FA';
console.log(language.log);
// expected output: Array ["EN", "FA"]
static
: methods that havestatic
written before their name are stored on the constructor.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Temperature {
constructor(celsius) {
this.celsius = celsius;
}
get fahrenheit() {
return this.celsius * 1.8 + 32;
}
set fahrenheit(value) {
this.celsius = (value - 32) / 1.8;
}
static fromFahrenheit(value) {
return new Temperature((value - 32) / 1.8);
}
}
let temp = new Temperature(22);
console.log(temp.fahrenheit);
// → 71.6
temp.fahrenheit = 86;
console.log(temp.celsius);
// → 30
Temperature.fromFahrenheit(100)
// -> Temperature {celsius: 37.77777777777778}
Inheritance
extends
operator1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class SymmetricMatrix extends Matrix {
constructor(size, element = (x, y) => undefined) {
super(size, size, (x, y) => { // 'super' call its parents constructor
if (x < y) return element(y, x);
else return element(x, y);
});
}
set(x, y, value) {
super.set(x, y, value); // 'super' call its parents methods
if (x != y) {
super.set(y, x, value); // 'super' call its parents methods
}
}
}
let matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`);
console.log(matrix.get(2, 3));
// → 3,2
instanceof
operator1
2
3
4
5
6
7
8
9console.log(
new SymmetricMatrix(2) instanceof SymmetricMatrix);
// → true
console.log(new SymmetricMatrix(2) instanceof Matrix);
// → true
console.log(new Matrix(2, 2) instanceof SymmetricMatrix);
// → false
console.log([1] instanceof Array);
// → true
prototype: 也是一个object, 用来表明该object中的哪些method用来 inherite
prototype
vs__proto__
prototype
: an shared object contains all methods to be inherited by child objects__proto__
: indicating the object it’s inheriting, ie,obj.prototype
what: can be treated as parent class in java. A prototype is another object that is used as a fallback source of properties.
object.prototype === null
Object.create()
: create an object using anther object as the prototype. likeconstructor()
1
2
3
4
5
6
7
8
9let protoRabbit = {
speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
};
let killerRabbit = Object.create(protoRabbit);
killerRabbit.type = "killer";
killerRabbit.speak("SKREEEE!");
// → The killer rabbit says 'SKREEEE!'
class: clearer way to create prototype
What & Details
- The one named
constructor
is treated specially. It provides the actual constructor function, which will be bound to the nameRabbit
. - The others are packaged into that constructor’s prototype. Thus, the earlier class declaration is equivalent to the constructor definition from the previous section. It just looks nicer.
- The one named
1
2
3
4
5
6
7
8
9
10
11class Rabbit {
constructor(type) {
this.type = type;
}
speak(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
}
}
let killerRabbit = new Rabbit("killer");
let blackRabbit = new Rabbit("black");NOTE: the constructor are capitalized
1
2
3
4
5
6
7
8function Rabbit(type) { // note: name are capitalized
this.type = type;
}
Rabbit.prototype.speak = function(line) {
console.log(`The ${this.type} rabbit says '${line}'`);
};
let weirdRabbit = new Rabbit("weird");
VII. Bugs
strict mode
why? => we don’t want to write bugs!
how?
use strict
at top of a fileuse strict
in body of function1
2
3
4
5
6
7
8
9function canYouSpotTheProblem() {
;
for (counter = 0; counter < 10; counter++) {
console.log("Happy happy");
}
}
canYouSpotTheProblem();
// → ReferenceError: counter is not defined
regular express
A regular expression is a type of object
- with the
RegExp
constructor - as a literal value by enclosing a pattern in forward slash (
/
) characters
- with the
1
2let re1 = new RegExp("abc");
let re2 = /abc/;
VIII. Modules
what & why
- Modules are code snippet can be reused repeatedly
- We want do DRY
Inner implementation: [ important ]
we need JS has the ability to execute string of code, because what imported in are strings!
2 ways to execute strings as code in JS
eval()
: drawback => change scope!1
2
3
4
5
6
7
8
9
10const x = 1;
function evalAndReturnX(code) {
eval(code);
return x;
}
console.log(evalAndReturnX("var x = 2"));
// → 2
console.log(x);
// → 1
Function ( params_string, body_string )
: self-contained module scope1
2
3let plusOne = Function("n", "return n + 1;");
console.log(plusOne(4));
// → 5
CommonJS:
require
&exports
How to use
1
2
3
4
5
6
7
8
9
10
11
12
13const ordinal = require("ordinal");
const {days, months} = require("date-names");
exports.formatDate = function(date, format) {
return format.replace(/YYYY|M(MMM)?|Do?|dddd/g, tag => {
if (tag == "YYYY") return date.getFullYear();
if (tag == "M") return date.getMonth();
if (tag == "MMMM") return months[date.getMonth()];
if (tag == "D") return date.getDate();
if (tag == "Do") return ordinal(date.getDate());
if (tag == "dddd") return days[date.getDay()];
});
};
require
: a function mainly used by CommonJS- calling a function to access a dependency
- When you call this with the module name of a dependency, it makes sure the module is loaded and returns its interface.
- The inner implementation of
require()
1
2
3
4
5
6
7
8
9
10
11
12require.cache = Object.create(null); // a running-time global obj
function require(name) {
if (!(name in require.cache)) { // check if alreay has the module in cache
let code = readFile(name); // read [code STRING] from file
let module = {exports: {}}; // an callback container to fetch real one
require.cache[name] = module; // bind this container to cache
let wrapper = Function("require, exports, module", code);// 'eval' code String in <local function scope>
wrapper(require, module.exports, module); // put all the code to module,
} // while tackle module of the required one
return require.cache[name].exports; // output the
}
ES6 Module:
export
&import
what & why [imported module are reference, not value]
- Same: try to tackle dependency, but
- Difference:
- imported module using ES6 are reference, not value; So local scope can access its value when there is update inside the imported module
- exporting module may change the value of the binding at any time, and the modules that import it will see its new value.
How to use
1
2
3
4import ordinal from "ordinal";
import {days, months} from "date-names";
export function formatDate(date, format) { /* ... */ }
IX. Asynchronous Programming
callback function
Asynchronous behavior happens on its own empty function call stack
callbacks are called by others, not directly called by the code that scheduled them.
- If I call
setTimeout
from within a function, that function will have returned by the time the callback function is called. - And when the callback returns, control does not go back to the function that scheduled it.
Promise
async
&await
generator
- What: functions can be paused and then resumed again
- how:
function* funName(){ ... }
event loop
- all asynchronous events are put onto a
Queue
- all synchronous function are executed in
Stack
- all asynchronous events are put onto a