Современное состояние JavaScript

Технопарк, весна, 2024 г.

Современный JavaScript
Слайды доступны по ссылке
frontend.tech-mail.ru

180 слайдов

190 минут 🤔

ISO/IEC 16262:2011(E)

ECMA-262

ECMAScript

Что есть JavaScript?

Подробнее про термины

Как это было

Подробнее про историю развития

Как это было

Современное состояние JavaScript

Скучненько

Определение переменных

Поднятие переменных

		console.log(typeof foo);
		console.log(typeof bar);
		 
		var foo = 'bar';
		if (false) {
		    var bar = 42;
		}
		 
		 
	

Поднятие переменных

		console.log(typeof foo);  // undefined
		console.log(typeof bar);  // undefined
		 
		var foo = 'bar';
		if (false) {
		    var bar = 42;
		}
		 
		 
	

let / const

		if (true) {
		    let bar = 42;
		}
		console.log(typeof foo);  // ReferenceError
		console.log(typeof bar);  // ReferenceError
		 
		const foo = 'bar';
		foo = 'baz';  // TypeError
		 
	

Строки

Шаблонные строки

		const name = 'Jon Snow';
		const answer = 40;
		 
		const res = `Hello, ${name}! Answer is ${answer + 2}`
		console.log(res);  // Hello, Jon Snow! Answer is 42
		 
		const multiline = `First line
		Second line
		Third line`;
		multiline.split('\n').length === 3; // true
		 
	

Поддержка Юникода

		// unicode support
		console.log('😀'.length); // 2
		console.log('\u{1F600}'); // 😀
		console.log('\uD83D\uDE00'); // 😀
		 
		String.prototype.charAt(index);       '😀'.charAt(0) === �
		String.prototype.charCodeAt(index);   '😀'.charCodeAt(0) === 55357
		String.prototype.codePointAt(index);  '😀'.codePointAt(0) === 128512
		 
	

Поддержка Юникода

		const переменная = 42;
		const джонни = {
		    имя: 'Jon',
		    фамилия: 'Snow',
		    возраст: 20
		};
		 
		function распечатать (пользователь) {
		    console.log(`${пользователь.имя} ${пользователь.фамилия}`);
		    console.log(`Возраст ${пользователь.возраст} лет`);
		}
		 
		распечатать(джонни);
		 
	

Дополнительные методы строк

		// ECMAScript 2015 features
		String.prototype.includes(searchString, position = 0)
		String.prototype.endsWith(searchString, position = length)
		String.prototype.startsWith(searchString, position = 0)
		String.prototype.repeat(times)
		 
		// ECMAScript 2017 features
		String.prototype.padStart(maxLength, fillString=' ')
		String.prototype.padEnd(maxLength, fillString=' ')
		 
		 
	

Промисы

