ECMA

ECMAScript® 2021

QRCODE with link ecma spec

https://tc39.es/ecma262/

Data Types

  1. Undefined
  2. Null
  3. Object
  4. Boolean
  1. String
  2. Number
  3. Symbol
  4. BigInt

Undefined

[undefined]
// x defined
var x;
if (x === undefined) {}
if (x === void 0) {}
 
// x not defined
if (typeof x === "undefined") {}

NULL

[null]

Object

  1. Constructor
  2. Prototype
  3. Ordinary
  1. Exotic
  2. Standard
  3. Built-in
  1. Boolean
  2. Number
  3. String
  1. BigInt
  2. Symbol
let container;
 
container = {};
container = Object(value);
container = new Object(value);
container = Object.create(prototype, props);

Boolean

[true; false]
let flag;
 
flag = false;
typeof flag; // boolean
flag = Boolean(false);
typeof flag; // boolean
 
flag = new Boolean(flag);
typeof flag; // object
 
flag = flag.valueOf();
typeof flag; // boolean
flag = new Boolean(); // false
flag = new Boolean(document.all); // false
flag = new Boolean(false);
if (flag) {
  // ???
}
flag = new Boolean(false);
typeof flag; // object
if (flag) {
  // будет выполнено    
}

String

[0; FFFF₁₆]{0, 2⁵³-1}
let container;
 
container = "Content";
typeof container; // string
 
container = String("Content");
typeof container; // string
 
container = new String(container);
typeof container; // object
 
container = container.valueOf();
typeof container; // string
container = "Content";
typeof container; // string
container.charAt(2); // n
typeof container; // string
container[2]; // n
container = "Line 1" + 
            "Line 2" +
            "Line 3";
 
container = "Line 1 \ 
             Line 2 \
             Line 3";

String.→

  1. startsWith("Substring")
  2. endsWith("Substring")
  3. includes("Substring")
  4. repeat(count)
  1. raw(callSite, ...substitutions)
  2. fromCodePoint(...codePoint)
  3. codePointAt(pos)
  4. normalize(form)

Template literal

`Literal`
`Line 1
 Line 2`
let name = "Valera"
  , result;
result = `And where is my ${name}?`
result; // And where is my Valera?
let  result
  , price = 234
  , tax = price * 0.05;
result = `Item cost € ${(price + tax).toFixed(2)}`
result; // Item cost € 245.70
currency`Item cost 
         ${price + tax}:c(EUR,nl), 
         that much.`;
// Item cost € 245,70, that much.
 
currency`Item cost 
         ${price + tax}:c(JPY,ja-JP), 
         that much.`;
// Item cost ¥246, that much.
function currency(strings, ...values) {
 // string = ["Item cost ", ":c(EUR,nl), that much."]
 // values = [245.7]
 // …
}
// …
const regExp = /^:c(\((.+),(.+)\))?/;
let strings_set = [...strings]
  , values_set = [...values];
// …
// …
for (let key in values_set) {
    const index = +key+1
        , string = strings_set[index]
        , match = regExp.exec(string);
    // …
}
// …
// …
if (match === null) continue;
// …
// …
// [":c(EUR,nl)", "(EUR,nl)", "EUR", "nl"]
const [ , , currency, locale] = match;
strings_set[index] = strings_set[index]
                      .replace(regExp, "");
values_set[key] = values_set[key]
                      .toLocaleString(locale, {
                          style: 'currency'
                        , currency: currency });
// …
// …
return String.raw({
  raw: strings_set
 }, ...values_set);

Example of string linterals usage

QRCODE with link to example

https://jsfiddle.net/silentimp/2dv5ua79/

Number

Number.→

  1. isFinite(number)
  2. isNaN(number)
  3. EPSILON
  4. toLocaleString(locales, options)
  1. isSafeInteger(number)
  2. MIN_SAFE_INTEGER
  3. MAX_SAFE_INTEGER
[-(2⁵³ - 1); +(2⁵³ - 1)]
+0
-0
+Infinity
−Infinity
NaN
-1/0; // -Infinity
Infinity/Infinity; // NaN
-1/Infinity; // -0
Math.round(-0.1); // -0
NaN === NaN; // false
+0 === -0; // true
let container;
 
container = 123.12;
typeof container; // number
 
container = Number(123.12);
typeof container; // number
 
container = new Number(container);
typeof container; // object
 
