Технопарк, весна, 2019 г.
let/const
, шаблонные строки, Promise, стрелочные функцииSharedArrayBuffer
и Atomics
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;
}
this
и своего
arguments
: берут их их
LexicalEnvironment
new
class User {
constructor(login, password) {
this._login = login;
this._password = password;
}
hello() {
console.log('Hello, ' + this._login);
}
}
class MathUtils {
static sqr(number) {
return number * number;
}
static abs(number) {
return number < 0 ? -number : number;
}
}
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
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;
}
}
const shape = new Shape(6, 12);
console.log(shape.Square); // 72
shape.SideLength = 7;
console.log(shape.Square); // 49
class View {
constructor(el) {
this._el = el;
}
hide() {
this._el.hidden = true;
}
}
class LoginView extends View {
constructor() {
super(document.getElementById('login'));
this._form = this._el.querySelector('.login__form');
}
hide() {
super.hide();
this._form.clear();
}
}
// экспортируем значения
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 };
// импортируем значения
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 wayfunc() { ... }, // 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
Встроенный 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
// 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
// расширение массивов
const arr = ['a', 'b', 'c', 'd'];
const arr2 = [1, 2, ...arr, 3];
console.dir(arr2); // [1, 2, 'a', 'b', 'c', 'd', 3];
// используется при деструктуризации
const scoreboard = ['Jane', 'Jon', 'Alex', 'Max'];
const [first, second, ...loosers] = scoreboard;
// first === 'Jane'
// second === 'Jon'
// loosers === ['Alex', 'Max']
// может проитерироваться по элементам коллекции
// и вернуть массив, состоящий из её элементов
const letters = [...'abcde'];
console.dir(letters); // ['a', 'b', 'c', 'd', 'e'];
// panes будет настоящим массивом
const panes = [...document.querySelectorAll('.pane')];
// передача параметров в функцию
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
// передача параметров в функцию
const numbers = [1, 2, 42, 532, -3.14, -Infinity];
const maximum = Math.max(...numbers); // 532
const minimum = Math.min(...numbers); // -Infinity
// сбор праметров функции
function summ(...nums) {
// можно работать не с arguments, а с настоящим массивом nums
return nums.reduce((sum, current) => sum + current, 0);
}
console.log(summ(1, 2, 3, 4, 5)); // 15
// сбор праметров функции
function split(coeff, ...nums) {
return nums.filter(num => num < coeff);
}
console.log(split(3.14, 1, 2, 3, 4, 5)); // [1, 2, 3]
const name = { first: 'Jon', last: 'Snow' };
const address = {
city: 'Norilsk',
country: 'Russian Federation',
street: 'Leninskiy Prospect',
};
const profile = {
age: 20,
...name,
...address,
}
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' }
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!'
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
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
: jewellimport { 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(); // 💎
Полифилл — это библиотека, которая добавляет в старые браузеры поддержку возможностей, которые в современных браузерах являются встроенными
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 — многофункциональный транспайлер, позволяет транспиллировать ES5, ES6, ES2016, ES2017, ES2018, ES.Next, JSX и Flow
Babel REPL — бабель-онлайн
# устанавливаем модуль
$ 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
ES.Next — так временно называют совокупность новых возможностей языка, которые могут войти в следующую версию спецификации. Фичи из ES.Next правильнее называть “предложения” (proposals) , потому что они всё ещё находятся на стадии обсуждения
TC39 (технический комитет 39) — занимается развитием JavaScript. Его членами являются компании (помимо прочих, все основные производители браузеров). TC39 регулярно собирается, на встречах присутствуют участники, представляющие интересы компаний, и приглашенные эксперты
Процесс TC39 — алгоритм внесения изменений в спецификацию ECMAScript. Каждое предложение по добавлению новой возможности в ECMAScript в процессе созревания проходит ряд этапов
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...
}
}
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');
}
}
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()
(ns hello-world.core
(:require [cljs.nodejs :as nodejs]))
(nodejs/enable-util-print!)
(defn -main [& args]
(println "Hello world!"))
(set! *main-cli-fn* -main)
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
(_, _) ->
[]
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 — язык программирования, представленный Microsoft в 2012 году. TypeScript является обратно совместимым с JavaScript и компилируется в последний. TypeScript отличается от JavaScript возможностью явного статического назначения типов, а также поддержкой подключения модулей
Разработчиком языка TypeScript является Андерс Хейлсберг (англ. Anders Hejlsberg), создавший ранее Turbo Pascal, Delphi и C#.
TypeScript Deep Dive — крутая книга по TypeScript
*.js
в *.ts
# установка компилятора
$ 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) — в программировании возможность компилятора самому логически вывести тип значения у выражения.
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;
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
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
function sum(x: number, y: number = 42): number {
return x + y;
}
console.log(sum(34, 8)); // 42
console.log(sum(42)); // OK! - 84
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
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;
}
}
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
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 { ... }
}
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'.
}
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
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
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
function concat(a /*: string */, b /*: string */) {
return a + b;
}
concat('A', 'B'); // Works!
concat(1, 2); // Error!
Во время создания WebAssembly решалась следующая задача: быстро исполнять код в браузере
WebAssembly (wasm) — эффективный низкоуровневый байт-код, предназначенный для исполнения в браузере. WebAssemblу представляет собой переносимое абстрактное синтаксическое дерево, обеспечивающее как более быстрый парсинг, так и более быстрое выполнение кода, чем JavaScript — developers.google.com
WebAssembly — это не полная замена JS, а лишь технология, позволяющая писать критичные к ресурсам модули и компилировать их в переносимый байт-код с линейной моделью памяти и статической типизацией
Применения: редактирование изображений/видео/музыки, криптография, математические вычисления, игры...
// исходник на 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); }
}
}
}
// текстовое представление 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
// ...
// скомпилированный байт-код 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]
);
// запускаем wasm-модуль
// создаем типизированный массив
const wasmCode = new Uint8Array([...]);
// компилируем WASM
const wasmModule = new WebAssembly.Module(wasmCode);
// получаем инстанс готовый к работе
const wasmInstance = new WebAssembly.Instance(wasmModule, []);
console.log(wasmInstance.exports.fib(10));
AssemblyScript — компилятор TypesScript в WebAssembly. Т.к. TypesScript статически типезированный, то мы можем его скомпилить в WebAssembly. Можно использовать специальные типы