Промисы

		function getJSON(url) {
		    return new Promise(function (resolve, reject) {
		        const xhr = new XMLHttpRequest();
		        xhr.open('GET', url, true);
		        xhr.onreadystatechange = function () {
		            if (xhr.readyState === 4) {
		                resolve(JSON.parse(xhr.responseText));
		            }
		        }
		    }
		}
		 
	

Промисы

		getJSON('/data/books.json')
		    .then(function (books) {
		        books.forEach(function (book) {
		            console.log(book.title);
		        });
		    })
		    .catch(function (error) {
		         console.error(error);
		    });
		    .finally(function () {
		         console.log('Promise ends');
		    });
		 
	

Методы промисов

		// Вернёт промис в состоянии fulfilled («выполнено успешно»)
		Promise.resolve( ... );
		// Вернёт промис в состоянии rejected («выполнено с ошибкой»)
		Promise.reject( ... );
		// Выполнит все промисы "параллельно"
		Promise.all( [ ... ] );
		// Вернёт промис, выполнившийся раньше всех
		Promise.race( [ ... ] );
		 
	

Стрелочные функции

Стрелочные функции

		const hello = () => console.log('Hello, World!');
		const sqr = num => num * 2;
		[1, 2, 3, 4].map(sqr); // [1, 4, 9, 16]
		 
		const compare = (left, right) => {
		    if (left.length === right.length) {
		        return left.localeCompare(right);
		    }
		    return left.length - right.length;
		}
		 
	

Стрелочные функции

ES6-классы

ES6-классы

		class User {
		    constructor(login, password) {
		        this._login = login;
		        this._password = password;
		    }
		    hello() {
		        console.log('Hello, ' + this._login);
		    }
		}
		 
	

ES6-классы: cтатические методы

		class MathUtils {
		    static sqr(number) {
		        return number * number;
		    }
		    static abs(number) {
		        return number < 0 ? -number : number;
		    }
		}
		 
	

ES6-классы

		const user1 = new User('Alex', 'qwerty123');
		const user2 = new User('Jon', 'passw0rd');
		 
		user1.hello();       // Hello, Alex
		user2.hello();       // Hello, Jon
		 
		MathUtils.sqr(6);    // 36
		MathUtils.abs(-42);  // 42
		 
	

ES6-классы: геттеры и сеттеры

		class Shape {
		    constructor(width, height) {
		        this._width = width;
		        this._height = height;
		    }
		    get Square() { return this._width * this._height; }
		    set SideLength(value) {
		        this._width = this._height = value;
		    }
		}
		 
	

ES6-классы: геттеры и сеттеры

		const shape = new Shape(6, 12);
		console.log(shape.Square);   // 72
		 
		shape.SideLength = 7;
		console.log(shape.Square);   // 49
		 
	

ES6-классы: наследование

		class View {
		    constructor(el) {
		        this._el = el;
		    }
		    hide() {
		        this._el.hidden = true;
		    }
		}
		 
	

ES6-классы: наследование

		class LoginView extends View {
		    constructor() {
		        super(document.getElementById('login'));
		        this._form = this._el.querySelector('.login__form');
		    }
		    hide() {
		        super.hide();
		        this._form.clear();
		    }
		}
		 
	

ES6-модули

ES6-модули

		// экспортируем значения
		export const PI = 4;
		export function square(number) { return number * number; }
		export default class User {
		    constructor() { ... }
		}
		 
		const name = 'Jon Snow', years = 42;
		export { name, years as age };
		 
	

ES6-модули

		// импортируем значения
		import { PI, square } from '../module.js';
		import { name as login } from '../module.js';
		import UserClass from '../module.js';
		import * as Utils from '../module.js';
		import '../module.js';
		 
		// ре-экспорт
		export { PI, login as username } from '../module.js';
		export * from '../module.js';
		 
	

Работа с объектами

Объявление литералов

		// es6 features
		const login = 'Jon Snow', age = 42;
		const User = {login, age}; // {login: 'Jon Snow', age: 42}
		 
		const Obj = {
		    func: function () { ... },        // old way
		    func() { ... },                   // inline methods
		    get Arr() { return this.array; }, // object getters & setters
		    ['foo' + 'bar']: value,           // computed property names
		    array: [1, 2, 3, 4, 5,],          // trailing commas
		    prop: value,                      // trailing commas
		}
		 
	

Объявление функций

		// es8 features
		function UseFull (
		    param1 ,
		    param2 ,
		    param3 ,   // trailing commas
		) { return param1 + param2 + param3; }
		 
		UseFull (
		    42 ,
		    100500 ,
		    -200600 ,   // trailing commas
		); // -100058
		 
	

Дополнительные методы

		// проверка двух выражений на совпадение
		Object.is(value1, value2);
		 
		Object.is(1, 1);              // true
		Object.is(1, '1');            // false
		Object.is(false, false);      // true
		Object.is({a: 42}, {a: 42});  // false
		 
		Object.is(NaN, NaN);    // true   (NaN === NaN) === false
		Object.is(0, -0);       // false  (-0 === 0) === true
		 
	

Дополнительные методы

		 // копирование свойств
		Object.assign(target, source, source, source, ...);
		 
		const s1 = {a: 'Jon'}, s2 = {b: 42};
		const result = Object.assign({}, s1, s2);
		// result: {
		//     a: 'Jon',
		//     b: 42
		// }
		 
	

Дополнительные методы

		// Запаковывание объектов
		Object.seal(target);    // можно изменить значение имеющихся свойств,
		                        // но нельзя добавить или удалить их
		// Заморозка объектов
		Object.freeze(target);  // нельзя изменять значения имеющихся свойств,
		                        // удалять их, добавлять новые
		 
		Object.isFrozen(target);   Object.isSealed(target);
		 
	

Дополнительные методы

		// Перебор ключей, значений и свойств
		const user = {login: 'Jon Snow', age: 42};
		 
		Object.keys(user);     // ['login', 'age']
		Object.values(user);   // ['Jon Snow', 42]
		Object.entries(user);  // [['login', 'Jon Snow'], ['age', 42]]
		 
	

Новые коллекции

Map — хэш-таблица

		const map = new Map();
		map.set(key, value);   // добавить значение
		map.get(key);          // получить значение
		map.has(key);          // проверить наличие ключа
		map.delete(key);   map.clear();
		 
		map.size;              // размер Map
		map.forEach(callback); // перебор ключей, свойств, значений
		map.values();      map.keys();   map.entries();
		 
	

Set — набор значений
без повторения

		const set = new Set();
		set.add(value);          // добавить значение
		set.has(value);          // проверить наличие значения
		set.delete(value);   set.clear();
		 
		set.size;              // размер Set
		set.forEach(callback); // перебор ключей, свойств, значений
		set.values();      set.keys();   set.entries();
		 
	

Reflect

Reflect

Встроенный JavaScript объект, предоставляющий методы для перехвата взаимодействий с объектами и работы с рефлексией в JavaScript

		Reflect.apply(target, thisArgument, argumentsList)
		Reflect.construct(target, argumentsList)
		 
		Reflect.get(target, propertyKey)
		Reflect.has()
		 
		Reflect.getPrototypeOf(target)
		Reflect.setPrototypeOf(target, prototype)
		 
	

Работа с дескрипторами

		// Reflect.defineProperty(target, propertyKey, attributes)
		const object = {};
		Reflect.defineProperty(object, 'foo', {
		    enumerable: false,    // разрешает перечисление
		    writable: false,      // разрешает перезапись
		    configurable: false,  // разрешает изменение дескриптора
		    value: undefined      // значение свойства
		    get: undefined        // геттер
		    set: undefined        // сеттер
		}
		
	

Дополнительные нововведения

Оператор возведения в степень

		// hello python from es7
		console.log(3 ** 4);     // 81
		console.log(49 ** 0.5);  // 7
		 
		 
	

Новые возможности регулярок

Приехало в ECMAScript 2018

		// новый флаг s (dotAll)
		/foo.bar/.test('foo\nbar');      // false
		/foo.bar/s.test('foo\nbar');     // true
		 
		// именованные группы в RegExp
		let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
		let result = re.exec('2018-04-06');
		// result.groups.year === '2018';
		// result.groups.month === '04';
		// result.groups.day === '06';
		 
	

SharedArrayBuffer и Atomics

Ликбез про Web Workers

		// main
		const worker = new Worker(scriptUrl);           // создали воркер
		// const sharedWorker = new SharedWorker(scriptUrl);
		 
		worker.postMessage({hello: 'world'});           // отправили данные
		worker.onmessage = function(e) { e.data ... };  // обработчик onmessage
		 
	
		// worker
		self.onmessage = function(e) { e.data... };   // обработчик onmessage
		self.postMessage({hello: 'world'});           // отправили данные
		 
	

SharedArrayBuffer и Atomics

		const buffer = new ArrayBuffer(15 * 1024 * 1024);  // 15 MB RAM
		worker.postMessage(buffer);                        // клонирует данные
		 
		const shared = new SharedArrayBuffer(length);      // разделяемая память
		worker.postMessage(shared);                        // клонирует данные
		 
		Atomics.add(typedArray, pos, val);     // потокобезопасное сложение
		Atomics.sub(typedArray, pos, val);     // потокобезопасное вычитание
		Atomics.store(typedArray, pos, val);   // потокобезопасная запись
		Atomics.load(typedArray, pos);         // потокобезопасное чтение
		Atomics.wait(typedArray, pos, val[, timeout]);  // like as Linux futexes
		...
		 
	

Что-то поинтереснее

Деструктуризация

Деструктуризация массивов

		// деструктуризация
		const [name, family] = 'Jon Snow'.split(' ');
		console.log(name);    // Jon
		console.log(family);  // Snow
		 
		// пропуск элементов
		const [, , var3, var4] = [1, 2, 3, 4];
		const [num1, , num3] = [1, 2, 3, 4];
		 
	

Деструктуризация массивов

		// значения по умолчанию
		const [name, family = 'Black'] = ['Jon'];
		console.log(name);    // Jon
		console.log(family);  // Black
		 
		// swap переменных
		let title1 = 'Book 1', title2 = 'Book 2';
		([title1, title2] = [title2, title1]);   // title1 === 'Book 2'
		                                         // title2 === 'Book 1'
		 
	

Деструктуризация объектов

		const person = {login: 'Jon Snow', age: 42, alive: true};
		// деструктуризация
		const {login, age} = person;
		// значения по умолчанию
		const {login, age, password = 'qwerty123'} = person;
		// переименование свойств
		const {login: username, age} = person;    // username === 'Jon Snow'
		// комбинация
		const {login: username = 'Anonimous', age} = person;
		 
	

Деструктуризация объектов

		// вложенная деструктуризация
		const element = {tagName: 'DIV', size: {width: 300, height: 200}};
		const {
		    tagName: tag,
		    size: {width: w = 250, height: h = 250} = {},
		    color = 'red',
		    childs: [first, second, , last] = []
		} = element;
		console.log({tag, w, h, color, first, second, last});
		 
	

Деструктуризация параметров
функций

		function sruare({width: w, height: h = 100}) {
		    return w * h;
		}
		 
		square({width: 20, height: 50, color: 'red'});   // 1000
		square({width: 42});                             // 4200
		 
	

Деструктуризация

		const [posts, messages, user] = Promise.all([
		    fetch('/posts'),
		    fetch('/messages'),
		    fetch('/me')
		]);
		 
		const [xlarge, large, medium, small, xsmall] =  Promise.all(
		    ['xl', 'l', 'm', 's', 'xs']
		        .map(size => fetch(`/profile/${size}-logo.png`));
		);
		 
	

Деструктуризация

		function figure({width: w = 10, height: h = 10, length: l = 20}) {
		    return w * h * l;
		}
		 
		const width = 15, height = 25, length = 50;
		const args = {
		    width: width,
		    height: height,
		    length: length,
		};
		 
		volume(args);   // 18750
		 
	

Деструктуризация

		function figure({width: w = 10, height: h = 10, length: l = 20}) {
		    return w * h * l;
		}
		 
		const width = 15, height = 25, length = 50;
		const args = {
		    width,
		    height,
		    length,
		};
		 
		volume(args);   // 18750
		 
	

Деструктуризация

		function figure({width: w = 10, height: h = 10, length: l = 20}) {
		    return w * h * l;
		}
		 
		const width = 15, height = 25, length = 50;
		const args = { width, height, length };
		 
		volume(args);   // 18750
		 
	

Деструктуризация

		function figure({width: w = 10, height: h = 10, length: l = 20}) {
		    return w * h * l;
		}
		 
		const width = 15, height = 25, length = 50;
		volume({ width, height, length });   // 18750
		 
	

Деструктуризация

		function figure({width: w = 10, height: h = 10, length: l = 20}) {
		    return w * h * l;
		}
		 
		const width = 15, height = 25, length = 50;
		volume({ width, height, length });   // 18750
		volume({ height, width, length });   // 18750
		volume({ height, width });           // 7500
		volume({ });                         // 2000
		 
	

Оператор расширения и остатка

...

Оператор расширения aka
spread-оператор

		// расширение массивов
		const arr = ['a', 'b', 'c', 'd'];
		const arr2 = [1, 2, ...arr, 3];
		console.dir(arr2); // [1, 2, 'a', 'b', 'c', 'd', 3];
		 
	

Оператор остатка aka
rest-оператор

		// используется при деструктуризации
		const scoreboard = ['Jane', 'Jon', 'Alex', 'Max'];
		const [first, second, ...loosers] = scoreboard;
		 
		// first === 'Jane'
		// second === 'Jon'
		// loosers === ['Alex', 'Max']
		 
	

rest-/spread- оператор

		// может проитерироваться по элементам коллекции
		// и вернуть массив, состоящий из её элементов
		const letters = [...'abcde'];
		console.dir(letters);   // ['a', 'b', 'c', 'd', 'e'];
		 
		// panes будет настоящим массивом
		const panes = [...document.querySelectorAll('.pane')];
		 
	

rest-/spread- оператор

		// передача параметров в функцию
		const numbers = [1, 2, 42, 532, -3.14, -Infinity];
		const maximum = ... ?
		const minimum = ... ?
		 
		const maximum = Math.max.apply(null, numbers);   // 532
		const minimum = Math.min.apply(null, numbers);   // -Infinity
		 
	

rest-/spread- оператор

		// передача параметров в функцию
		const numbers = [1, 2, 42, 532, -3.14, -Infinity];
		const maximum = Math.max(...numbers);   // 532
		const minimum = Math.min(...numbers);   // -Infinity
		 
	

rest-параметры

		// сбор праметров функции
		function summ(...nums) {
		    // можно работать не с arguments, а с настоящим массивом nums
		    return nums.reduce((sum, current) => sum + current, 0);
		}
		 
		console.log(summ(1, 2, 3, 4, 5));    // 15
		 
	

rest-параметры

		// сбор праметров функции
		function split(coeff, ...nums) {
		    return nums.filter(num => num < coeff);
		}
		 
		console.log(split(3.14, 1, 2, 3, 4, 5));    // [1, 2, 3]
		 
	

Оператор spread для объектов

		const name = { first: 'Jon', last: 'Snow' };
		const address = {
		    city: 'Norilsk',
		    country: 'Russian Federation',
		    street: 'Leninskiy Prospect',
		};
		 
		const profile = {
		    age: 20,
		    ...name,
		    ...address,
		}
		 
	

Оператор rest при
деструктуризации объектов

		const {login, password, ...profile} = {
		     login: 'Jon',
		     password: 'qwerty123',
		     age: 42,
		     city: 'Norilsk',
		     birthdate: '30.03.2000'
		};
		 
		console.log(profile);
		// { age: 42, city: 'Norilsk', birthdate: '30.03.2000' }
		 
	

Symbol

Типы данных в JavaScript

  1. Number
  2. Boolean
  3. String
  4. Object
  5. null
  6. undefined
  7. Symbol

Типы данных в JavaScript

Number
Boolean
String
Object
null
undefined
Symbol
Reference
List
Completion
Property Descriptor
Lexical Environment
Environment Record
Data Block
...

Создание символов

		// без new
		const symbol1 = Symbol();
		const symbol2 = Symbol('label');
		const symbol3 = Symbol('label');
		 
		console.log(typeof symbol1);       // symbol
		console.log(symbol2 == symbol3);   // false
		console.log(symbol2 === symbol3);  // false
		 
		console.log(symbol1);              // 'Symbol()'
		console.log(symbol2);              // 'Symbol(label)'
		 
	

Глобальные символы

		// берутся из реестра глобальных символов
		// если символа нет в реестре - создаётся новый символ
		const symbol1 = Symbol.for('label');
		const symbol2 = Symbol.for('label');
		console.dir(symbol1 == symbol2);       // true
		 
		const symbol3 = Symbol('label');
		console.dir(Symbol.keyFor(symbol1));   // 'label'
		console.dir(Symbol.keyFor(symbol3));   // undefined
		 
	

Символы в качестве
имён новых свойств

		const User = {
		    name: 'Jon Snow',
		    [Symbol.for('hello')]() {
		        console.log(`Hello, ${this.name}!`);
		    }
		}
		 
		User[Symbol.for('hello')]();   // 'Hello, Jon Snow!'
		 
		const helloSymbol = Symbol.for('hello');
		User[helloSymbol]();           // 'Hello, Jon Snow!'
		 
	

Well-known symbols

		Symbol.hasInstance
		Symbol.iterator
		Symbol.replace
		Symbol.search
		Symbol.toPrimitive
		Symbol.toStringTag
		...
		 
	

Итераторы

Итераторы

Итераторы — расширяющая понятие «массив» концепция, которая пронизывает современный стандарт JavaScript сверху донизу. Итерируемые или, иными словами, «перебираемые» объекты — это те, содержимое которых можно перебрать в цикле

Итерируемые объекты

Итераторы

В общем смысле, итератор — это объект, предоставляющий метод next(), который возвращает следующий элемент определённой последовательности. Для перебора итераторов существует специальный цикл for ... of

Перебор итераторов

		const numbers = [2, 3, 5, 7, 11, 13];
		 
		for (const prime of numbers) {
		    console.log(`Prime number ${prime}!`);
		}
		 
	

Связь со спредами

		// оператор расширения итерируется по итератору
		// и возвращает массив из элементов итератора
		 
		function arrayUniq()
		    const source = [...arguments];
		    // Set.prototype.values() возвращает итератор по элементам коллекции
		    return [...new Set(source).values()];
		}
		 
	

Symbol.iterator

		const iterable = [1, 2, 3, 4];
		const iterator = iterable[Symbol.iterator]();
		 
		console.log(iterator.next());    { value: 1, done: false }
		console.log(iterator.next());    { value: 2, done: false }
		console.log(iterator.next());    { value: 3, done: false }
		console.log(iterator.next());    { value: 4, done: false }
		console.log(iterator.next());    { value: undefined, done: true }
		console.log(iterator.next());    { value: undefined, done: true }
		 
	

Кастомные итераторы

		const iterable = {
		    current: 0,
		    [Symbol.iterator]() { return this; },
		    next() {
		        if (this.current) {
		            return { value: this.current--, done: false };
		        }
		        return { value: undefined, done: true }
		    }
		}
		 
	

Кастомные итераторы

		iterable.current = 7;
		const elements = [...iterable]; // [ 7, 6, 5, 4, 3, 2, 1 ]
		 
		iterable.current = 5;
		let summ = 0;
		for (const n of iterable) {
		    summ += n;
		}
		console.log(summ);    // 15
		 
	

Генераторы

Объявление функции-генератора

		function * generator() {
		    yield 1;
		    yield 2;
		    return 3;
		}
		 
		const generator = function * () {
		    yield 1; yield 2; return 3;
		};
		 
	

Объявление функции-генератора

		const gen = generator();
		 
		console.log(gen.next());   // { value: 1, done: false }
		console.log(gen.next());   // { value: 2, done: false }
		console.log(gen.next());   // { value: 3, done: true }
		console.log(gen.next());   // { value: undefined, done: true }
		console.log(gen.next());   // { value: undefined, done: true }
		 
		const gen2 = generator();  // console.log([...gen2])
		 
	

"Бесконечные" генераторы

		function * fibonacci() {
		    let prev = 1, curr = 0;
		    while (true) {
		        let now = prev + curr;
		        prev = curr; curr = now;
		        yield now;
		    }
		}
		 
	

Взаимодействие с генераторами

		function * rand(length) {
		    while (length--) {
		        yield Math.random();
		    }
		}
		 
		console.log([...rand(3)]);   // [ 0.216, 0.39, 0.555 ]
		console.log([...rand(5)]);   // [ 0.782, 0.806, 0.294, 0.228, 0.755 ]
		 
	

Взаимодействие с генераторами

		function * simple() {
		    let num = yield 'line 2';
		    return num;
		}
		const gen = simple();
		console.log(gen.next());     // { value: 'line 2', done: false }
		console.log(gen.next(42));   // { value: 42, done: true }
		console.log(gen.next());     // { value: undefined, done: true }
		 
	

Взаимодействие с генераторами

		function * wow() {
		    let num = 0, sum = 0;
		    while (num = yield sum) {
		        sum += num;
		    }
		    return sum;
		}
		 
	

Взаимодействие с генераторами

		const gen = wow();
		gen.next();      // { value: 0, done: false }
		gen.next(1);     // { value: 1, done: false }
		gen.next(2);     // { value: 3, done: false }
		gen.next(3);     // { value: 6, done: false }
		gen.next(4);     // { value: 10, done: false }
		gen.next(5);     // { value: 15, done: false }
		 
		gen.next(0);     // { value: 15, done: true }
		 
	

Взаимодействие с генераторами

		const gen = wow();
		gen.next();      // { value: 0, done: false }
		gen.next(1);     // { value: 1, done: false }
		gen.throw(new Error('kek'));  // Error: kek
		 
		gen.next(0);     // до этого места выполнение не дойдёт
		 
	

Композиция генераторов

		function * twicer(element) {
		    yield element; yield element;
		}
		function * test() {
		    yield * twicer(42);
		    yield * twicer('test');
		}
		 
		console.log([...test()]);    // [ 42, 42, 'test', 'test' ]
		 
	

Асинхронные функции (async/await)

async/await

Ключевое слово async позволяет объявлять асинхронные функции, которые возвращают промис. Внутри таких функций возможна "синхронная" работа с промисами с помощью ключевого слова await

Код, использующий async/await-функции очень похож на код, написанный на генераторах с использованием библиотеки co

Объявление функций

		async function good() {
		    return 42;
		}
		 
		good()
		    .then(res => console.log('Good: ', res);
		 
	

Объявление функций

		async function bad() {
		    throw new Error('kek');
		}
		 
		good()
		    .then(res => console.log('Good: ', res));
		    .catch(err => console.error(err));
		 
	

Объявление функций

		async function luck(num) {
		    if (Math.random() < 0.5) {
		        return num * 2;
		    }
		    throw new Error('kek');
		}
		luck(21)
		    .then(res => console.log('Good: ', res));   // may be 42
		    .catch(err => console.error(err));          // or may be an Error
		 
	

Использование

		async function loadJSON(url) {
		    const response = await fetch(url, {method: 'GET'});
		    if (response.statusCode !== 200) {
		        throw new Error(`Can not load json ${url}`);
		    }
		    const json = await response.json();
		    return json;
		}
		 
	

Использование

		async function load(query) {
		    const list = await fetchList(query);
		    const result = await Promise.all(list.map(item => loadItem(item)));
		    return result;
		}
		 
	
		async function load(query) {
		    const list = await fetchList(query);
		    return Promise.all(list.map(item => loadItem(item)));
		}
		 
	

Асинхронные итераторы

		function readFiles(names) {
		    return names.map(
		        filename => promiseRead(filename)
		    )
		}
		 
		for (const pRead of readFiles([...])) {
		    const source = await pRead;
		    // logic ...
		}
		 
	

Новый цикл for-await-of

		for await (const source of readFiles([...])) {
		    console.log(source)
		    // logic ...
		}
		 
	

Асинхронные генераторы

Незаменимо, когда заранее не известно количество итерируемых элементов

		async function* readLines(path) {
		    let file = await fileOpen(path);
		    try {
		        while (!file.EOF) {
		            yield await file.readLine();
		        }
		    } finally {
		        await file.close();
		    }
		}
		 
	

Асинхронные генераторы

		for await (const line of readLines(filePath)) {
		    console.log(line)
		    // logic ...
		}
		 
	

Proxy

Proxy

Объест Proxy (Прокси) — особый объект, смысл которого — перехватывать обращения к другому объекту и, при необходимости, модифицировать их

		// создание Proxy
		const proxy = new Proxy(target, handler);
		 
		// target - объект, обращения к которому надо перехватывать
		// handler - объект с функциями-перехватчиками для операций к target
		 
	

Создание Proxy

		const user = {};
		const proxy = new Proxy(user, {
		    get (target, property, receiver) {
		        console.log(`Чтение ${property}`);
		        return target[property];
		    },
		    set (target, property, value, receiver) {
		        console.log(`Запись ${property} = ${value}`);
		        target[property] = value;
		        return true;
		    },
		});
		 
	

Использование Proxy

		proxy.name = 'Jon Snow';               // Запись name = Jon Snow
		proxy.age = 22;                         // Запись age = 22
		proxy['long property'] = 'qux';         // Запись long property = qux
		 
		const name = proxy.name;                // Чтение name
		const age = proxy.age;                  // Чтение age
		const long = proxy['long property'];    // Чтение long property
		 
	

Конфигурация Proxy

		const handler = {
		    get (target, name, receiver);       // получение свойств
		    set (target, name, val, receiver);  // установка свойства
		    apply (target, thisValue, args);    // вызовы функции
		    construct (target, args);           // вызовы конструктора с new
		    has (target, name);                 // оператор in
		    defineProperty (target, property, descriptor);
		                                        // метод Object.defineProperty()
		    deleteProperty (target, property);  // оператор delete
		    ...
		};
		 
	

Применение Proxy

		const original = {};
		const magic = wrapWithProxy(original);
		 
		magic.data.elements[0].attributes.color = 'black';
		magic.country.map.shops = [ ... ];
		 
	

Применение Proxy: jewell

		import { jewellPrototype } from 'jewell';
		jewellPrototype(Array);
		const diamonds = [...];
		 
		// Get price: [2k, 100k, 300k]
		diamonds.map(diamond => diamond.price); // Traditional
		diamonds.map.price; // 💎
		 
		// Buy all pink diamonds
		diamonds
		    .filter(diamond => diamond.pink)
		    .forEach(diamond => diamond.buy()); // Traditional
		diamonds.filter.pink.forEach.buy(); // 💎
		 
	

Перерыв

Что с поддержкой?

Поддержка версий ECMAScript
https://kangax.github.io/

Возможности браузера
http://caniuse.com/

Что делать?

Полифиллы

Полифилл — это библиотека, которая добавляет в старые браузеры поддержку возможностей, которые в современных браузерах являются встроенными

			if (!Object.is) {
			    Object.is = function(x, y) {
			        if (x === y) { return x !== 0 || 1 / x === 1 / y; }
			        else { return x !== x && y !== y; }
			    }
			}
			 
		

Транспайлинг

Транспайлинг — это конвертация кода программы, написанной на одном языке программирования в другой язык программирования

			// before
			const f = num => `${num} в квадрате это ${num ** 2}`;
			 
			// after
			var f = function (num) {
			    return num + ' в квадрате это ' + Math.pow(num, 2);
			};
			 
		

Babel (babeljs.io)

Babel — многофункциональный транспайлер, позволяет транспиллировать ES5, ES6, ES2016, ES2017, ES2018, ES.Next, JSX и Flow

Babel REPL — бабель-онлайн

Babel — использование

			# устанавливаем модуль
			$ npm install -D @babel/core @babel/cli @babel/preset-env
			 
			# файл с конфигурацией
			$ nano .babelrc
			{
			    "presets": [[
			        "@babel/env",
			        { targets: {edge: "15"}}
			    ]]
			}
			 
			# запускаем
			$ babel modern.js --watch --out-file compatible.js
			 
		

Как работает Babel?

Текущие версии JavaScript’а

ES.Next

ES.Next — так временно называют совокупность новых возможностей языка, которые могут войти в следующую версию спецификации. Фичи из ES.Next правильнее называть “предложения” (proposals) , потому что они всё ещё находятся на стадии обсуждения

Процесс TC39

TC39

TC39 (технический комитет 39) — занимается развитием JavaScript. Его членами являются компании (помимо прочих, все основные производители браузеров). TC39 регулярно собирается, на встречах присутствуют участники, представляющие интересы компаний, и приглашенные эксперты

Процесс TC39

Процесс TC39 — алгоритм внесения изменений в спецификацию ECMAScript. Каждое предложение по добавлению новой возможности в ECMAScript в процессе созревания проходит ряд этапов

Будущее JavaScript

Диалекты JavaScript

Транспилляция из ES.Next

		import { flying } from 'abilities';
		class Creature {
			constructor({ name, ...rest}) {
				console.log(`Привет, ${name}, твои свойства:`, rest);
			}
		}
		@flying
		class Dragon extends Creature {
			static haveTail = true;
			legs = 4;
			async *eat(...staff) {
				// Eat something...
			}
		}
	

Dart programming language

		import 'dart:async';
		import 'dart:math' show Random;
		 
		Stream<double> computePi({int batch: 1000000}) async* { ... }
		 
		main() async {
		    print('Compute π using the Monte Carlo method.');
		    await for (var estimate in computePi()) {
		        print('π ≅ $estimate');
		    }
		}
		 
	

CoffeeScript

		class Human
		    constructor : (@name) ->
		 
		class Baby extends Human
		    say    : (msg) -> alert "#{@name} говорит '#{msg}'"
		    saymsg = (msg) -> alert msg
		    @echo  = (msg) -> console.log msg
		 
		matt = new Baby("Матвей")
		matt.sayHi()
		 
	

Больше кофечки богу
кофечки!

		if happy and knowsIt
		    lyrics = while num -= 1
		        "#{num} little monkeys, jumping on the bed"
		else
		    date = if friday then sue else jill
		 
		for filename in list
		    do (filename) ->
		        fs.readFile filename, (err, contents) ->
		            compile filename, contents.toString()
		 
	

ClojureScript

		(ns hello-world.core
		  (:require [cljs.nodejs :as nodejs]))
		 
		(nodejs/enable-util-print!)
		 
		(defn -main [& args]
		  (println "Hello world!"))
		 
		(set! *main-cli-fn* -main)
		 
	

Elm

		import Html exposing (text)
		 
		main =
		  text (toString (zip ["Tom", "Sue", "Bob"] [45, 31, 26]))
		 
		zip : List a -> List b -> List (a,b)
		zip xs ys =
		  case (xs, ys) of
			( x :: xBack, y :: yBack ) ->
				(x,y) :: zip xBack yBack
		 
			(_, _) ->
				[]
		

TypeScript

		interface Person {
		    name: string;
		    age: number;
		}
		function meet(person: Person) {
		    return `Привет, я ${person.name}, мне ${parson.age}`;
		}
		const user = { name: "Jane", age: 21 };
		console.log(meet(user));
		 
	

TypeScript

TypeScript

TypeScript — язык программирования, представленный Microsoft в 2012 году. TypeScript является обратно совместимым с JavaScript и компилируется в последний. TypeScript отличается от JavaScript возможностью явного статического назначения типов, а также поддержкой подключения модулей

Разработчиком языка TypeScript является Андерс Хейлсберг (англ. Anders Hejlsberg), создавший ранее Turbo Pascal, Delphi и C#.

TypeScript Deep Dive — крутая книга по TypeScript

Преимущества TypeScript

Переписываем проект на TS

  1. Переименовываем *.js в *.ts
  2. ???????
  3. PROFIT

Установка и использование

		# установка компилятора
		$ npm install -g typescript
		 
		# компиляция файла
		$ tsc helloworld.ts
		 
		# утилита позволяет компилировать и сразу запускать .ts файлы
		$ npm install -g ts-node
		$ ts-node script.ts
		 
	

Файл tsconfig.json

		{
		    "compilerOptions": {
		        "outDir": "cache/",
		        "target": "es2016",
		        "declaration": false,
		        "module": "commonjs",
		        "strictNullChecks": true,
		        "sourceMap": true
		        ...
		     }
		}
		 
	

Аннотации типов

		const valid: boolean = true;
		const count: number = 42;
		const man: string = 'Jon Snow';
		 
		console.log(man * 2);
		// Error: The left-hand side of an arithmetic
		// operation must be of type 'any', 'number' or an enum type
		 
	

Type Inference

Вывод типов (англ. type inference) — в программировании возможность компилятора самому логически вывести тип значения у выражения.

		const valid = true;
		const count = 42;
		const man = 'Jon Snow';
		 
		console.log(man * 2);
		// Error: The left-hand side of an arithmetic
		// operation must be of type 'any', 'number' or an enum type
		 
	

Аннотации типов

		const valid = true;
		const count = 42;
		const man: any = 'Jon Snow';
		 
		console.log(man * 2); //NaN
		// Зато нет TS ошибок!
		// Profit!
		 
	

Аннотации типов

		const valid = true;
		const count = 42;
		const name = 'Jon Snow';
		 
		const values: number[] = [1, 2, 3, 4, 5];
		const tuple: [string, number] = ['Mean of life', 42];
		 
		enum Color {Red, Green, Blue};
		const c: Color = Color.Green;
		 
	

Аннотации типов

		let some: any = true; some = 42;
		some = 'maybe a string instead'; // типы не проверяются
		// приведение типов (“trust me, I know what I’m doing”)
		let length: number = (<string>some).length;
		length = (some as string).length;
		 
		let unusable: void = undefined;
		let u: undefined = undefined;
		let n: null = null;
		 
	

Функции в TypeScript

		function sum(x: number, y: number): number {
		    return x + y;
		}
		 
		const many: number = sum(40, 2);
		 
		const gcd = (a: number, b: number): number =>
		    (b === 0) ? a : gcd(b, a % b);
		 
		console.log(gcd(48, 30)); // 6
		 
	

Функции в TypeScript

		function sum(x: number, y?: number): number {
		    if (y) {
		        return x + y;
		    } else {
		        return x;
		    }
		}
		 
		console.log(sum(34, 8));  // 42
		console.log(sum(42));     // OK! - 42
		 
	

Функции в TypeScript

		function sum(x: number, y: number = 42): number {
		    return x + y;
		}
		 
		console.log(sum(34, 8));  // 42
		console.log(sum(42));     // OK! - 84
		 
	

Функции в TypeScript

		function sum(...numbers: number[]): number {
		    return numbers.reduce((sum: number, current: number): number => {
		        sum += current; return sum;
		    }, 0);
		}
		 
		console.log(sum(1, 2, 3, 4, 5));     // 15
		console.log(sum(42, 0, -10, 5, 5));  // 42
		 
	

Функции в TypeScript

		function square(num: number): number;
		function square(num: string): number;
		function square(num: any): number {
		    if (typeof num === 'string') {
		        return parseInt(num, 10) * parseInt(num, 10);
		    } else {
		        return num * num;
		    }
		}
		 
	

Функции в TypeScript

		function square(num: string | number): number {
		    if (typeof num === 'string') {
		        return parseInt(num, 10) * parseInt(num, 10);
		    } else {
		        return num * num;
		    }
		}
		 
	

Интерфейсы

		interface Figure {
		    width: number;
		    readonly height: number;
		}
		 
		const square: Figure = {width: 42, height: 42};
		square.width = 15;    // OK
		square.height = 15;   // Cannot assign to read-only property
		 
	

Интерфейсы

		interface Figure {
		    width: number;
		    height: number;
		}
		interface Square extends Figure {
		    square: () => number;
		}
		const sq = {width: 15, height: 20, 
		    square() { return this.width * this.height; } };
		sq.square();    // 300
		 
	

Классы в TypeScript

		abstract class Class1 {
		    abstract func1(): void;  // необходимо определить в наследниках
		}
		class Class2 extends Class1 {
		    static readonly field3: string = 'hello';
		    protected name: string;
		    private field1: number;
		    constructor() { super(); }
		    public func1(): void { ... }
		}
		 
	

Классы в TypeScript

		interface Squarable {
			calcSomething(): number;
		}
		 
		class Square implements Squarable {
			width: number;
			height: number;
		 
			// Error: Class 'Square' incorrectly implements interface 'Squarable'.
			// Property 'calcSomething' is missing in type 'Square'.
		}

	

Generics

		class Queue<T> {
		    private data = [];
		    push = (item: T) => this.data.push(item);
		    pop = (): T => this.data.shift();
		}
		 
		const queue = new Queue<number>();
		queue.push(0);    // OK
		queue.push('1');  // Error: cannot push a string
		 
	

Generics

		function makeKeyValue<K, V>(key: K, value: V): { key: K; value: V } {
		    return {key, value};
		}
		 
		const pair = makeKeyValue('days', ['ПН', 'ВТ']);
		pair.value.push('СР', 'ЧТ', 'ПТ', 'СБ', 'ВС');  // OK
		pair.value.push(42);   // Error: cannot push a number
		 
	

Декораторы свойств и методов

		class Utils {
		    @memoize
		    static fibonacci (n: number): number {
		        return n < 2 ? 1 : Utils.fibonacci(n - 1) + Utils.fibonacci(n - 2)
		    }
		}
		console.time('count');
		console.log(Utils.fibonacci(50));
		console.timeEnd('count');     // оооочень долго
		 
	

Декораторы свойств и методов

		function memoize (target, key, descriptor) {
		    const originalMethod = descriptor.value;
		    const cash = {};
		    descriptor.value = function (n: number): number {
		        return cash[n] ? cash[n] : cash[n] = originalMethod(n);
		    }
		}
		console.log(Utils.fibonacci(1000));   // 7.0330367711422765e+208
		console.timeEnd('count');             // count: 5.668ms
		 
	

Как "типизировать" js-код?

TypeScript Declaration Files

TypeScript Declaration Files (.d.ts) — служат для описания интерфейсов, экспортируемых классов и методов для модулей, написанных на обычном JavaScript

.d.ts файлы

		interface JQueryStatic {
		    ajax(settings: JQueryAjaxSettings): JQueryXHR;
		    (element: Element): JQuery;
		    (html: string, ownerDocument?: Document): JQuery;
		    (): JQuery;
		}
		 
		declare var $: JQueryStatic;
		declare module 'jquery' {
		    export = $;
		}
		 
	

Типизация с помощью JSDoc

		// Пример типизирования функции с помощью JSDoc + TypeScript
		/**
		 * @param p0 {string} - Строковый аргумент объявленный на манер TS
		 * @param {string}  p1 - Строковый аргумент
		 * @param {string=} p2 - Опциональный аргумент
		 * @param {string} [p3] - Другой опциональный аргумент
		 * @param {string} [p4="test"] - Аргумент со значением по-умолчанию
		 * @return {string} Возвращает строку
		 */
		function fn3(p0, p1, p2, p3, p4){
		  // TODO
		}
		

Flow от Facebook

        // @flow
        function concat(a /*: string */, b /*: string */) {
            return a + b;
        }
         
        concat('A', 'B'); // Works!
        concat(1, 2); // Error!
         
	

Высший пилотаж

WebAssembly

WebAssembly

Во время создания WebAssembly решалась следующая задача: быстро исполнять код в браузере

WebAssembly (wasm) — эффективный низкоуровневый байт-код, предназначенный для исполнения в браузере. WebAssemblу представляет собой переносимое абстрактное синтаксическое дерево, обеспечивающее как более быстрый парсинг, так и более быстрое выполнение кода, чем JavaScript — developers.google.com

WebAssembly

WebAssembly — это не полная замена JS, а лишь технология, позволяющая писать критичные к ресурсам модули и компилировать их в переносимый байт-код с линейной моделью памяти и статической типизацией

Применения: редактирование изображений/видео/музыки, криптография, математические вычисления, игры...

Что же такое WebAssembly?

WebAssembly

Необходим статически типизированный код

		// исходник на C
		int fib(int n) {
		    if (n == 0) { return 0; } else {
		        if ((n == -1) || (n == 1)) { return 1; } else  {
		            if (n > 0) { return fib(n - 1) + fib(n - 2); }
		            else { return fib(n + 2) - fib(n + 1); }
		        }
		    }
		}
		 
	

WebAssembly

		// текстовое представление WAST
		(module
		    (table 0 anyfunc)
		    (memory $0 1)
		    (data (i32.const 12) "\01\00\00\00\00\00\00\00\01\00\00\00")
		    (export "memory" (memory $0))
		    (export "fib" (func $fib))
		    (func $fib (param $0 i32) (result i32)
		        (local $1 i32)
		        (block $label$0
		            (br_if $label$0
		                (i32.ge_u
		                // ...
		 
	

Как читать WAST

WebAssembly

		// скомпилированный байт-код wasm
		const wasmCode = new Uint8Array(
		[0,97,115,109,1,0,0,0,1,134,128,128,128,0,1,96,1,127,1,127,3,130,128,
		128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,
		1,6,129,128,128,128,0,0,7,144,128,128,128,0,2,6,109,101,109,111,114,
		121,2,0,3,102,105,98,0,0,10,203,128,128,128,0,1,197,128,128,128,0,1,
		1,127,2,64,32,0,65,1,106,34,1,65,3,79,13,0,32,1,65,2,116,65,12,106,
		40,2,0,15,11,2,64,32,0,65,1,72,13,0,32,0,65,127,106,16,0,32,0,65,
		126,106,16,0,106,15,11,32,0,65,2,106,16,0,32,1,16,0,107,11,11,146,
		128,128,128,0,1,0,65,12,11,12,1,0,0,0,0,0,0,0,1,0,0,0]
		);
		 
	

WebAssembly

		// запускаем wasm-модуль
		// создаем типизированный массив
		const wasmCode = new Uint8Array([...]);
		// компилируем WASM
		const wasmModule = new WebAssembly.Module(wasmCode);
		// получаем инстанс готовый к работе
		const wasmInstance = new WebAssembly.Instance(wasmModule, []);
		 
		console.log(wasmInstance.exports.fib(10));
		 
	

Демо: — Танки, Quake.

Благодаря WebAssembly можно писать Frontend на Go

Благодаря WebAssembly можно писать Frontend на Go

Благодаря WebAssembly можно писать Frontend на TypeScript!!!

AssemblyScript

AssemblyScript — компилятор TypesScript в WebAssembly. Т.к. TypesScript статически типезированный, то мы можем его скомпилить в WebAssembly. Можно использовать специальные типы

Го тусить на локалхост

Ваши вопросы?

Полезные ссылки

Полезные ссылки

Всем спасибо!