Изменения

ECMAScript и все-все-все

29 806 байтов добавлено, 11:49, 9 февраля 2017
м
Destructuring @@
<slideshow title="" style="nobookyellnobook" scaled="true" font="Roboto, Segoe UI, cursive" headingmark="@@" centermark="%%" incmark="++" footer="">; subfooter: <span style="font-family: Consolas; background: #f1dd56; padding: 5px; font-style: normal; color: black">ECMAScript и все-все-все</span>
</slideshow>
; PDF-версия: [[Media:ES6 and everyone.pdf|ES6 and everyone.pdf]]
== ECMAScript — ассемблер будущего,<br /> бэкенд, фронтенд и все-все-все @@ %% ==
== Скриптота vs типизация @@ %% ==
http[[File://3.bp.blogspot.com/_aWbNXEa3LP8/TFvQsof7IZI/AAAAAAAAA8U/NlSMwn-h2L0/s1600/script_kiddies_demotivatorScript_kiddies_demotivator.jpeg]]
=== Холивар!!! @@ ===
* Шутки в сторону: тема серьёзная
*: {{gray|''но кто на них пишет-то?''}}
=== Все хотят одного @@ ===
* Типизация — не необходимость, как раньше, а лишь один из способов проверки
*: тайпчекер (частично) уже даже в PHP (+ Hack)
=== Проверки — хорошо, но @@ %% ===
http[[Файл://demotivators.to/media/posters/327/414874_paranojyaParanoia.jpg]]
== Почему JS? @@ ==
* Мощное сообщество и развитие
* Удобный пакетный менеджер npm
* Есть тайпчекеры: TypeScript, Flow, Dart…
== Синтаксис @@ %% ==
{{gray|Сравнительный анализ}} === Perl @@ ===
<code-perl>
* Спецсимволы захватили мир
* Репутация «write-only»* Развитие , развитие умерло
=== PHP @@ ===
<code-php>
* Репутация г**нокода
=== Python @@ ===
<code-python>
* Пробелы меняют смысл?!!!!
=== Ruby @@ ===
<source lang="ruby">
* чем он лучше хотя бы питона?
=== Go @@ ===
<source lang="go">
* Что за смайлики <tt><nowiki>:= <- & *</nowiki></tt>? Где мои скобочки?
=== Erlang @@ ===
<source lang="erlang">
* Ой-ой-ой…
* Специфичен. Классов нет, есть процессы. Но сетевой, агада!
=== OCaml O_O @@ ===
<source lang="ocaml">
* ПОЛИЗ?!!!!
=== Lua @@ ===
<source lang="lua">
* Для полноты картины
=== JS @@ ===
<source lang="javascript">
</source>
=== Синтаксис JS @@ %% ===
Имхо вполне нейтральненько.
* 2008+: шустрота и современный период
=== Дремучий период @@ ===
1995—1997
* pre-AJAX: JSONP, невидимый iframe
=== Появление AJAX @@ ===
2004
* 2006 — jQuery
=== Современное развитие @@ ===
* 2008 — Google V8
На ЛОРе до сих пор шутят, что «Java тормозит», а что ж тогда JS?
* А ничего — и Java быстрая, и он быстрый. {{gray|Мамонты видать шутят}}
*: {{mag|из скриптоты node.js быстрее всех}}
* Но он же интерпретируемый?
*: НЕТ! '''Интерпретируемых''' языков уже вообще нет.
*: {{gray|Ну, разве что bash…}}
=== Вычислительный [https://blog.famzah.net/2016/02/09/cpp-vs-python-vs-perl-vs-php-performance-benchmark-2016/ бенчмарк] @@ ===
* https://github.com/famzah/langs-performance, время на 10 итераций (i386)* C++ (g++ 56.31.1) -O2 = 1{{green|0.124s92s}}* java8 = 1{{green|0.428s92s}}; java6 = {{green|1s}} {{gray|''(ручные массивы)''}}* PyPy (tracing jit) = 1.72s25s* Rust 1.12 = {{green|0.85s}}! Go 1.7 = 1.14s* '''node.js 4.6 = 2s1.35s'''* java7 + [https://blog.famzah.net/2016/09/10/cpp-vs-python-vs-php-vs-java-vs-others-performance-benchmark-2016-q3/#comment-21850 баг]; nodejs 0.10 = ~2.5s6s
* PHP 7 = 6.7s
* Ruby 2.3 = 14.3s, Python 3.5 = 19s17s, 2.7 = 21s20s* Perl = 25s24s* '''PHP 5.6 = 69s42.5s''' :))))) {{mag|ахаха, прекрати}}
==== Разница между 64 и 32 бит: i386 @@ ==== <plot>binwidth=0.5set yrange [0:13]set xrange [0:55]set ytics ('Rust 1.11' 1, 'C++' 2, 'Java 8' 3, 'PyPy' 4, 'Go' 5, 'node.js' 6, 'PHP 7' 7, 'Ruby 2.3' 8, 'Python 3.5' 9, 'Python 2.7' 10, 'Perl' 11, 'PHP 5.6' 12)set grid xticsset mxtics 1set style fill solid 1.0 noborderset boxwidth 0.7 relativeplot 'lang.dat' using 2:1:(0.0):2:(($1)-(binwidth/2.0)): (($1)+(binwidth/2.0)) with boxxyerrorbars title 'x = Быстрее PHP 5.6 в x раз'DATASET lang1.0 49.92.0 46.083.0 46.384.0 34.985.0 37.366.0 31.777.0 6.38.0 2.989.0 2.4910.0 2.1211.0 1.7612.0 1.00ENDDATASET</plot> ==== Разница между 64 и 32 бит: amd64 @@ ==== <plot>binwidth=0.5set yrange [0:13]set xrange [0:55]set ytics ('Rust 1.12' 1, 'C++' 2, 'Java 8' 3, 'PyPy' 4, 'Go' 5, 'node.js' 6, 'PHP 7' 7, 'Ruby 2.3' 8, 'Python 3.5' 9, 'Python 2.7' 10, 'Perl' 11, 'PHP 5.6' 12)set grid xticsset mxtics 1set style fill solid 1.0 noborderset boxwidth 0.7 relativeplot 'lang.dat' using 2:1:(0.0):2:(($1)-(binwidth/2.0)): (($1)+(binwidth/2.0)) with boxxyerrorbars title 'x = Быстрее PHP 5.6 в x раз'DATASET lang1.0 50.952.0 47.883.0 43.534.0 29.545.0 28.656.0 27.187.0 8.538.0 4.629.0 3.1810.0 2.5911.0 2.2212.0 1.00ENDDATASET</plot> === Почему V8 такой быстрый? @@ ===
Потому, что 4-слойный JIT!
* 4 слой — [https://webkit.org/blog/3362/introducing-the-webkit-ftl-jit/ FTL JIT] (<s>LLVM</s> [https://webkit.org/blog/5852/introducing-the-b3-jit-compiler/ B3])
[[File:FTL_JIT_performance.png]] [https://webkit.org/blog-files/ftl-jit/four_tier_performance.png]
=== Ключевые слова о том, как это всё устроено @@ ===
* Какой бывает JIT?
* OSR (On-Stack Replace)
=== Отступление: PyPy @@ %% ===
Трассирующий JIT-компилятор для Python, очень медленный
Рисует множество Мандельброта при сборке
http[[File://gyazo.com/2c32601f8747c5e93becc08270fa5127PyPy_mandel.png]]
=== LLVM @@ ===
LLVM (http://llvm.org), ранее «Low Level Virtual Machine»
* {{gray|На LLVM сделаны компилятор шейдеров Radeon и OpenCL}}
http[[File://www.aosabook.org/images/llvm/LLVMCompiler1.png]]
{{----}}
=== SSA @@ ===
Пример LLVM IR:
</pre>
=== V8 JIT @@ ===
* Первый раз функции запускаются в LLInt
*: {{green|&rarr; OSR в LLVM/B3 JIT}}
== Производительность = Мало? @@ === [http://asmjs.org/ asm.js] ([http://kripken.github.io/mloc_emscripten_talk/ презентация]) <source lang="javascript"> function strlen(ptr) { // calculate length of C string ptr = ptr|0; var curr = 0; curr = ptr; while (MEM8[curr]|0 != 0) { curr = (curr + 1)|0; } return (curr - ptr)|0; }</source> [[File:asmjs-bench.png|600px]] === Производительность — итого @@ ===
* Итого, V8 — «смешанный» JIT
* Чакра Наделлы тоже очень похожа на V8
* Постоянная битва :) https://arewefastyet.com/
* {{red|Но язык - ещё не всё!}} Ещё есть:
*: В браузере — {{green|DOM}} (медленный в старых Firefox при формально быстром JS)
*: На сервере — {{green|ввод/вывод}}
* &rArr; что же делать?..
=== Событийные машины! @@ %% ===
{{gray|("event loop")}}
=== Как вообще обрабатываются соединения клиентов? @@ ===
{{green|Все писали сетевой сервер на C? :)}}
*: ''«Проблема C10K» {{gray|(обработать 10000 соединений на 1 сервере)}}''
=== Событийная машина @@ ===
{{green|Неблокирующий ввод/вывод:}}
* '''всё в один поток''' {{gray|(или в несколько по числу ядер CPU)}}
=== Так работает Nginx… @@ %% ===
…и весь современный Web (фронтенды)
{{gray|(плюс zero-copy и раздача статики через sendfile())}}
=== А если пойти дальше? @@ ===
Кроме HTTP-запросов клиентов ещё есть:
{{mag|&rArr; Это и будет «событийная машина»}}
=== Событийные машины @@ ===
Почти везде опциональная сущность:
* Механизмы разные в разных ОС &rArr; '''libevent''', '''libev'''
=== Событийная машина node.js @@ ===
* Отличительная черта: '''вообще нет блокирующего I/O'''
*: 1M соединений переваривает даже успешнее — [https://blog.whatsapp.com/196/1-million-is-so-2011 на продакшне у WhatsApp]
*: и gc быстрый (кучи отдельные)
*: {{gray|но... но… erlang. свои минусы и [https://habrahabr.ru/post/183150/ начинает хотеться монад]}}
* [http://blog.virtan.com/2012/07/million-rps-battle.html Million RPS Battle]
=== Можно писать и на префорке @@ %% ===
Но…
== Обзор языка @@ %% ==
 
{{gray|и приколы}}
=== Типы данных @@ ===
* всё остальное — '''object'''
** хеш — объект дефолтного типа: <tt><nowiki>{ key: "value" }</nowiki></tt>
** массив — разновидность объекта (класс Array): <ttstyle="white-space: nowrap"><nowiki>[ "value1", 2, 3, "value3" ]</nowiki></tt>
** функции — тоже объекты, их typeof = '''function'''
* a == b (мягкое сравнение) и a === b (точное сравнение)
*: <tt><nowiki>"" == false, 0 == false, "1" == true, 1 == true, "5" == 5, null == undefined</nowiki></tt>
* regexp literals: <tt><nowiki>var re = /<html[^<>]*>/i;</nowiki></tt>
* break label, continue label
* Цикл по {{blue|ключам}}: <tt><nowiki>for (var i </nowiki>{{blue|in }}<nowiki> obj) {}</nowiki></tt>* '''Цикл по {{green|значениям (ES6)'''}}: <tt><nowiki>for (var i </nowiki>{{green|of }}<nowiki> obj) {}</nowiki></tt>
<source lang="javascript">
</source>
== <span style="font-family: Consolas; background: #f1dd56; padding: 5px10px 10px 0 10px; font-style: normal">ES6</span> @@ %% ==
Он же ES2015. А также ES2016, ES2017
http://es6-features.org/, https://babeljs.io/repl/
=== Фичи ES6 @@ ===
Уже сказал про:
* for .. of
== Функции, локальные в блоках @@ == (Block-scoped functions) <source lang="javascript">{ function foo () { return 1 } foo() === 1 { function foo () { return 2 } foo() === 2 } foo() === 1}</source> == Вычислимые свойства объектов @@ ===
ES6
</source>
=== Destructuring @@ ===
«Деструктивное присваивание»
<source lang="javascript">
var obj = { key: 'abc', value: [ 1, 2 ] };
var { key: k, value: [ a, b ], other: o = 3 } = obj; // 3 - значение по умолчанию
// можно в параметрах функции!
[ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ].reduce( function(obj, [ k, v ]) => { obj[k] = v; return obj; }), {});
</source>
=== Оператор распаковки @@ ===
<source lang="javascript">
function f(...args) {}
 
f(...iterable);
</source>
=== Упрощённые названия ключей @@ ===
ES6
</source>
== Итераторы для for .. of @@ == <source lang="javascript">let fibonacci = { [Symbol.iterator]() { let pre = 0, cur = 1; return { next() { [ pre, cur ] = [ cur, pre + cur ]; return { done: false, value: cur }; } }; }}for (let n of fibonacci) { if (n > 1000) break; console.log(n); }</source> == Классы (!) @@ ===
«Наконец-то», скажете вы.
</source>
== super = Наследование и property @@ ===
<source lang="javascript">
Нюанс: IE8+, так как при трансляции в ES5 требуют Object.defineProperty().
=== Генераторы @@ === Функции, из которых можно выйти и вернуться. {{mag|Пожалуй, самая крутая из всех доработок!}} <source lang="javascript">function* a(){ try { var v = yield 1; if (v < 0) yield 2; else yield 3; } catch (e) { yield "error"; } return "final";} var b = a(); // объект GeneratorFunction. функция ещё не начала выполнятьсяb.next(); // вернёт { value: 1, done: false }b.next(-5); // передаст -5 внутрь генератора. и вернёт { value: 2, done: false }b.throw(new Error("x")); // передаст исключение внутрь генератора. и вернёт { value: "error", done: false}b.next(); // вернёт { value: "final", done: true }</source> ==== Чем генераторы круты? @@ ==== {{mag|Они дают убрать лестницу колбэков!}} Пример лестницы (node-postgres): <source lang="javascript">const pg = require('pg');function makeQueries(callback){ var client = new pg.Client(); client.connect(function(err) { if (err) throw err; client.query('SELECT $1::text as name', ['brianc'], function (err, result) { if (err) throw err; client.end(function (err) { if (err) throw err; callback(result); }); }); });}makeQueries(function(result) { console.log(result.rows[Категория0]); });</source> ==== А теперь с генераторами @@ ==== Генератор можно приостановить. Пишем обёртку и получаем coroutine:VitaliPrivate <source lang="javascript">const gen = require('gen-thread');const pg = require('pg'); function* makeQueries(){ var client = new pg.Client(); // yield подождёт, пока не выполнится автоматически созданный колбэк gen.ef() yield client.connect(gen.ef()); var result = (yield client.query('SELECT $1::text as name', ['brianc'], gen.ef()))[0]; yield client.end(gen.ef()); return result;} gen.run(makeQueries(), function(result) { console.log(result.rows[0]); });</source> === Промисы и async/await @@ === То же, но стандартно — делается через промисы и async/await. <source lang="javascript">function sleep(millis){ return new Promise(function(resolve, reject) { setTimeout(resolve, millis); });}async function f(){ await sleep(500); await sleep(1000);}// эквивалентно цепочке промисов:function f(){ return sleep(500).then(result => sleep(1000));}</source> {{blue|Это уже не ES6 (2015), а 2016-2017; но Babel всё равно их поддерживает (и транслирует в генераторы).}} ==== Поддержка Promise @@ ==== API с колбэками надо оборачивать. Это нетрудно, но надо знать, куда ставить колбэк: <source lang="javascript">function wrap(fn, ...args){ return new Promise(function(resolve, reject) { try { fn(resolve, ...args); } catch (e) { reject(e); } });}async function test(){ await wrap(setTimeout, 500);}</source> ==== Нюансы с исключениями @@ ==== {{red|Нюанс 1: Promise'ы nodejs глотают исключения}} Решение — [http://bluebirdjs.com Bluebird], он бросает '''Unhandled rejection error''' {{red|Нюанс 2: У асинхронных исключений нет вменяемого стека.}} Стеки в духе:<pre> at Connection.parseE (node_modules/pg/lib/connection.js:554:11) at Connection.parseMessage (node_modules/pg/lib/connection.js:381:17) at Socket.<anonymous> (node_modules/pg/lib/connection.js:117:22) at emitOne (events.js:77:13) at Socket.emit (events.js:169:7) at readableAddChunk (_stream_readable.js:146:16) at Socket.Readable.push (_stream_readable.js:110:10) at TCP.onread (net.js:523:20)</pre> Решение — опять-таки Bluebird + <tt><nowiki>Promise.config({ longStackTraces: true })</nowiki></tt>. ==== gen-thread @@ ==== * Ну, или забить пока на промисы* Юзать генераторы и мой [https://github.com/vitalif/gen-thread gen-thread] (в промисы он тоже умеет) <source lang="javascript">yield gen.p(<PROMISE>);</source> ''Но в итоге, конечно, все перейдут на Promise.'' === Модули ES6 @@ === <source lang="javascript">// lib/math.jsexport function sum (x, y) { return x + y };export var pi = 3.141593;// lib/mathplusplus.jsexport * from "lib/math";export var e = 2.71828182846;export default (x) => Math.exp(x);// someApp.jsimport exp, { pi, e } from "lib/mathplusplus";console.log("e^{π} = " + exp(pi));</source> {{gray|Оговорка: чёткой уверенности, что это лучше CommonJS, у меня нет. Но гибче, да.}} === Template strings @@ === PHP-подобная строковая интерполяция. <source lang="javascript">let name = "John";`Hello, ${name}`;</source> Кастомная интерполяция (для DSL, видимо): <source lang="javascript">get`http://example.com/foo?bar=${bar + baz}&quux=${quux}`;// эквивалентно:get([ "http://example.com/foo?bar=", "&quux=", "" ], bar + baz, quux);</source> === Proxy @@ === <source lang="javascript">let target = { foo: "Welcome, foo" };let proxy = new Proxy(target,{ get (receiver, name) { return name in receiver ? receiver[name] : `Hello, ${name}`; }});proxy.foo === "Welcome, foo";proxy.world === "Hello, world";</source> === Функции, локальные в блоках @@ === (Block-scoped functions) <source lang="javascript">{ function foo () { return 1 } foo() === 1 { function foo () { return 2 } foo() === 2 } foo() === 1}</source> === Итераторы для for .. of @@ === <source lang="javascript">let fibonacci = { [Symbol.iterator]() { let pre = 0, cur = 1; return { next() { [ pre, cur ] = [ cur, pre + cur ]; return { done: false, value: cur }; } }; }}for (let n of fibonacci) { if (n > 1000) break; console.log(n); }</source> == Обзор инструментов @@ == * Пакетные менеджеры* Трансляторы* Сборка, преобразования и минификация* Отладка, профилировка* Редакторы, IDE === Системы модулей @@ === * {{red|AMD}} (RequireJS): Asynchronous Module Definition* {{green|CommonJS}} (node.js)* {{red|UMD}} (AMD+CommonJS, поддерживается и там, и там)* {{green|Модули ES6}} (уже рассказал) Наиболее актуальны CommonJS и ES6. ==== CommonJS @@ ==== ИМХО — самый краткий и удобный синтаксис. <source lang="javascript">const pg = require('pg');const gen = require('gen-thread'); module.exports.method = function() { /*...*/ }; // или даже:module.exports = MyClass; function MyClass(){}/*...*/</source> ==== AMD @@ ==== <source lang="javascript">define(['jquery', 'underscore'], function ($, _) { // methods function a(){}; // private because it's not returned (see below) function b(){}; // public because it's returned function c(){}; // public because it's returned  // exposed public methods return { b: b, c: c }});</source> ==== UMD @@ ==== <source lang="javascript">(function (root, factory) { if (typeof define === 'function' && define.amd) define(['jquery', 'underscore'], factory); else if (typeof exports === 'object') module.exports = factory(require('jquery'), require('underscore')); else root.returnExports = factory(root.jQuery, root._);}(this, function ($, _) { function a(){}; // private because it's not returned (see below) function b(){}; // public because it's returned function c(){}; // public because it's returned return { b: b, c: c }}));</source> === Пакетный менеджер [https://www.npmjs.com/ NPM] @@ === * Использует систему модулей CommonJS* Ставит зависимости '''рекурсивно''' (node_modules/module1/node_modules/module2/…)* Можно их упростить: '''npm dedup''' Пользоваться им не просто, а очень просто:* Установить модуль: {{green|npm install [-g] [--save] [--save-dev] module}}* Создать package.json для проекта: {{green|npm init}}* Зарегистрироваться/залогиниться: {{green|npm login}}* Опубликовать свой модуль: {{green|npm publish}} {{mag|Самих модулей столько, что аж страшно.}} === <s>[https://bower.io/ Bower]</s> @@ === * {{red|Ещё один пакетный менеджер}}* Ставится из npm O_o* Команды примерно те же: install, init… те же semver’ы* {{red|Основные отличия — AMD и плоское дерево зависимостей…}}*: {{green|то есть как будто вы просто сказали npm dedup}}*: {{gray|только свалится, если что-то несовместимо по semver}} : &rArr; Нафиг-нафиг. === Transpiler: Babel/Bublé @@ === Как писать на ES6, если он не везде поддерживается? {{green|Ответ — Babel!}} * REPL: https://babeljs.io/repl/* Командная строка: npm install babel-cli (babel-node)* browserify: babelify ''Transpiler — транслятор JS в JS. Другие: [https://buble.surge.sh/ Bublé], [https://github.com/google/traceur-compiler Traceur].'' === Трансляторы @@ === {{red|В JS транслируют всё, что можно и нельзя}}* [https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-js List of languages that compile to JS]* Тайпчекеры: [https://www.typescriptlang.org/ TypeScript], [https://flowtype.org/ Flow], [https://www.dartlang.org/ Dart] (язык)* Kotlin, Ceylon, Scala.js, ClojureScript* Непосредственно Java: [http://teavm.org/ TeaVM]* '''C++ (O_O)''': [http://emscripten.org/ Emscripten] (LLVM &rarr; JS), [http://mandreel.com/ Mandreel]* Python: Pyjamas, Transcrypt; Ruby: Opal* OCaml: Ocsigen/Bucklescript* Haskell: ghcjs, haste* {{gray|И много чего ещё}} === Сборка, упаковка и минификация @@ === Актуальное:* {{mag|browserify}} — упаковка npm-модулей (CommonJS) для браузера* {{mag|webpack}} — универсальная система сборки (AMD / CommonJS / ES6 модулей) + CSS + картинки + whatever* {{mag|stealjs}} — оптимизатор js для многостраничных сайтов* [http://eslint.org/ eslint] — проверка на ошибки и стиль* [http://gulpjs.com/ gulp] — запускатор скриптов сборки* uglifyjs — обфускатор === Чуть старее @@ === * grunt — запускатор скриптов сборки (часто grunt+bower)* bower — пакетный менеджер (рассказал выше)* requirejs — система загрузки и сборки AMD модулей* [http://jshint.com/ jshint] — проверка на ошибки и стиль* yui-compressor, closure compiler — обфускаторы* [http://rollupjs.org/ rollup] — система сборки для ES6 модулей (используется реже) === IDE, редакторы, отладка @@ === * {{blue|Netbeans}}*: отладчик встроен*: {{blue|(!)}} умеет живое обновление кода при отладке* {{mag|Atom}} от GitHub*: сам написан на node.js + webkit (Electron)*: напоминает Sublime*: модульный; отладчик — отдельный пакет* {{green|Visual Studio Code}}*: мелкий и мягкий форк Atom’а*: встроен отладчик и поддержка typescript и C# === Отладка из консоли @@ === <tt>nodejs debug app.js;</tt> Есть REPL <source lang="javascript">for (var i = 0; i < 5; i++){ debugger; // брейкпоинт console.log(i);}console.log("done");</source> === Отладка из [https://github.com/node-inspector/node-inspector node-inspector] @@ ===  {{blue|$ npm install -g node-inspector; node-debug app.js}} Node Inspector is now available from http://127.0.0.1:8080/?ws=127.0.0.1:8080&port=5858 Debugging `app.js` Debugger listening on port 5858 [[File:Node_inspector_ss.png|600px]] === Браузерная часть @@ === {{green|Ну, тут проблем нет совсем}}* F12 даже в IE* watchify* есть фокусы с live reload (react/redux)* chrome + netbeans connector === Профилировка @@ === * node --prof, node --prof-process* Профилировка памяти: [https://github.com/bnoordhuis/node-heapdump heapdump]* Flamegraph: [https://github.com/davidmarkclements/0x 0x] [https://github.com/davidmarkclements/0x/raw/master/demo.gif анимация]<br /> [[File:0xFlamegraph.png|300px]]* [http://stackoverflow.com/questions/1911015/how-do-i-debug-node-js-applications/16512303#16512303 Ещё заметки] === Тестирование @@ === * Для браузера: [https://karma-runner.github.io/ Karma]*: {{gray|Запускает обычные тесты, но в открытом приложении в браузере}}* В целом: [https://mochajs.org/ Mocha], Chai, Sinon === Тайпчекеры: TypeScript, Flow @@ === В целом {{green|очень похожи}}.* У {{blue|Flow}} лучше вывод типов* У {{mag|TypeScript}} есть public/private/protected* У {{blue|Flow}} есть Nullable типы (и он автоматически проверяет на null)* TypeScript — {{mag|над}}множество JS* Flow — {{blue|под}}множество JS === TS null @@ === <source lang="javascript">function foo(num: number) { if (num > 10) { return 'cool'; }}</source> <source lang="javascript">// coolconst result: string = foo(100);console.log(result.toString());</source> <source lang="javascript">// still cool?console.log(foo(1).toString());// error at runtime: "Cannot read property 'toString' of undefined"</source> === Flow null @@ === <source lang="javascript">function foo(num: number) { if (num > 10) return 'cool';} // error: call of method `toString`.// Method cannot be called on possibly null valueconsole.log(foo(100).toString()); // error: return undefined. This type is incompatible with stringfunction foo(num: number): string { if (num > 10) return 'cool';}</source> === TS vs Flow: generics @@ === <source lang="javascript">class Animal { name: string; constructor(name: string) { this.name = name; }}class Dog extends Animal { // just to make this different from cat goodBoyFactor: number;}class Cat extends Animal { purrFactor: number;}</source> === TS vs Flow: присваивание элементов @@ === OK в обоих <source lang="javascript">let cats: Array<Cat> = []; // только котыlet animals: Array<Animal> = []; // только животные // неа, не котcats.push(10); // неа, не котcats.push(new Animal('Fido')); // круто, котcats.push(new Cat('Purry')); // круто, кот - тоже животноеanimals.push(new Cat('Purry'));</source> === TS vs Flow: присваивание коллекций @@ === <source lang="javascript">// error TS2322: Type 'Animal[]' is not assignable to type 'Cat[]'.// Type 'Animal' is not assignable to type 'Cat'.// Property 'purrFactor' is missing in type 'Animal'.//cats = animals; // во Flow не пройдёт// опа, в TS работает. но теперь animals небезопасныanimals = cats; // потому что вот это тоже проходит без ошибкиanimals.push(new Dog('Brutus'));animals.push(new Animal('Twinky')); // упсcats.forEach(cat => console.log(`Cat: ${cat.name}`));// Cat: Purry// Cat: Brutus// Cat: Twinky</source> == Обзор фреймворков @@ == * Клиентские фреймворки «старые»* Клиентские фреймворки «новые»* Попытки мобильной разработки* Попытки игровых фреймворков === «Старые» @@ === Заслуживают упоминания:* {{blue|jQuery}} (фиг сдохнет, а надо бы)* {{green|ExtJS}} (тоже фиг сдохнет)* {{red|R.I.P}}: Yahoo UI, PrototypeJS* По-моему, сдыхает: Dojo ==== jQuery @@ ==== {{blue|«Меня зовут Мистер Свинья!»}} ''Если скриптота — рас***дяйство, то jQuery — {{red|ВЕРХ}} рас***дяйства'' Ибо РАСПОЛАГАЕТ к плохому коду:* Всё в кучу: объект-бог с кучей хелперов $ {{mag|и такие же модули}} {{gray|(например, DataTables)}}* <tt><nowiki>$.extend(), $.ajax(), $("<html>"), $(".class")</nowiki></tt>* Селекторы / операции над множествами: <tt><nowiki>$(".class").each(e => $(e).hide())</nowiki></tt>*: {{red|если элементов не найдётся, no problem, ничего не сдохнет}}* Страшные контроллеры, гвоздями прибитые к UI* Отсутствие возможностей оптимизации при динамической сборке UI*: {{gray|Пример — WikiEditor}} ==== ExtJS @@ ==== * Desktop-like фреймворк, {{green|умеет кучу всего}}* Фундаментально {{red|тормозноват}}* Со стилизацией большие трудности*: {{gray|1=Ну хоть тема в ExtJS >= 4 стала норм, можно смотреть без рвотных позывов}}* По моему опыту — писать на нём НЕ проще и НЕ быстрее, чем на HTML+JS* Data binding есть, но ограниченный — только свойств (а не иерархии) компонентов === «Новые» (компонентные) @@ === * Data binding*: <s>Knockout, Ember, Backbone</s>*: {{gray|упомянем для истории; неактуальны}}*: {{green|AngularJS 1, 2}}* Virtual DOM*: {{green|React}} И те, и другие — по сути, {{mag|решают задачу шаблонизации на JS}}. === AngularJS 1 @@ === * Dirty checking* Все биндинги 2-way* Поэтому медленный* Есть компоненты («директивы») и контроллеры* {{blue|На этом уже ''можно'' писать}} ==== Пример @@ ==== <div></div> &lt;div '''ng-controller="LoanCalculator"'''&gt; &lt;div&gt; Дата выдачи: &lt;input name="" type="text" '''ng-model="props.start"''' /&gt;&lt;br /&gt; Сумма: &lt;input name="" type="text" '''ng-model="props.total"''' /&gt;&lt;br /&gt; Процент: &lt;input name="" type="text" '''ng-model="props.percent"''' /&gt;&lt;br /&gt; Срок: &lt;input name="" type="text" '''ng-model="props.months"''' /&gt; месяцев&lt;br /&gt; Штраф за просрочку: &lt;input name="" type="text" '''ng-model="props.fine"''' /&gt;&lt;br /&gt; Пеня % годовых на просрочку: &lt;input name="" type="text" '''ng-model="props.penaltyPercent"''' /&gt;&lt;br /&gt; &lt;input type="button" value="Рассчитать" '''ng-click="recalc()"''' /&gt; &lt;input type="button" value="Сбросить" '''ng-click="clear()"''' /&gt; &lt;/div&gt; &lt;table '''ng-if="payments.length"''' border="0"&gt; &lt;tr&gt; &lt;th&gt;Дата&lt;/th&gt; &lt;th&gt;Сумма&lt;/th&gt; &lt;th&gt;Комментарий&lt;/th&gt; &lt;/tr&gt; &lt;tr '''ng-repeat="payment in payments"'''&gt; &lt;td&gt;&lt;input name="" type="text" '''ng-model="payment.date" ng-change="clear_from($index+1)"''' /&gt;&lt;/td&gt; &lt;td&gt;&lt;input name="" type="text" '''ng-model="payment.total" ng-change="clear_from($index+1)"''' /&gt;&lt;/td&gt; &lt;td&gt;&#x7B;{payment.text}}&lt;/td&gt; &lt;/tr&gt; &lt;tr '''ng-if="clean"'''&gt; &lt;th&gt;Всего&lt;/th&gt; &lt;td&gt;&#x7B;{sum}}&lt;/td&gt; &lt;td&gt;&#x7B;{outsums}}&lt;/td&gt; &lt;/tr&gt; &lt;/table&gt; &lt;/div&gt; === Angular 2 @@ === * Пофиксили скорость, разделив 1-way и 2-way биндинги* Перешли на TypeScript ''{{gray|и всех агитируют}}''* Обновились в целом* {{green|Больше «искаропки»}}* {{red|Но зато тяжелее. Зачем-то тянет за собой RxJS…}} === React: почему я за React? @@ === * {{mag|JSX}}: на первый взгляд странно, на самом деле — круто и удобно!* '''Строго однонаправленный''' поток данных*: {{gray|(2-way binding — как чуть сложнее, так проблема)}}* {{green|Правильная компонентность}}*: Убирает все сомнения в том, MV-что у нас — MVVM, MVC, M-V-VC, M-V-VC-VM…*: Контроллер занимается тем, чем и должен: работой с данными* Легковесный, простой, изящный. Учится за 1 вечер. Не принуждает к дополнительным компонентам. ==== Почему JSX — круто? @@ ==== Даёт писать шаблоны прямо на JS! Удобно и безопасно. Например, цикл:* Ember: {{red|<nowiki>{{# each}}</nowiki>}}* Angular 1: {{red|ng-repeat}}* Angular 2: {{red|ngFor}}* Knockout: {{red|<nowiki>data-bind="foreach"</nowiki>}}* React: {{blue|ПРОСТО ИСПОЛЬЗУЙТЕ JS :)}} (обычный for или Array.map()) <blockquote>Angular 2 continues to put «JS» into HTML. React puts «HTML» into JS.</blockquote> ==== JSX @@ ==== <source lang="javascript">var MessageInList = React.createClass({ msgClasses: { unread: 'unread', unseen: 'unseen', answered: 'replied', flagged: 'pinned', sent: 'sent' }, render: function() { var msg = this.props.msg; return <div data-i={this.props.i} className={'message'+ (msg.body_text || msg.body_html ? '' : ' unloaded')+ (msg.flags.map(c => (this.msgClasses[c] ? ' '+this.msgClasses[c] : '')).join(''))+ (this.props.selected ? ' selected' : '')+ (msg.thread && this.props.threads ? ' thread0' : '')} onMouseDown={this.props.onClick}>  <div className="icon" style={{ width: (20+10*(msg.level||0)), backgroundPosition: (10*(msg.level||0))+'px 7px' }}></div> <div className="subject" style={{ paddingLeft: (20+10*(msg.level||0)) }}>{msg.subject}</div> {msg.thread && this.props.threads ? <div className={'expand'+(msg.collapsed ? '' : ' collapse')}></div> : null} <div className="bullet"></div> <div className="from" style={{ left: (21+10*(msg.level||0)) }}> {(msg.props.sent ? 'To '+(msg.props.to[0][0]||msg.props.to[0][1]) : (msg.props.from ? msg.props.from[0]||msg.props.from[1] : ''))} </div> <div className="size">{Util.formatBytes(msg.size||0)}</div> <div className="attach" style={msg.props.attachments && msg.props.attachments.length ? null : { display: 'none' }}></div> <div className="time">{Util.formatDate(msg.time)}</div>  </div> }});</source> ==== JSX и Virtual DOM @@ ==== * Как применять изменения быстро?*: ''(DOM — относительно медленный)''* Храним {{mag|копию DOM}} в виде json* После render() сравниваем* Изменения применяем {{red|к реальному DOM}}: {{green|&rArr; Быстро и при этом гибко}} ==== [https://facebook.github.io/react/docs/component-specs.html Компоненты React] @@ ==== * Обязательный метод ОДИН: {{blue|render()}}* props и state {{gray|(XML-атрибуты и внутреннее состояние)}}* children {{gray|(тело элемента)}}* getDefaultProps(), getInitialState()* setState(). {{red|нет setProps() и setChildren()!}} {{gray|(получает при render родителя)}}* componentWillMount(), componentDidMount(), componentWillUnmount()* componentWillReceiveProps(p), shouldComponentUpdate(p, s)* componentWillUpdate(p, s), componentDidUpdate(p, s)* propTypes, mixins, statics ==== А вот почему это красиво @@ ==== ExtJS. Класс «панель». {{red|Где тут делать инициализацию?}} [[File:Extjs-events.png|400px]] {{red|beforerender? render? afterrender? afterlayout? added? add? show?}} {{gray|спойлер: нигде}} ==== Библиотеки для React @@ ==== * Контроллер: Flux* Или {{mag|Redux}} (2 кб!)* react-router* react-router-redux* HTTP? superagent* [https://facebook.github.io/draft-js/ Draft.js] {{gray|(rich editor)}} ==== О Redux @@ ==== * «State container»* {{green|Однонаправленный поток данных:}}*: Store &rarr; Компоненты &rarr; Действия &rarr; Диспетчер &rarr; Store* Компоненты «привязываются» к данным в Store* И к вызовам действий* Действие = reducer: {{mag|(Состояние, Действие) &rarr; (НовоеСостояние)}}.: {{gray|&rarr; Очень чёткое отделение контроллера}} === Как писать на React @@ === [https://facebook.github.io/react/docs/thinking-in-react.html Thinking in React]# Набросать (или взять у дизайнеров) HTML-макет# Выделить компоненты#: Помним, что компонент — ''единица рендера''# Сделать статическую версию на React, всё через props# Выделить состояние, определить владельца состояния#: Можно уже брать Flux/Redux#: Presentational vs Controller# Научить менять состояние === Прочее @@ === * Мобильное** React Native: {{gray|Virtual DOM над нативными компонентами}}** NativeScript: {{gray|Тоже JS в отдельном потоке и свой XML-язык описания UI}}** {{green|Уверен, появятся ещё}}* https://html5gameengine.com/* WebGL: SceneJS, OSG.JS, Blend4Web == Демонстрация LikeOpera @@ %% == {{gray|Клон Opera Mail на React и node.js}} == Вопросы @@ %% == ?