container = container.valueOf();
typeof container; // number
23.51 // decimal
2351e-2 // scientific, 23.51
0x10 // hexadecimal, 16
0b10 // binary, 2
0o10 // octal, 8
let x = 10;
x.toString(16); // a
x.toString(8); // 12
x.toString(6); // 14
x.toString(2); // 1010
x.toExponential(1); // 1.0e+1
0.2 + 0.1 !== 0.3 
// 0.30000000000000004
0.2.toFixed(20) 
// 0.20000000000000001110
0.1.toString(2) 
// 0.000110011001100[1100]
(
  Math.abs(
        (0.2 + 0.1) - 0.3 
  ) < Number.EPSILON
); // true
Number.isNaN('???') //false
isNaN('???') //true 
12.34.toPrecision();  // 12.34
12.34.toString();     // 12.34
12.34.toPrecision(5); // 12.340
12.34.toPrecision(4); // 12.34
12.34.toPrecision(3); // 12.3
12.34.toPrecision(2); // 12
12.34.toPrecision(1); // 1e+1
0.002345.toPrecision(5); // 0.0023450
0.002345.toPrecision(4); // 0.002345
0.002345.toPrecision(3); // 0.00234
0.002345.toPrecision(2); // 0.0023
0.002345.toPrecision(1); // 0.002
0.002385.toFixed(5); // 0.00238
0.002386.toFixed(5); // 0.00239
0.002385.toFixed(4); // 0.0024
0.002385.toFixed(3); // 0.002
0.002385.toFixed(2); // 0.00
0.002385.toFixed(1); // 0.0
0.002385.toFixed(0); // 0
10..toFixed(5); // 10.00000
// .toLocaleString([locales [, options]])
10000000..toLocaleString(); // 10,000,000
10000000..toLocaleString('RU'); // 10 000 000
10000000..toLocaleString('DE'); // 10.000.000
// .toLocaleString([locales [, options]])
10000000..toLocaleString(); 
// 10,000,000
10000000..toLocaleString('RU'); 
// 10 000 000
10000000..toLocaleString('DE'); 
// 10.000.000
10000000..toLocaleString('en-US', {
  style: "currency",
  currency: "USD"   
}); // $10,000,000.00
10000000..toLocaleString('RU', {
  style: "currency",
  currency: "RUB"   
}); // 10 000 000,00 ₽
10000000..toLocaleString('DE', {
  style: "currency",
  currency: "EUR"   
}); // 10.000.000,00 €

symbol

let key_name;
 
// Unique symbol
key_name = Symbol(); 
key_name = Symbol("Wide description"); 
 
// Character to be entered in the register
key_name = Symbol.for("Wide description"); 
this[key_name] = "Valera";
let key_1, key_2;
 
key_1 = Symbol("Wide description");
key_2 = Symbol("Wide description"); 
(key_1 === key_2) // false
 
key_1 = Symbol.for("Wide description");
key_2 = Symbol.for("Wide description"); 
(key_1 === key_2) // true
key_name = new Symbol(); // TypeError
key_name = Symbol();
typeof key_name; // "symbol" 
key_name = Object(key_name);
typeof key_name; // "object" 
key_name = key_name[Symbol.toPrimitive]('symbol');
typeof key_name; // "symbol" 
this[key_name] = "Valera";
let obj = {"a": 123}
  , key = Symbol.for('Wide description');
obj[key] = "Valera";
Object.getOwnPropertySymbols(obj);
// [Symbol(Wide description)]
for (let x of obj) {} // as usual
for (let x in obj) {} // ignoring symbols

typeof

  • Undefined
  • Null баг
  • Boolean
  • String
  • "undefined"
  • "object"
  • "boolean"
  • "string"
  • Number
  • Object
  • Object callable
  • Symbol
  • BigInt
  • "number"
  • "object"
  • "function"
  • "symbol"
  • "bigint"
Object.prototype
  .toString
      .call((new Date())) 
      // [Object Date]
          .slice(8, -1)
          // Date

Coercion

→ ToPrimitive(input, hint)

Undefined input
Null input
Number input
Boolean input
String input
Symbol input
BigInt input
Object tricky bit

→ ToPrimitive(input, hint)

  1. hint can be [default, string, number]
  2. if input is exotic object check if it has @@toPrimitive
  3. if it has and value is a callable object — call and return result or TypeError
const date = new Date()
date[Symbol.toPrimitive]('number')
// 1588847933493
date[Symbol.toPrimitive]('string')
// "Thu May 07 2020 12:38:53 GMT+0200 
//  (Central European Summer Time)"

→ ToPrimitive(input, hint)

  1. if it doesn't or input — ordinary object, continue
  2. if hint == "default", set it to "number"
  3. call OrdinaryToPrimitive(input, hint)

