ECMA - GradedJestRisk/js-training GitHub Wiki
Overview:
- JavaScript
- high-level, interpreted, curly-bracket syntax, dynamic typing
- support event-driven, functional, and imperative (eg. object-oriented)
- does not include any I/O (networking, storage, graphics facExportsilities) => relies upon the host environment
- scope
- initial: client-side in web browsers (vanilla JS)
- later:
- executes JS code outside of a browser (Node.js)
- library for building user interfaces (React.js)
- frameworks (Vue.js, Angular.js, Express.js)
Version:
- (Head first JS: 2014 - does NOT include ECMA6)
- 6: ES2015 (Harmony) - feature list
- add
let
to restrict variable scope to a code block (var declare a function-based scope) - add class declarations
class Foo { ... }
- add modules
- add
// lib/math.js export function sum (x, y) { return x + y } export var pi = 3.141593 // someApp.js import * as math from "lib/math" console.log("2π = " + math.sum(math.pi, math.pi))
import * as moduleName from "..."; export const Foo;
- add iterators and
for/of
- add constant variable declaration
const
- add new collection type: maps, sets
- destructuring assignment
var list = [ 1, 2, 3 ]; var [ a, , b ] = list; [ b, a ] = [ a, b ];
- array find
- add iterators and
[ 1, 3, 4, 2 ].find(x => x > 3) // 4 [ 1, 3, 4, 2 ].findIndex(x => x > 3) // 2
List:
- primitive types: number, boolean, strings, array
- special types: function, object, String, Array
- variables: local and global
element | chars | symbol | sample |
---|---|---|---|
end of statement |
;
|
semi colon |
return;
|
assignment (value/ref) |
=
|
equal |
var name = "joe"; var person = { name:"joe", age:19 }; |
object definition (property value - function definition assigner) |
:
|
colon |
var person = { name : "joe", assign: function(role){(..)} }; |
function parameters |
()
|
parentheses |
function assign (person, role)
|
array constructor |
[]
|
square brackets |
myArray = ["apple", "orange"]
|
code block object definition |
{}
|
curly braces |
else {console.log("")} var person = { name:joe, age:19 } |
delimiter |
,
|
comma |
function assign (person, role) var person = { name:joe, age:19 } |
anonymous functions |
=>
|
fat arrow/comma, hash rocket |
(x => x*x)(10) [evaluate to 100]
|
You can declare your variable "name" (aka identifier, not type).
Its value will be undefined (known as hoisting).
But JavaScript does not require that.
It will prevent you to implicitly create global, hidden in closures.
"use strict"; x = 3.14; Uncaught ReferenceError: x is not defined
See modes
keyword | stands for | scope | mutability | sample |
---|---|---|---|---|
empty | global | global | mutable |
i = 8 ;
|
var | variable | function | mutable |
var i;
|
let | constant | block | mutable |
let i;
|
const | constant | block | immutable |
const i;
|
Introduce a variable:
- start with const in
const myWellNamedVariable;
in the innermost code block; - if you need mutability, switch to let;
- if you need function scoped:
- move let declararation to function uppermost code block
- if you're still dissatisfied (why ?), think twice, and then with pain, replace let by var.
Creation, 2 ways:
- object literal:
- straightforward:
const person = { name: 'joe', age:19 };
- step-by-step:
- straightforward:
const person = {}; person.name = 'joe'; person.age = 19;
- object constructor
var person = new Person{ joe, 19 } ;
JavaScript has:
-
first-class functions
- function as argument and variable
- function that create function, aka lambda
- naming:
- named
- anonymous
- invocation mode:
- invoked later
- immediately invoked (Immediately Invoked Function Expression, IIFE)
- take no argument;
- does not return anything;
- has no side-effect;
- store no code
- store some state.
const myStrangeFunction = function(){ state : 'hidden_state'; }; const result = myStrangeFunction();
A function is a regular JS object, but can be seen as, in addition, to:
- has a string containing code
- can be invoked
- receive arguments and returns values.
const body = "return Math.PI * radius * radius"; var circle = new Function("radius", body); console.log(circle(1));
Function:
- reuse code
- named block with arguments
- invoke with ()
- pass variables as argument
- return variables
Declare function:
- standalone:
function <FUNCTION_NAME> (<PARAMETERS>){(..)}
,
egfunction assign (person, role){..}
- object:
<FUNCTION_NAME>: function(<PARAMETERS>){(..)}
,
egassign: function (person, role){..}
You will met these forms.
Functions are not invoked here.
code | name | type | invoke | scope |
---|---|---|---|---|
function greet(name) {};
|
definition | definition | N times | global ? |
greetFunction = function greet (name) {};
|
expression | named | N times | global ? |
greetFunction = function (name) {};
|
expression | anonymous (standard) | N times | global ? |
greetFunction = (name) => {};
|
expression | anonymous (arrow) | N times | global ? |
You can assign a function expression to :
- a variable with =
- an object property with :
They are invoked when defined.
As such, their syntax is ( definition ) (argumentValue)
They can be:
- named
- anonymous (arrow or regular)
Arrow function show input => ouput
.
The usual form being
function increment(input) { const out put = input + 1; return ouput }
Its aims to add expresiveness for short function bodies.
More symbols are required to handle:
- additional parameters;
- statement instead of expressions.
So forget using arrow functions if it ends up making your code more complex that not.
const myFn = (x) => { return x + 1; }; const myFn = (x) => x + 1; const myFn = x => (x + 1); const myFn = x => x + 1;
The better choice is the latter: x => x + 1
They have their own scope rules, especially on this keywords to prevent side effects.
Functions are always invoked here ()
parameter | body | syntax | sample |
---|---|---|---|
(none) | expression |
() => expression ()
|
() => 'foo' ()
|
1 | expression |
parameter => expression
|
( name => name.length ) ()
|
>1 | expression |
(firstParameter, secondParameter) => expression
|
|
>1 | statement |
(firstParameter, secondParameter) => { (statements) }
|
A function that sums two number:
- standard way
const add = (a, b) => a + b add(1, 2)
- currified way
const add = a => b => a + b add(1)(2)
Step by step:
- takes one argument
- return another (anonymous) function that
- takes the second argument
- add both elements
- return the result.
The second form works because x is still in scope (thanks to closure).
Let's figure two objects having a common property, id, stored in a collection
To extract all ids, we need to write down twice the map code.
movies.map( (movie) => movie.id ) series.map( (serie) => serie.id )
With currying:
- let's create an generic accessor
const get = property => object => object[property];
- and then a specific accessor for id property
const getId = get('id');
- now, we can use it on any object
movies.map(getId); series.map(getId);
JavaScript is a:
- dynamic typing: useless to declare a variable type;
- weak type check: you can combine different type in expressions.
== or ==, this is the question ?
Overview:
- Array
- Map
- Set
- String
let string = 'this is a string'; for (let char of string) { console.log("Character :", char); } let string = 'this is a string'; for (let char in string) { console.log("Character :", char); }
List:
declare: let myArray = [];
method/property | Use | sample |
---|---|---|
length | array size |
let arraySize = anArray.length;
|
concat | return an array with both array elements |
let totalArray = beginningArray.concat(endArray);
|
JS module enable:
- modularity: subdivides a system into smaller parts, which can be independently created, modified or exchanged between different systems.
- restrict public interface (hide implementation details).
See node
All module's mode is strict, see modes
file is prototype.js
const makeAgedBrie = function (agedBrieParams) {(..)}; // export export Item; export Shop; export makeAgedBrie, OR export { Item, SHop, makeAgedBrie}
More in API
file is prototype.test.js
// import import {Shop, Item, makeAgedBrie} from "./prototype"; OR import from "./prototype"; // use const standardItem = new Item(standardItemParams) ;
You can rename module and imports, see API
You can:
- rename (to avoid name clash)
import { export1 as alias1 } from "module-name";
- omit a name
import defaultExport from "module-name";
- execute code and discard the reference (side-effect only):
import '/modules/my-module.js';
Prototype-based API
File is rectangle.js
function Rectangle(width, height) { this.height = width; this.width = height; } Rectangle.prototype.area = function() { return this.height * this.width; };
File is rectangle.test.js
const Rectangle = require('./rectangle'); const rectangle = new Rectangle(2, 4); console.log(rectangle.area()); // 8
Syntax
File is rectangle.js
class Rectangle { constructor(height, width) { this.height = height; this.width = width; } area() { return this.height * this.width; } }
File is rectangle.test.js
const Rectangle = require('./rectangle'); const rectangle = new Rectangle(2, 4); console.log(rectangle.area()); // 8
See also:
- binding a function to the object to compute a value without executing it:
get
keyword - class-level
- methods: inside class definition, using
static
keyword - properties: outside of class definition,
Rectangle.hasFourSide = true
- methods: inside class definition, using
- invoking parent with
super
keyword - mix-in: can be implemented a function, taking a superclass as input, outputting the subclass
File animal.js
class Animal { constructor(name) { this.name = name; } speak() { console.log(`${this.name} makes a noise.`); } }
File dog.js
class Dog extends Animal { constructor(name) { super(name); // call the super class constructor and pass in the name parameter } speak() { console.log(`${this.name} barks.`); } }
File animal.test.js
let d = new Dog('Mitzie'); d.speak(); // Mitzie barks.
class Rectangle { height = 0; width; constructor(height, width) { this.height = height; this.width = width; } }
class Rectangle { #height = 0; #width; constructor(height, width) { this.#height = height; this.#width = width; } }
States:
- pending: initial state, neither fulfilled nor rejected.
- fulfilled: meaning that the operation completed successfully.
- rejected: meaning that the operation failed.
- await: to wait for a Promise. It can only be used inside an async function.
A list:
- list all object properties:
console.dir
- list all collection members:
console.table
- format:
- indent:
console.group()
- unindent:
console.groupEnd()
- indent:
- timer:
- start:
console.time()
- stop:
console.timeEnd()
- start:
Two modes:
- strict;
- sloppy.
"use strict"; myFunction { "use strict"; }
Be aware that strict mode change other behaviours ! eg. this
keyword: full list here
Can strict considered harmful ?