→ ToPrimitive(input, hint)

  1. if hint — string call toString, valueOf
  2. if hint — number call valueOf, toString
  3. if method is present and callable — call it and return result or TypeError

→ toBoolean

Undefined false
Null false
Number false for [+0; -0; NaN] or true
String false for [""] or true
Symbol true
BigInt false for [0n] or true
Object true if not document.all
(value === 'true');  // true
typeof (value === 'true');  // boolean
!!+"1"; // true
typeof !!+"1"; // boolean

→ toNumber

Undefined NaN
Null +0
Boolean 1 если true, 0 если false
String
  1. Trying ot use StringNumericLiteral UTF-16
  2. Otherwise return NaN
Symbol TypeError
BigInt TypeError
Object
  1. ToPrimitive(input, number)
  2. ToNumber(input)
parseInt(value, 10);
parseFloat(value);
+value;
Number(value);

→ toString

Undefined "undefined"
Null "null"
Boolean "true" if true, "false" if false
Symbol TypeError
Object
  1. ToPrimitive(input, string)
  2. ToString(input)

→ toString

Number "NaN" if NaN
"0" if +0 или -0
"Infinity" if +∞
"-" + toString(arg) if value less then 0
number N convert to UTF-16
value.toString(); 
// don't work with null
// don't work with undefined
"" + value;
String(value);

→ toObject

Undefined TypeError
Null TypeError
BooleanNew object Boolean
NumberNew object Number
StringNew object String
BigIntNew object BigInt
SymbolNew object Symbol
Object("3"); 
// String {"3"}
Object(3); 
// Number {10}

→ toBigInt

Call ToPrimitive(argument, hint Number), or

Undefined TypeError
Null TypeError
Boolean 1n if true, 0n if false
Number TypeError
Symbol TypeError
String
  1. Trying ot use StringNumericLiteral UTF-16, but no Infinity, decimal point or exponents
  2. Otherwise its NaN, throw TypeError
BigInt("23");
// 23n
BigInt(3);
// 3n
// no TypeError because 
// of ToPrimitive (.valueOf())

What the f*ck JavaScript?

QRCODE with link to the presentation

https://github.com/denysdovhan/wtfjs

Type safety

// @flow
function concat(a: string, b: string) {
    return a + b;
}
function concat(a: string, b: string) {
    return a + b;
}

Let's make JavaScript a real language!

JAVA

npx -p typescript tsc 
    --allowJs 
    --checkJs 
    --noEmit 
    src/**/**.js
/**
 * Cities - Class to work with unique cities
 * @class
 * @namespace
 */
class Cities {
    /**
     * @type {array}
     * @static
     */
    static collector = [];
}
/**
 * getCity - Get city by index
 * @throws {Error} if index not found
 * @param {number} index city index
 * @return {string} city name
 */
getCity(index) {
    // …
}

And
what
does it do?

— Nothing.

npm i -D jsdoc
// package.json
"scripts": {
    "doc": "jsdoc src/*.js -d docs"
}
npm run doc
npm i -D jsdoc-babel
// conf.json
{
    "plugins": 
        ["node_modules/jsdoc-babel"]
}
npm i -D documentation
// package.json
"scripts": {
    "doc": "documentation 
        build src/*.js 
        -f html -o docs"
}
npm run doc
// README.md
# Cities
Cool Library that …

# API
// package.json
"scripts": {
    "readme": "documentation 
        readme src/*.js 
        --section=API"
}
npm run readme

Where is my type linting, dude?

— TS can read JSDoc.

Type Safe JavaScript with JSDoc

QRCODE with link to article about configuring jsdoc type checks

https://goo.gl/sBHmmH

npm i -D typescript
// tsconfig.json
{
    "compilerOptions": {
        "target": "ESNext",
        "noEmit": true,
        "allowJs": true,
        "checkJs": true,
    },
    "include": [
        "src/**/*.js"
    ]
}
// package.json
"scripts": {
    "lint": "tsc"
}
/**
 * Add new city
 * @param {string} word city name
 */
add(word) {
    //…
}
citys.add(222);
npm run lint
// package.json
{ // …
    "scripts": { // …
        "precommit": "lint-staged"
    },
    "lint-staged": { // …
        "*.{js,jsx}": "tsc --target ESNext 
            --noEmit --allowJs --checkJs"
    }
}

Anton Nemtsev

skype: ravencry

QRCODE with link to the presentation

https://github.com/SilentImp/ES6Types