ECMAScript и все-все-все — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
м
м (Destructuring @@)
 
(не показано 140 промежуточных версий этого же участника)
Строка 1: Строка 1:
<slideshow title="" style="nobook" scaled="true" font="Consolas, Segoe UI, cursive" headingmark="@@" centermark="%%" incmark="++" footer="">
+
<slideshow title="" style="yellnobook" scaled="true" font="Roboto, Segoe UI, cursive" headingmark="@@" centermark="%%" incmark="++" footer="">
; subfooter: <span style="font-family: Consolas"><span style='background: #f1dd56; padding: 5px'>ECMAScript</span> и все-все-все</span>
+
; subfooter: <span style="font-family: Consolas; background: #f1dd56; padding: 5px; font-style: normal; color: black">ECMAScript и все-все-все</span>
 
</slideshow>
 
</slideshow>
 +
; PDF-версия: [[Media:ES6 and everyone.pdf|ES6 and everyone.pdf]]
  
== ECMAScript - ассемблер будущего,<br /> бэкенд, фронтенд и все-все-все @@ %% ==
+
== ECMAScript — ассемблер будущего,<br /> бэкенд, фронтенд и все-все-все @@ %% ==
  
 
''(Об эволюции и фичах JavaScript)''
 
''(Об эволюции и фичах JavaScript)''
  
https://udemy-images.udemy.com/course/480x270/498246_f031_4.jpg
+
[[File:ES6.jpg|300px]]
  
 
<big>''Виталий Филиппов, CUSTIS''</big>
 
<big>''Виталий Филиппов, CUSTIS''</big>
Строка 16: Строка 17:
  
 
* Начинал лет в 11 с C/C++ (Turbo C / C++Builder)
 
* Начинал лет в 11 с C/C++ (Turbo C / C++Builder)
* Потом открыл для себя Linux, свободный софт...
+
* Потом открыл для себя Linux, свободный софт…
 
*: ''главное читать логи :)''
 
*: ''главное читать логи :)''
* ...LAMP (Perl/PHP), HTML и JS
+
* …LAMP (Perl/PHP), HTML и JS
 
* Теперь полюбил серверный JS (nodejs)
 
* Теперь полюбил серверный JS (nodejs)
  
Строка 33: Строка 34:
  
 
<s>JavaScript</s>ECMAScript
 
<s>JavaScript</s>ECMAScript
 +
* '''От Java только часть названия'''
 
* Скриптота! (динамический язык)
 
* Скриптота! (динамический язык)
 
* Объект/массив/скаляр (JSON)
 
* Объект/массив/скаляр (JSON)
 
* Прототипы, замыкания, колбэки, нет многопоточности
 
* Прототипы, замыкания, колбэки, нет многопоточности
* ES - язык. А ещё есть окружение (DOM, BOM)
+
* ES — язык. А ещё есть окружение (DOM, BOM)
 
* Куча новых фич (ES2015-2016-2017)
 
* Куча новых фич (ES2015-2016-2017)
* Браузерный - Chrome (V8), Firefox (SpiderMonkey) и даже IE (ChakraCore)
+
* Браузерный — Chrome (V8), Firefox (SpiderMonkey) и даже IE (ChakraCore)
* Серверный - node.js (V8)
+
* Серверный — node.js (V8)
  
 
== Скриптота vs типизация @@ %% ==
 
== Скриптота vs типизация @@ %% ==
  
http://3.bp.blogspot.com/_aWbNXEa3LP8/TFvQsof7IZI/AAAAAAAAA8U/NlSMwn-h2L0/s1600/script_kiddies_demotivator.jpeg
+
[[File:Script_kiddies_demotivator.jpeg]]
  
== Холивар!!! @@ ==
+
=== Холивар!!! @@ ===
  
 
* Шутки в сторону: тема серьёзная
 
* Шутки в сторону: тема серьёзная
 
* Популярных динамических языков много
 
* Популярных динамических языков много
*: ''можно пытаться это отрицать, но таки удобно''
+
*: {{gray|''можно пытаться это отрицать, но таки удобно''}}
* Статические... хде они? Java? (C# не в счёт, винда)
+
* Статические… хде они? Java? (C# не в счёт, винда)
 
* Компилируемые новые есть: D, Rust, Go, Vala, Swift
 
* Компилируемые новые есть: D, Rust, Go, Vala, Swift
*: ''но кто на них пишет-то?''
+
*: {{gray|''но кто на них пишет-то?''}}
  
== Все хотят одного @@ ==
+
=== Все хотят одного @@ ===
  
* Типизация - не необходимость, как раньше, а лишь один из способов проверки
+
* Типизация — не необходимость, как раньше, а лишь один из способов проверки
 
*: что ещё можно проверять?
 
*: что ещё можно проверять?
*: Rust = borrow checker
+
*: Rust: borrow checker
*: функциональщина = "purity" checker
+
*: функциональщина: «purity» checker
 +
*: Java: checked exceptions
 
* При этом
 
* При этом
 
*: auto уже даже в C++
 
*: auto уже даже в C++
 
*: тайпчекер (частично) уже даже в PHP (+ Hack)
 
*: тайпчекер (частично) уже даже в PHP (+ Hack)
  
== Проверки - хорошо, но @@ %% ==
+
=== Проверки — хорошо, но @@ %% ===
  
http://demotivators.to/media/posters/327/414874_paranojya.jpg
+
[[Файл:Paranoia.jpg]]
  
 
== Почему JS? @@ ==
 
== Почему JS? @@ ==
  
 
* Нейтральный C-подобный синтаксис
 
* Нейтральный C-подобный синтаксис
* Быстрые интерпретаторы
+
* Быстрый! {{gray|(как вычислительно, так и для I/O)}}
* Событийная машина
+
*: Событийная машина node.js — не пустой звук! {{gray|(1M соединений на одном сервере)}}
* Мощно развивается
+
* Всегда нужен в браузерах {{gray|(а веб нынче даже 1С)}}
 +
* Мощное сообщество и развитие
 +
* Удобный пакетный менеджер npm
 +
* Есть тайпчекеры: TypeScript, Flow, Dart…
  
 
== Синтаксис @@ %% ==
 
== Синтаксис @@ %% ==
  
== Perl @@ ==
+
{{gray|Сравнительный анализ}}
 +
 
 +
=== Perl @@ ===
  
 
<code-perl>
 
<code-perl>
if ($single)
+
sub _skip_attrs
 
{
 
{
    # Single bug
+
     my ($tag, $attrs) = @_;
     my ($id) = list $ARGS->{id};
+
    $tag = lc $tag;
     push @bugs, Bugzilla::Bug->check($id);
+
     return "<$tag>" if $tag =~ m!^/!so;
     if (defined $ARGS->{mark})
+
    my ($enclosed) = $attrs =~ m!/$!so ? ' /' : '';
 +
     $attrs = { $attrs =~ /([^\s=]+)=([^\s=\'\"]+|\"[^\"]*\"|\'[^\']*\')/gso };
 +
    my $new = {};
 +
    for (qw(name id class style title))
 
     {
 
     {
         foreach my $range (split ',', $ARGS->{mark})
+
         $new->{$_} = $attrs->{$_} if $attrs->{$_};
        {
+
            if ($range =~ /^(\d+)-(\d+)$/)
+
            {
+
                foreach my $i ($1..$2)
+
                {
+
                    $marks{$i} = 1;
+
                }
+
            }
+
            elsif ($range =~ /^(\d+)$/)
+
            {
+
                $marks{$1} = 1;
+
            }
+
        }
+
 
     }
 
     }
 +
    my %l = (a => 'href', blockquote => 'cite', q => 'cite');
 +
    if ($attrs->{$l{$tag}} && $attrs->{$l{$tag}} !~ /^[\"\']?javascript/iso)
 +
    {
 +
        $new->{$l{$tag}} = $attrs->{$l{$tag}};
 +
    }
 +
    return "<$tag".join("", map { " $_=".$new->{$_} } keys %$new).$enclosed.">";
 
}
 
}
 
</code-perl>
 
</code-perl>
  
== PHP @@ ==
+
* Спецсимволы захватили мир
 +
* Репутация «write-only», развитие умерло
 +
 
 +
=== PHP @@ ===
  
 
<code-php>
 
<code-php>
Строка 124: Строка 131:
  
 
* Что за $$$$$?
 
* Что за $$$$$?
 +
* Репутация г**нокода
  
== Python @@ ==
+
=== Python @@ ===
  
 
<code-python>
 
<code-python>
Строка 145: Строка 153:
 
* Пробелы меняют смысл?!!!!
 
* Пробелы меняют смысл?!!!!
  
== Ruby @@ ==
+
=== Ruby @@ ===
  
 
<source lang="ruby">
 
<source lang="ruby">
Строка 168: Строка 176:
 
</source>
 
</source>
  
* projects - переменная? Фигвам. Метод без аргументов.)
+
* projects — переменная? Фигвам. Метод без аргументов.)
 +
* чем он лучше хотя бы питона?
  
== Go @@ ==
+
=== Go @@ ===
  
 
<source lang="go">
 
<source lang="go">
Строка 195: Строка 204:
 
* Что за смайлики <tt><nowiki>:= <- & *</nowiki></tt>? Где мои скобочки?
 
* Что за смайлики <tt><nowiki>:= <- & *</nowiki></tt>? Где мои скобочки?
  
== Erlang @@ ==
+
=== Erlang @@ ===
  
 
<source lang="erlang">
 
<source lang="erlang">
Строка 222: Строка 231:
 
</source>
 
</source>
  
* Ой-ой-ой
+
* Ой-ой-ой…
* Классов нет, есть процессы
+
* Специфичен. Классов нет, есть процессы. Но сетевой, да!
  
== OCaml O_O @@ ==
+
=== OCaml O_O @@ ===
  
 
<source lang="ocaml">
 
<source lang="ocaml">
Строка 255: Строка 264:
 
</source>
 
</source>
  
 +
* «вроде что-то древнее»
 
* ПОЛИЗ?!!!!
 
* ПОЛИЗ?!!!!
  
== JS @@ ==
+
=== Lua @@ ===
  
<source lang="javascript>
+
<source lang="lua">
 +
function Encoder.put(self, chunk)
 +
    if self.buffersize < 2 then
 +
        coroutine.yield(chunk)
 +
    else
 +
        if #self.buffer + #chunk > self.buffersize then
 +
            local written = 0
 +
            local fbuffer = self.buffersize - #self.buffer
 +
 
 +
            coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
 +
            written = fbuffer
 +
 
 +
            while #chunk - written > self.buffersize do
 +
                fbuffer = written + self.buffersize
 +
                coroutine.yield(chunk:sub(written + 1, fbuffer))
 +
                written = fbuffer
 +
            end
 +
 
 +
            self.buffer = chunk:sub(written + 1)
 +
        else
 +
            self.buffer = self.buffer .. chunk
 +
        end
 +
    end
 +
end
 +
</source>
 +
 
 +
* Для полноты картины
 +
 
 +
=== JS @@ ===
 +
 
 +
<source lang="javascript">
 
TreeGridNode.prototype.setChildren = function(isLeaf, newChildren)
 
TreeGridNode.prototype.setChildren = function(isLeaf, newChildren)
 
{
 
{
Строка 290: Строка 330:
 
</source>
 
</source>
  
[[Категория:VitaliPrivate]]
+
=== Синтаксис JS @@ %% ===
 +
 
 +
Имхо вполне нейтральненько.
 +
 
 +
{{gray|(неприятных рефлексов вызывать не должен)}}
 +
 
 +
== История JS @@ ==
 +
 
 +
* 1995—2004: дремучий лес с партизанами
 +
* 2004—2008: появление AJAX
 +
* 2008+: шустрота и современный период
 +
 
 +
=== Дремучий период @@ ===
 +
 
 +
1995—1997
 +
* Создание и начальная стандартизация
 +
* DOM ещё нет, только «DOM level 0»
 +
*: document.forms, document.images
 +
 
 +
1998
 +
* DOM level 1 (DHTML)
 +
* document.getElementById, все элементы — объекты
 +
 
 +
2000
 +
* DOM level 2
 +
* События и более-менее современный вид объектов
 +
* pre-AJAX: JSONP, невидимый iframe
 +
 
 +
=== Появление AJAX @@ ===
 +
 
 +
2004
 +
* Firefox 1.0
 +
* XmlHttpRequest
 +
* Первое SPA — Gmail, впервые упомянут термин «AJAX»
 +
* {{gray|Начало конца дремучего периода JS, как языка для всплывающих баннеров}}
 +
 
 +
Совместимость браузеров ещё плохая, так что
 +
* 2006 — jQuery
 +
 
 +
=== Современное развитие @@ ===
 +
 
 +
* 2008 — Google V8
 +
* 2009 — IE8 (M$ очнулся)
 +
* 2009 — node.js (2011 — v0.6)
 +
* 2011 — начало работы над ES6
 +
* 2012 — Angular 1.0
 +
* 2013 — React
 +
* {{mag|Где-то тут же изоморфность}}
 +
* 2015 — ES6 принят
 +
 
 +
== Производительность @@ ==
 +
 
 +
На ЛОРе до сих пор шутят, что «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++ 6.1.1) -O2 = {{green|0.92s}}
 +
* java8 = {{green|0.92s}}; java6 = {{green|1s}} {{gray|''(ручные массивы)''}}
 +
* PyPy (tracing jit) = 1.25s
 +
* Rust 1.12 = {{green|0.85s}}! Go 1.7 = 1.14s
 +
* '''node.js 4.6 = 1.35s''' + [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.6s
 +
* PHP 7 = 6.7s
 +
* Ruby 2.3 = 14.3s, Python 3.5 = 17s, 2.7 = 20s
 +
* Perl = 24s
 +
* '''PHP 5.6 = 42.5s''' :))))) {{mag|ахаха, прекрати}}
 +
 
 +
==== Разница между 64 и 32 бит: i386 @@ ====
 +
 
 +
<plot>
 +
binwidth=0.5
 +
set 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 xtics
 +
set mxtics 1
 +
set style fill solid 1.0 noborder
 +
set boxwidth 0.7 relative
 +
plot '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 lang
 +
1.0 49.9
 +
2.0 46.08
 +
3.0 46.38
 +
4.0 34.98
 +
5.0 37.36
 +
6.0 31.77
 +
7.0 6.3
 +
8.0 2.98
 +
9.0 2.49
 +
10.0 2.12
 +
11.0 1.76
 +
12.0 1.00
 +
ENDDATASET
 +
</plot>
 +
 
 +
==== Разница между 64 и 32 бит: amd64 @@ ====
 +
 
 +
<plot>
 +
binwidth=0.5
 +
set 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 xtics
 +
set mxtics 1
 +
set style fill solid 1.0 noborder
 +
set boxwidth 0.7 relative
 +
plot '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 lang
 +
1.0 50.95
 +
2.0 47.88
 +
3.0 43.53
 +
4.0 29.54
 +
5.0 28.65
 +
6.0 27.18
 +
7.0 8.53
 +
8.0 4.62
 +
9.0 3.18
 +
10.0 2.59
 +
11.0 2.22
 +
12.0 1.00
 +
ENDDATASET
 +
</plot>
 +
 
 +
=== Почему V8 такой быстрый? @@ ===
 +
 
 +
Потому, что 4-слойный JIT!
 +
* {{gray|Как уже сказано, интерпретируемых языков нет.}}
 +
* 1 слой — LLInt, интерпретатор байткода (быстрый старт)
 +
* 2 слой — Baseline JIT
 +
* 3 слой — DFG (Data Flow Graph) JIT
 +
*: {{gray|здесь появляется типизация}}
 +
* 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?
 +
*: method-based jit (JVM)
 +
*: tracing jit (PyPy, TraceMonkey)
 +
* Девиртуализация
 +
* Ускорение поиска в хеше (Lua)
 +
* OSR (On-Stack Replace)
 +
 
 +
=== Отступление: PyPy @@ %% ===
 +
 
 +
Трассирующий JIT-компилятор для Python, очень медленный
 +
 
 +
Рисует множество Мандельброта при сборке
 +
 
 +
[[File:PyPy_mandel.png]]
 +
 
 +
=== LLVM @@ ===
 +
 
 +
LLVM (http://llvm.org), ранее «Low Level Virtual Machine»
 +
* Набор библиотек для построения компиляторов/интерпретаторов
 +
* Модульный
 +
*: исходник &rarr; фронтенд (ЯП) &rarr; LLVM IR (SSA)
 +
*: IR &rarr; оптимизатор LLVM &rarr; IR
 +
*: IR &rarr; бэкенд &rarr; машинный код
 +
* А также сами компиляторы (в первую очередь C/C++/ObjC: Clang)
 +
* {{gray|На LLVM сделаны компилятор шейдеров Radeon и OpenCL}}
 +
 
 +
[[File:LLVMCompiler1.png]]
 +
{{----}}
 +
 
 +
=== SSA @@ ===
 +
 
 +
Пример LLVM IR:
 +
 
 +
<pre>
 +
  store i32 1, i32* %e, align 4
 +
  br label %4
 +
; <label>:4                                      ; preds = %29, %0
 +
  %5 = load i32* %a, align 4
 +
  %6 = load i32* %b, align 4
 +
  %7 = add nsw i32 %5, %6
 +
  store i32 %7, i32* %c, align 4
 +
  %8 = load i32* %c, align 4
 +
  %9 = load i32* %a, align 4
 +
  %10 = sub nsw i32 %8, %9
 +
  store i32 %10, i32* %d, align 4
 +
  %11 = load i32* %d, align 4
 +
  %12 = icmp ne i32 %11, 0
 +
  br i1 %12, label %13, label %14
 +
; <label>:13                                      ; preds = %4
 +
  br label %20
 +
</pre>
 +
 
 +
=== V8 JIT @@ ===
 +
 
 +
* Первый раз функции запускаются в LLInt
 +
* 6 вызовов либо 100 повторов строки
 +
*: {{green|&rarr; OSR в Baseline JIT}}
 +
* C*66 вызовов либо C*1000 повторов строки
 +
*: {{green|&rarr; OSR в DFG JIT}}
 +
*: C ~ 1, больше для больших функций
 +
* Нарушение type guard в DFG
 +
*: {{red|&rarr; OSR обратно в Baseline JIT}}
 +
* C*6666 вызовов либо C*100000 повторов строки
 +
*: {{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
 +
* В Firefox — [https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/Tracing_JIT тоже всё шустро]
 +
* Чакра Наделлы тоже очень похожа на V8
 +
* Постоянная битва :) https://arewefastyet.com/
 +
* {{red|Но язык — ещё не всё!}} Ещё есть:
 +
*: В браузере — {{green|DOM}} (медленный в старых Firefox при формально быстром JS)
 +
*: На сервере — {{green|ввод/вывод}}
 +
 
 +
== I/O bound, а не CPU bound @@ ==
 +
 
 +
* Типичные веб- и бизнес- приложения {{mag|сетевые}}
 +
*: ''«получить из одного места (базы, кэша) и переложить в другое / отдать клиенту»''
 +
*: {{gray|nb: все наши веб-фреймворки ни фига не делают :)}}
 +
*: {{gray|nb: иначе PHP-сайтики были бы неюзабельны}}
 +
* Иногда — '''очень сетевые''' (C10K / C1M)
 +
*: чисто русский термин «хайлоад»
 +
*: 10gbit ethernet уже среди нас
 +
* &rArr; что же делать?..
 +
 
 +
=== Событийные машины! @@ %% ===
 +
 
 +
{{gray|("event loop")}}
 +
 
 +
=== Как вообще обрабатываются соединения клиентов? @@ ===
 +
 
 +
{{green|Все писали сетевой сервер на C? :)}}
 +
 
 +
{{red|Обычный '''(блокирующий)''' ввод/вывод:}}
 +
* {{blue|forking}}: socket(), accept(), fork(), дочерний процесс работает с клиентом
 +
* {{blue|threading}}: socket(), accept(), создаём поток, дочерний работает с клиентом
 +
* {{blue|prefork / thread pool}}: создаём N процессов/потоков заранее
 +
* Потоки и процессы — объекты ОС
 +
*: {{gray|разница только в изоляции памяти}}
 +
*: 1000 потоков — уже тяжело {{gray|(context switch)}}
 +
*: ''«Проблема C10K» {{gray|(обработать 10000 соединений на 1 сервере)}}''
 +
 
 +
=== Событийная машина @@ ===
 +
 
 +
{{green|Неблокирующий ввод/вывод:}}
 +
* socket()
 +
* select() / poll() / epoll / kqueue
 +
*: говорим ОС: «разбуди, когда в любом из сокетов что-то произойдёт»
 +
*: сокет пока один (слушающий)
 +
* новое соединение => добавляем в select и спим дальше
 +
* кто-то прислал запрос => читаем, быстро обрабатываем, отвечаем, спим дальше
 +
* '''всё в один поток''' {{gray|(или в несколько по числу ядер CPU)}}
 +
 
 +
=== Так работает Nginx… @@ %% ===
 +
 
 +
…и весь современный Web (фронтенды)
 +
 
 +
[[Файл:Nginx-logo.png]]
 +
 
 +
{{gray|(плюс zero-copy и раздача статики через sendfile())}}
 +
 
 +
=== А если пойти дальше? @@ ===
 +
 
 +
Кроме HTTP-запросов клиентов ещё есть:
 +
* СУБД/кэши
 +
* Файлы
 +
* REST сервисы
 +
* Websocket’ы
 +
* Сетевые серверы (IMAP?)
 +
 
 +
Почему бы всё это не обрабатывать в одном цикле?
 +
 
 +
{{mag|&rArr; Это и будет «событийная машина»}}
 +
 
 +
=== Событийные машины @@ ===
 +
 
 +
Почти везде опциональная сущность:
 +
* Python: [https://twistedmatrix.com/ Twisted]
 +
*: более-менее популярен
 +
* Java: [http://vertx.io/ Vert.x], [https://github.com/webbit/webbit Webbit]
 +
*: {{gray|мало кто использует, ынтырпрайз же, а тут хипстота какая-то}}
 +
*: {{gray|да, внутри wildfly тоже event loop — но только для HTTP}}
 +
*: в JEE — потоки во все поля
 +
* PHP: [https://github.com/kakserpom/phpdaemon kak.serpom.po.yaitsam/phpdaemon]
 +
*: {{gray|почти никто не использует}}
 +
* Go: goroutine, {{red|но не совсем}}
 +
* Lua+nginx: ngx_lua/cosocket
 +
* Механизмы разные в разных ОС &rArr; '''libevent''', '''libev'''
 +
 
 +
=== Событийная машина node.js @@ ===
 +
 
 +
* Отличительная черта: '''вообще нет блокирующего I/O'''
 +
*: {{gray|тут-то колбэки JS и становятся преимуществом}}
 +
* Реально работает: [https://habrahabr.ru/post/123154/ тест 1M соединений на 1 сервере]
 +
* Кроме {{green|node.js}} такое есть только в {{mag|Erlang}}
 +
*: 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]
 +
 
 +
=== Можно писать и на префорке @@ %% ===
 +
 
 +
Но…
 +
 
 +
<div style="background: #ff0080; color: white; font-family: Consolas, Droid Sans, monospace; padding: 10px; display: inline-block; text-align: center !important">
 +
«Хорошо быть девочкой в розовом пальто,<br />
 +
можно и не девочкой, но уже не то»
 +
</div>
 +
 
 +
== Обзор языка @@ %% ==
 +
 
 +
{{gray|и приколы}}
 +
 
 +
=== Типы данных @@ ===
 +
 
 +
Типы данных (что может вернуть typeof):
 +
* '''null''' {{gray|(не NULL, всё регистрозависимо)}}
 +
* '''undefined''' {{gray|("не определено")}}
 +
* '''number''': <tt><nowiki>1, −100500.05, NaN</nowiki></tt>
 +
* '''boolean''': <tt><nowiki>true, false</nowiki></tt>
 +
* '''string''': <tt><nowiki>"hello1", 'hello2'</nowiki></tt> {{gray|(всегда Unicode)}}
 +
* {{gray|'''symbol''' — для «скрытых полей» (ES6)}}
 +
* всё остальное — '''object'''
 +
** хеш — объект дефолтного типа: <tt><nowiki>{ key: "value" }</nowiki></tt>
 +
** массив — разновидность объекта (класс Array): <tt style="white-space: nowrap"><nowiki>[ "value1", 2, 3, "value3" ]</nowiki></tt>
 +
** функции — тоже объекты, их typeof = '''function'''
 +
 
 +
=== Переменные @@ ===
 +
 
 +
<source lang="javascript">
 +
var a = 123;
 +
</source>
 +
 
 +
Новый синтаксис ES6: let/const.
 +
 
 +
<source lang="javascript">
 +
let a = 123;
 +
const b = 'hello';
 +
</source>
 +
 
 +
* '''var''' локальны в рамках функции или Global Scope (в браузере — window)
 +
* '''let''' и '''const''' локальны, как и положено, в рамках блока
 +
 
 +
=== Функции @@ ===
 +
 
 +
Функции — объекты первого класса в JS.
 +
 
 +
<source lang="javascript">
 +
function f(arg)
 +
{
 +
    return arg;
 +
}
 +
var f = function(a)
 +
{
 +
    return a*a;
 +
};
 +
</source>
 +
 
 +
Новый синтаксис ES6: arrow functions
 +
 
 +
<source lang="javascript">
 +
var f = a => a*a;
 +
var g = (a, b) => a*b;
 +
</source>
 +
 
 +
=== Замыкания @@ ===
 +
 
 +
Функции в JS являются замыканиями («замыкаются» на текущую область видимости):
 +
 
 +
<source lang="javascript">
 +
function a(arg)
 +
{
 +
    var sum = 0;
 +
    var f = function(x) { sum += x; };
 +
    var g = function() { return sum; };
 +
    return [ f, g ];
 +
}
 +
</source>
 +
 
 +
Вызывать функцию можно с любым числом аргументов, пропущенные будут undefined, лишние — в arguments.
 +
 
 +
<source lang="javascript">
 +
function a()
 +
{
 +
    console.log(arguments[1]); // "abc"
 +
}
 +
a(1, "abc", 3, 4);
 +
</source>
 +
 
 +
=== Прототипы (голый/старый JS) @@ ===
 +
 
 +
<source lang="javascript">
 +
function TreeGrid()
 +
{
 +
    // конструктор. тут есть this
 +
}
 +
TreeGrid.prototype.method = function()
 +
{
 +
    // метод класса. тут есть this
 +
    console.log(this);
 +
}
 +
 
 +
var obj = new TreeGrid();
 +
obj.method();
 +
</source>
 +
 
 +
=== Прототипы @@ ===
 +
 
 +
{{gray|(Очень простая альтернатива классам)}}
 +
 
 +
* {{blue|''Объекты «создаются из функций»''}}
 +
* У каждого объекта есть прототип
 +
* Прототип — тоже объект, в нём функции и «свойства по умолчанию»
 +
* У прототипа может быть свой прототип &rArr; наследование
 +
* {{mag|object.__proto__}} — прототип этого объекта (класс, по сути)
 +
*: {{gray|невидим в IE <= 8; можно object.constructor.prototype}}
 +
* {{mag|function.prototype}} — прототип, создаваемый этой функцией как конструктором
 +
 
 +
=== Наследование (голый JS) @@ ===
 +
 
 +
Для наследования в {{mag|prototype}} нужно присвоить объект, {{mag|__proto__}} которого ссылается на базовый класс.
 +
 
 +
<source lang="javascript">
 +
function CalendarGrid()
 +
{
 +
    TreeGrid.call(this, arguments); // "super()"
 +
}
 +
 
 +
CalendarGrid.prototype = Object.create(TreeGrid.prototype);
 +
CalendarGrid.prototype.constructor = CalendarGrid;
 +
</source>
 +
 
 +
=== this @@ ===
 +
 
 +
* this передаётся отдельно, как «контекст вызова»
 +
* this можно «подменить» через '''function.apply(obj, arguments)''' / '''function.call(obj, arg1, arg2, …)'''
 +
* обычные функции/замыкания '''не помнят this''' (!)
 +
* arrow functions '''помнят''' this
 +
* this можно «запомнить» через function.bind() {{gray|(IE9+ / FF4+)}}
 +
*: также через function.bind() можно сделать '''карринг'''
 +
*: {{gray|(ну или явно создав замыкание)}}
 +
 
 +
=== this — примеры @@ ===
 +
 
 +
<source lang="javascript">
 +
var obj = new TreeGrid();
 +
obj.method(); // this = obj
 +
 
 +
var f = obj.method;
 +
f(); // this = window
 +
 
 +
f.apply(obj); // this снова = obj
 +
 
 +
f = obj.method.bind(obj);
 +
f(); // f привязана к obj (this = obj)
 +
 
 +
f = obj.method.bind(obj, "hello"); // карринг
 +
f("arg2"); // эквивалентно obj.method("hello", "arg2");
 +
 
 +
f = obj.method;
 +
f = function() { return f.apply(obj, arguments); }
 +
f(); // то же самое вручную
 +
</source>
 +
 
 +
=== this и замыкания @@ ===
 +
 
 +
<source lang="javascript">
 +
TreeGrid.prototype.method = (arg1, arg2) =>
 +
{
 +
    // Так делать нельзя, this всегда = контексту, в котором описан метод (обычно window)
 +
    console.log(this);
 +
};
 +
 
 +
TreeGrid.prototype.setListener = function()
 +
{
 +
    // Так делать нельзя, this будет = контексту вызова (кликнутому HTML-элементу)
 +
    this.table.addEventListener('click', this.handleClick);
 +
    // И так тоже делать нельзя
 +
    this.table.addEventListener('click', function(event)
 +
    {
 +
        this.handleClick(event);
 +
    });
 +
    // А вот так можно (оп-па!)
 +
    this.table.addEventListener('click', (event) => this.handleClick(event));
 +
    // Ну и вот так тоже можно
 +
    var self = this;
 +
    this.table.addEventListener('click', function(event) { self.handleClick(event); });
 +
};
 +
</source>
 +
 
 +
=== Стандартные конструкции @@ ===
 +
 
 +
* <tt><nowiki>if () {} else if () {} else {}</nowiki></tt> (тело можно однострочное)
 +
* C-подобный for: <tt><nowiki>for (var i = 0; i < 100; i++) {}</nowiki></tt>
 +
* <tt><nowiki>while () {}</nowiki></tt>
 +
* <tt><nowiki>do {} while ()</nowiki></tt>
 +
* break, continue
 +
* C-подобный switch: <tt><nowiki>switch (x) { case 1: case 2: break; default: alert('x'); }</nowiki></tt>
 +
* <tt><nowiki>try { throw 'x'; } catch(e) {} finally {}</nowiki></tt>
 +
 
 +
=== Интересные конструкции @@ ===
 +
 
 +
* 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">
 +
function a(a1, a2)
 +
{
 +
label:
 +
    for (var i of a1)
 +
        for (var j of a2)
 +
            if (!a2[j])
 +
                continue label;
 +
}
 +
</source>
 +
 
 +
=== Приколы: приведение типов @@ ===
 +
 
 +
Приколов в JS немного, но они есть.
 +
 
 +
<source lang="javascript">
 +
// "+" переопределён для строк
 +
console.log("100"+1 == "1001");
 +
console.log("100"-1 == 99); // можно приводить к числу через a-0 или 1*a
 +
 
 +
// в ключах хеша true, false, null и undefined превращаются
 +
// в строки "true", "false", "null" и "undefined"
 +
// имхо, в PHP (true="1", false="", null="") сделано логичней
 +
var a = {};
 +
a[false] = 123;
 +
console.log(a["false"] == 123);
 +
</source>
 +
 
 +
=== Приколы: var @@ ===
 +
 
 +
<source lang="javascript">
 +
// var локальны в функциях!
 +
function f()
 +
{
 +
    for (var i = 0; i < 100; i++)
 +
    {
 +
        // через 0.5сек 100 раз напечатает "100"
 +
        setTimeout(function() { console.log(i); }, 500);
 +
    }
 +
}
 +
 
 +
// и определяются как будто в начале!
 +
var a = 42;
 +
function foo() { alert(typeof a); var a = 10; }
 +
foo(); // --> не 42, а undefined!
 +
</source>
 +
 
 +
=== Приколы: неоднозначный разбор @@ ===
 +
 
 +
<source lang="javascript">
 +
// эта функция, увы, вернёт undefined ({} станет блоком, а key: меткой)
 +
// хеши начинать строго с той же строки!
 +
function fn()
 +
{
 +
    return
 +
    {
 +
        key: "value"
 +
    };
 +
}
 +
 
 +
// (function() {})() - определение + вызов функции, типично, чтобы не засорять контекст
 +
// точки с запятой опциональны, но лучше их ставить. иначе:
 +
A.prototype.m = function()
 +
{
 +
} /* вот тут нужна ; */
 +
 
 +
(function() { B.prototype.m = ... })();
 +
 
 +
// Эквивалентно A.prototype.m = ( ( function(){} ) ( function() { B.prototype.m = ... } ) )();
 +
</source>
 +
 
 +
== <span style="font-family: Consolas; background: #f1dd56; padding: 10px 10px 0 10px; font-style: normal">ES6</span> @@ %% ==
 +
 
 +
Он же ES2015. А также ES2016, ES2017
 +
 
 +
http://es6-features.org/, https://babeljs.io/repl/
 +
 
 +
=== Фичи ES6 @@ ===
 +
 
 +
Уже сказал про:
 +
* let/const
 +
* arrow functions
 +
* for .. of
 +
 
 +
=== Вычислимые свойства объектов @@ ===
 +
 
 +
ES6
 +
<source lang="javascript">
 +
var b = "key";
 +
var a = { [b+"_value"]: "v2" }; // выражение в []
 +
</source>
 +
 
 +
ES5
 +
<source lang="javascript">
 +
var b = "key2";
 +
var a = { key: "value" }; // ключи - только литералы, можно без кавычек
 +
a[b] = 'v2'; // динамические ключи только так
 +
</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);
 +
 
 +
// Кстати, в ES5 нельзя сделать apply конструктору. А тут:
 +
new Object(...args);
 +
 
 +
// Распаковка массива
 +
let arr = [ ...iterable, 4, 5, 6 ];
 +
 
 +
// слияние объектов. у каждого фреймфорка было своё - $.extend, Ext.extend, Object.assign
 +
let merge_obj = { ...obj1, ...obj2, key: "value" };
 +
 
 +
// Распаковка в массив
 +
[ a, b, ...other ] = [ 1, 2, 3, 4, 5 ];
 +
</source>
 +
 
 +
=== Упрощённые названия ключей @@ ===
 +
 
 +
ES6
 +
<source lang="javascript">
 +
obj = { x, y, a() { return 'abc'; } };
 +
</source>
 +
 
 +
ES5
 +
<source lang="javascript">
 +
obj = { x: x, y: y, a: function a() { return 'abc'; } };
 +
</source>
 +
 
 +
=== Классы (!) @@ ===
 +
 
 +
«Наконец-то», скажете вы.
 +
 
 +
<source lang="javascript">
 +
class Shape
 +
{
 +
    constructor (id, x, y)
 +
    {
 +
        this.id = id;
 +
        this.move(x, y);
 +
    }
 +
    move (x, y)
 +
    {
 +
        this.x = x;
 +
        this.y = y;
 +
    }
 +
}
 +
</source>
 +
 
 +
=== Наследование и property @@ ===
 +
 
 +
<source lang="javascript">
 +
class Rectangle extends Shape
 +
{
 +
    constructor (id, x, y, width, height)
 +
    {
 +
        super(id, x, y);
 +
        this._width = width;
 +
        this._height = height;
 +
    }
 +
    static default()
 +
    {
 +
        return new Rectangle("default", 0, 0, 100, 200);
 +
    }
 +
    set width(width) { this._width = width; }
 +
    get width() { return this._width; }
 +
    get area() { return this._width*this._height; }
 +
}
 +
</source>
 +
 
 +
Нюанс: 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:
 +
 
 +
<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.js
 +
export function sum (x, y) { return x + y };
 +
export var pi = 3.141593;
 +
// lib/mathplusplus.js
 +
export * from "lib/math";
 +
export var e = 2.71828182846;
 +
export default (x) => Math.exp(x);
 +
// someApp.js
 +
import 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">
 +
// cool
 +
const 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 value
 +
console.log(foo(100).toString());
 +
 
 +
// error: return undefined. This type is incompatible with string
 +
function 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}}
 +
 
 +
== Вопросы @@ %% ==
 +
 
 +
?

Текущая версия на 14:49, 9 февраля 2017

Автор

Виталий Филиппов
Дополнительный нижний колонтитул

ECMAScript и все-все-все
PDF-версия
ES6 and everyone.pdf

Содержание

ECMAScript — ассемблер будущего,
бэкенд, фронтенд и все-все-все @@ %%

(Об эволюции и фичах JavaScript)

ES6.jpg

Виталий Филиппов, CUSTIS

О своих предпочтениях @@

«И давно вы занимаетесь программизмом?»

  • Начинал лет в 11 с C/C++ (Turbo C / C++Builder)
  • Потом открыл для себя Linux, свободный софт…
    главное читать логи :)
  • …LAMP (Perl/PHP), HTML и JS
  • Теперь полюбил серверный JS (nodejs)

О чём доклад? @@

  • Почему JS?
  • История JavaScript
  • Обзор языка, производительность
  • Обзор выдумок
    (фреймворки, системы сборки и т.п)
  • Немного демо

Что такое JS? @@

JavaScriptECMAScript

  • От Java только часть названия
  • Скриптота! (динамический язык)
  • Объект/массив/скаляр (JSON)
  • Прототипы, замыкания, колбэки, нет многопоточности
  • ES — язык. А ещё есть окружение (DOM, BOM)
  • Куча новых фич (ES2015-2016-2017)
  • Браузерный — Chrome (V8), Firefox (SpiderMonkey) и даже IE (ChakraCore)
  • Серверный — node.js (V8)

Скриптота vs типизация @@ %%

Script kiddies demotivator.jpeg

Холивар!!! @@

  • Шутки в сторону: тема серьёзная
  • Популярных динамических языков много
    можно пытаться это отрицать, но таки удобно
  • Статические… хде они? Java? (C# не в счёт, винда)
  • Компилируемые новые есть: D, Rust, Go, Vala, Swift
    но кто на них пишет-то?

Все хотят одного @@

  • Типизация — не необходимость, как раньше, а лишь один из способов проверки
    что ещё можно проверять?
    Rust: borrow checker
    функциональщина: «purity» checker
    Java: checked exceptions
  • При этом
    auto уже даже в C++
    тайпчекер (частично) уже даже в PHP (+ Hack)

Проверки — хорошо, но @@ %%

Paranoia.jpg

Почему JS? @@

  • Нейтральный C-подобный синтаксис
  • Быстрый! (как вычислительно, так и для I/O)
    Событийная машина node.js — не пустой звук! (1M соединений на одном сервере)
  • Всегда нужен в браузерах (а веб нынче даже 1С)
  • Мощное сообщество и развитие
  • Удобный пакетный менеджер npm
  • Есть тайпчекеры: TypeScript, Flow, Dart…

Синтаксис @@ %%

Сравнительный анализ

Perl @@

sub _skip_attrs
{
    my ($tag, $attrs) = @_;
    $tag = lc $tag;
    return "<$tag>" if $tag =~ m!^/!so;
    my ($enclosed) = $attrs =~ m!/$!so ? ' /' : '';
    $attrs = { $attrs =~ /([^\s=]+)=([^\s=\'\"]+|\"[^\"]*\"|\'[^\']*\')/gso };
    my $new = {};
    for (qw(name id class style title))
    {
        $new->{$_} = $attrs->{$_} if $attrs->{$_};
    }
    my %l = (a => 'href', blockquote => 'cite', q => 'cite');
    if ($attrs->{$l{$tag}} && $attrs->{$l{$tag}} !~ /^[\"\']?javascript/iso)
    {
        $new->{$l{$tag}} = $attrs->{$l{$tag}};
    }
    return "<$tag".join("", map { " $_=".$new->{$_} } keys %$new).$enclosed.">";
}
  • Спецсимволы захватили мир
  • Репутация «write-only», развитие умерло

PHP @@

$isExact = [];
foreach ([ 'line' => 'l', 'cfo' => 'cc' ] as $k => $t)
{
    if (!isset($specified[$k.'_id']) && !isset($specified[$k.'_id_exact']) && 
        !isset($groups[$k]) && !isset($groups[$k.'_all']))
        $isExact[] = "$posAlias.${k}_id IS NULL";
    elseif ($lastgrp == $k.'all')
        $isExact[] = "$posAlias.${k}_id=$t.id";
}
foreach ([ 'party', 'account', 'paytype' ] as $k)
    if (!isset($specified[$k.'_id']) && !isset($groups[$k]))
        $isExact[] = "$posAlias.${k}_id IS NULL";
return implode(' AND ', $isExact) ?: '1=1';
  • Что за $$$$$?
  • Репутация г**нокода

Python @@

class FileCache:
  def __init__(self, dir):
    self.dir = dir
    if not os.path.isdir(dir):
      os.mkdir(dir)
  def fn(self, key):
    key = re.sub('([^a-zA-Z0-9_\-]+)', lambda x: binascii.hexlify(x.group(1)), key)
    return self.dir+'/'+key
  def clean(self):
    t = time.time()
    for fn in os.listdir(self.dir):
      if t > os.stat(self.dir+'/'+fn).st_mtime:
        os.unlink(self.dir+'/'+fn)
  • Пробелы меняют смысл?!!!!

Ruby @@

module Gitlab
  class SearchResults
    attr_reader :current_user, :query
 
    def objects(scope, page = nil)
      case scope
      when 'projects'
        projects.page(page).per(per_page)
      when 'issues'
        issues.page(page).per(per_page)
      when 'merge_requests'
        merge_requests.page(page).per(per_page)
      when 'milestones'
        milestones.page(page).per(per_page)
      else
        Kaminari.paginate_array([]).page(page).per(per_page)
      end
    end
  • projects — переменная? Фигвам. Метод без аргументов.)
  • чем он лучше хотя бы питона?

Go @@

func TestChannelStoreSave(t *testing.T) {
        Setup()
 
        teamId := model.NewId()
 
        o1 := model.Channel{}
        o1.TeamId = teamId
        o1.DisplayName = "Name"
        o1.Name = "a" + model.NewId() + "b"
        o1.Type = model.CHANNEL_OPEN
 
        if err := (<-store.Channel().Save(&o1)).Err; err != nil {
                t.Fatal("couldn't save item", err)
        }
 
        if err := (<-store.Channel().Save(&o1)).Err; err == nil {
                t.Fatal("shouldn't be able to update from save")
        }
  • Что за смайлики := <- & *? Где мои скобочки?

Erlang @@

iq_handler(From, _To,
	   #iq{type=set, lang = Lang,
	       sub_el = #xmlel{name = Operation} = SubEl} = IQ, CC)->
    ?DEBUG("carbons IQ received: ~p", [IQ]),
    {U, S, R} = jid:tolower(From),
    Result = case Operation of
        <<"enable">>->
	    ?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]),
            enable(S,U,R,CC);
        <<"disable">>->
	    ?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
            disable(S, U, R)
    end,
    case Result of
        ok ->
	    ?DEBUG("carbons IQ result: ok", []),
            IQ#iq{type=result, sub_el=[]};
	{error,_Error} ->
	    ?ERROR_MSG("Error enabling / disabling carbons: ~p", [Result]),
	    Txt = <<"Database failure">>,
            IQ#iq{type=error,sub_el = [SubEl, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)]}
end;
  • Ой-ой-ой…
  • Специфичен. Классов нет, есть процессы. Но сетевой, да!

OCaml O_O @@

let log_client_info c sock = 
  let buf = Buffer.create 100 in
  let date = BasicSocket.date_of_int (last_time ()) in
  Printf.bprintf buf "%-12s(%d):%d -> %-30s[%-14s %-20s] connected for %5d secs %-10s bw %5d/%-5d %-6s %2d/%-2d reqs " 
    (Date.simple date) 
  (nb_sockets ())
  (client_num c)
  (
    let s = c.client_name in
    let len = String.length s in 
    if len > 30 then String.sub s 0 30 else s)
 
  (brand_to_string c.client_brand)
  (match c.client_kind with Indirect_address _ | Invalid_address _ -> "LowID"
    | Direct_address (ip,port) -> Printf.sprintf "%s:%d"
          (Ip.to_string ip) port)
  (last_time () - c.client_connect_time)
  (if c.client_rank > 0 then
      Printf.sprintf "rank %d" c.client_rank
      else "")
  (nwritten sock) (nread sock)
  (if c.client_banned then "banned" else "")
  c.client_requests_received
    c.client_requests_sent
  ;
  • «вроде что-то древнее»
  • ПОЛИЗ?!!!!

Lua @@

function Encoder.put(self, chunk)
    if self.buffersize < 2 then
        coroutine.yield(chunk)
    else
        if #self.buffer + #chunk > self.buffersize then
            local written = 0
            local fbuffer = self.buffersize - #self.buffer
 
            coroutine.yield(self.buffer .. chunk:sub(written + 1, fbuffer))
            written = fbuffer
 
            while #chunk - written > self.buffersize do
                fbuffer = written + self.buffersize
                coroutine.yield(chunk:sub(written + 1, fbuffer))
                written = fbuffer
            end
 
            self.buffer = chunk:sub(written + 1)
        else
            self.buffer = self.buffer .. chunk
        end
    end
end
  • Для полноты картины

JS @@

TreeGridNode.prototype.setChildren = function(isLeaf, newChildren)
{
    if (!this.tr)
        this.grid.tbody.innerHTML = '';
    else
    {
        var tr = this.tr[this.tr.length-1];
        while (tr.nextSibling && tr.nextSibling._node.level > this.level)
            this.grid.tbody.removeChild(tr.nextSibling);
        if (this.leaf != isLeaf)
        {
            if (isLeaf)
            {
                this.tr[0].cells[0].firstChild.className = 'collapser collapser-inactive';
                removeListener(this.tr[0].cells[0].firstChild, 'click', this._getToggleHandler());
            }
            else
            {
                this.tr[0].cells[0].firstChild.className = this.collapsed ? 'collapser collapser-collapsed' : 'collapser collapser-expanded';
                addListener(this.tr[0].cells[0].firstChild, 'click', this._getToggleHandler());
            }
        }
    }
    this.leaf = isLeaf;
    this.children = [];
    this.childrenByKey = {};
    this.addChildren(newChildren);
}

Синтаксис JS @@ %%

Имхо вполне нейтральненько.

(неприятных рефлексов вызывать не должен)

История JS @@

  • 1995—2004: дремучий лес с партизанами
  • 2004—2008: появление AJAX
  • 2008+: шустрота и современный период

Дремучий период @@

1995—1997

  • Создание и начальная стандартизация
  • DOM ещё нет, только «DOM level 0»
    document.forms, document.images

1998

  • DOM level 1 (DHTML)
  • document.getElementById, все элементы — объекты

2000

  • DOM level 2
  • События и более-менее современный вид объектов
  • pre-AJAX: JSONP, невидимый iframe

Появление AJAX @@

2004

  • Firefox 1.0
  • XmlHttpRequest
  • Первое SPA — Gmail, впервые упомянут термин «AJAX»
  • Начало конца дремучего периода JS, как языка для всплывающих баннеров

Совместимость браузеров ещё плохая, так что

  • 2006 — jQuery

Современное развитие @@

  • 2008 — Google V8
  • 2009 — IE8 (M$ очнулся)
  • 2009 — node.js (2011 — v0.6)
  • 2011 — начало работы над ES6
  • 2012 — Angular 1.0
  • 2013 — React
  • Где-то тут же изоморфность
  • 2015 — ES6 принят

Производительность @@

На ЛОРе до сих пор шутят, что «Java тормозит», а что ж тогда JS?

  • А ничего — и Java быстрая, и он быстрый. Мамонты видать шутят
    из скриптоты node.js быстрее всех
  • Но он же интерпретируемый?
    НЕТ! Интерпретируемых языков уже вообще нет.
    Ну, разве что bash…

Вычислительный бенчмарк @@

  • https://github.com/famzah/langs-performance, время на 10 итераций (i386)
  • C++ (g++ 6.1.1) -O2 = 0.92s
  • java8 = 0.92s; java6 = 1s (ручные массивы)
  • PyPy (tracing jit) = 1.25s
  • Rust 1.12 = 0.85s! Go 1.7 = 1.14s
  • node.js 4.6 = 1.35s + баг; nodejs 0.10 = 2.6s
  • PHP 7 = 6.7s
  • Ruby 2.3 = 14.3s, Python 3.5 = 17s, 2.7 = 20s
  • Perl = 24s
  • PHP 5.6 = 42.5s :))))) ахаха, прекрати

Разница между 64 и 32 бит: i386 @@

Разница между 64 и 32 бит: amd64 @@

Почему V8 такой быстрый? @@

Потому, что 4-слойный JIT!

  • Как уже сказано, интерпретируемых языков нет.
  • 1 слой — LLInt, интерпретатор байткода (быстрый старт)
  • 2 слой — Baseline JIT
  • 3 слой — DFG (Data Flow Graph) JIT
    здесь появляется типизация
  • 4 слой — FTL JIT (LLVM B3)

FTL JIT performance.png [1]

Ключевые слова о том, как это всё устроено @@

  • Какой бывает JIT?
    method-based jit (JVM)
    tracing jit (PyPy, TraceMonkey)
  • Девиртуализация
  • Ускорение поиска в хеше (Lua)
  • OSR (On-Stack Replace)

Отступление: PyPy @@ %%

Трассирующий JIT-компилятор для Python, очень медленный

Рисует множество Мандельброта при сборке

PyPy mandel.png

LLVM @@

LLVM (http://llvm.org), ранее «Low Level Virtual Machine»

  • Набор библиотек для построения компиляторов/интерпретаторов
  • Модульный
    исходник → фронтенд (ЯП) → LLVM IR (SSA)
    IR → оптимизатор LLVM → IR
    IR → бэкенд → машинный код
  • А также сами компиляторы (в первую очередь C/C++/ObjC: Clang)
  • На LLVM сделаны компилятор шейдеров Radeon и OpenCL

LLVMCompiler1.png

SSA @@

Пример LLVM IR:

  store i32 1, i32* %e, align 4
  br label %4
; <label>:4                                       ; preds = %29, %0
  %5 = load i32* %a, align 4
  %6 = load i32* %b, align 4
  %7 = add nsw i32 %5, %6
  store i32 %7, i32* %c, align 4
  %8 = load i32* %c, align 4
  %9 = load i32* %a, align 4
  %10 = sub nsw i32 %8, %9
  store i32 %10, i32* %d, align 4
  %11 = load i32* %d, align 4
  %12 = icmp ne i32 %11, 0
  br i1 %12, label %13, label %14
; <label>:13                                      ; preds = %4
  br label %20

V8 JIT @@

  • Первый раз функции запускаются в LLInt
  • 6 вызовов либо 100 повторов строки
    → OSR в Baseline JIT
  • C*66 вызовов либо C*1000 повторов строки
    → OSR в DFG JIT
    C ~ 1, больше для больших функций
  • Нарушение type guard в DFG
    → OSR обратно в Baseline JIT
  • C*6666 вызовов либо C*100000 повторов строки
    → OSR в LLVM/B3 JIT

Мало? @@

asm.js (презентация)

  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;
  }

Asmjs-bench.png

Производительность — итого @@

  • Итого, V8 — «смешанный» JIT
  • В Firefox — тоже всё шустро
  • Чакра Наделлы тоже очень похожа на V8
  • Постоянная битва :) https://arewefastyet.com/
  • Но язык — ещё не всё! Ещё есть:
    В браузере — DOM (медленный в старых Firefox при формально быстром JS)
    На сервере — ввод/вывод

I/O bound, а не CPU bound @@

  • Типичные веб- и бизнес- приложения сетевые
    «получить из одного места (базы, кэша) и переложить в другое / отдать клиенту»
    nb: все наши веб-фреймворки ни фига не делают :)
    nb: иначе PHP-сайтики были бы неюзабельны
  • Иногда — очень сетевые (C10K / C1M)
    чисто русский термин «хайлоад»
    10gbit ethernet уже среди нас
  • ⇒ что же делать?..

Событийные машины! @@ %%

("event loop")

Как вообще обрабатываются соединения клиентов? @@

Все писали сетевой сервер на C? :)

Обычный (блокирующий) ввод/вывод:

  • forking: socket(), accept(), fork(), дочерний процесс работает с клиентом
  • threading: socket(), accept(), создаём поток, дочерний работает с клиентом
  • prefork / thread pool: создаём N процессов/потоков заранее
  • Потоки и процессы — объекты ОС
    разница только в изоляции памяти
    1000 потоков — уже тяжело (context switch)
    «Проблема C10K» (обработать 10000 соединений на 1 сервере)

Событийная машина @@

Неблокирующий ввод/вывод:

  • socket()
  • select() / poll() / epoll / kqueue
    говорим ОС: «разбуди, когда в любом из сокетов что-то произойдёт»
    сокет пока один (слушающий)
  • новое соединение => добавляем в select и спим дальше
  • кто-то прислал запрос => читаем, быстро обрабатываем, отвечаем, спим дальше
  • всё в один поток (или в несколько по числу ядер CPU)

Так работает Nginx… @@ %%

…и весь современный Web (фронтенды)

Nginx-logo.png

(плюс zero-copy и раздача статики через sendfile())

А если пойти дальше? @@

Кроме HTTP-запросов клиентов ещё есть:

  • СУБД/кэши
  • Файлы
  • REST сервисы
  • Websocket’ы
  • Сетевые серверы (IMAP?)

Почему бы всё это не обрабатывать в одном цикле?

⇒ Это и будет «событийная машина»

Событийные машины @@

Почти везде опциональная сущность:

  • Python: Twisted
    более-менее популярен
  • Java: Vert.x, Webbit
    мало кто использует, ынтырпрайз же, а тут хипстота какая-то
    да, внутри wildfly тоже event loop — но только для HTTP
    в JEE — потоки во все поля
  • PHP: kak.serpom.po.yaitsam/phpdaemon
    почти никто не использует
  • Go: goroutine, но не совсем
  • Lua+nginx: ngx_lua/cosocket
  • Механизмы разные в разных ОС ⇒ libevent, libev

Событийная машина node.js @@

Можно писать и на префорке @@ %%

Но…

«Хорошо быть девочкой в розовом пальто,
можно и не девочкой, но уже не то»

Обзор языка @@ %%

и приколы

Типы данных @@

Типы данных (что может вернуть typeof):

  • null (не NULL, всё регистрозависимо)
  • undefined ("не определено")
  • number: 1, −100500.05, NaN
  • boolean: true, false
  • string: "hello1", 'hello2' (всегда Unicode)
  • symbol — для «скрытых полей» (ES6)
  • всё остальное — object
    • хеш — объект дефолтного типа: { key: "value" }
    • массив — разновидность объекта (класс Array): [ "value1", 2, 3, "value3" ]
    • функции — тоже объекты, их typeof = function

Переменные @@

var a = 123;

Новый синтаксис ES6: let/const.

let a = 123;
const b = 'hello';
  • var локальны в рамках функции или Global Scope (в браузере — window)
  • let и const локальны, как и положено, в рамках блока

Функции @@

Функции — объекты первого класса в JS.

function f(arg)
{
    return arg;
}
var f = function(a)
{
    return a*a;
};

Новый синтаксис ES6: arrow functions

var f = a => a*a;
var g = (a, b) => a*b;

Замыкания @@

Функции в JS являются замыканиями («замыкаются» на текущую область видимости):

function a(arg)
{
    var sum = 0;
    var f = function(x) { sum += x; };
    var g = function() { return sum; };
    return [ f, g ];
}

Вызывать функцию можно с любым числом аргументов, пропущенные будут undefined, лишние — в arguments.

function a()
{
    console.log(arguments[1]); // "abc"
}
a(1, "abc", 3, 4);

Прототипы (голый/старый JS) @@

function TreeGrid()
{
    // конструктор. тут есть this
}
TreeGrid.prototype.method = function()
{
    // метод класса. тут есть this
    console.log(this);
}
 
var obj = new TreeGrid();
obj.method();

Прототипы @@

(Очень простая альтернатива классам)

  • Объекты «создаются из функций»
  • У каждого объекта есть прототип
  • Прототип — тоже объект, в нём функции и «свойства по умолчанию»
  • У прототипа может быть свой прототип ⇒ наследование
  • object.__proto__ — прототип этого объекта (класс, по сути)
  • function.prototype — прототип, создаваемый этой функцией как конструктором

Наследование (голый JS) @@

Для наследования в prototype нужно присвоить объект, __proto__ которого ссылается на базовый класс.

function CalendarGrid()
{
    TreeGrid.call(this, arguments); // "super()"
}
 
CalendarGrid.prototype = Object.create(TreeGrid.prototype);
CalendarGrid.prototype.constructor = CalendarGrid;

this @@

  • this передаётся отдельно, как «контекст вызова»
  • this можно «подменить» через function.apply(obj, arguments) / function.call(obj, arg1, arg2, …)
  • обычные функции/замыкания не помнят this (!)
  • arrow functions помнят this
  • this можно «запомнить» через function.bind() (IE9+ / FF4+)
    также через function.bind() можно сделать карринг
    (ну или явно создав замыкание)

this — примеры @@

var obj = new TreeGrid();
obj.method(); // this = obj
 
var f = obj.method;
f(); // this = window
 
f.apply(obj); // this снова = obj
 
f = obj.method.bind(obj);
f(); // f привязана к obj (this = obj)
 
f = obj.method.bind(obj, "hello"); // карринг
f("arg2"); // эквивалентно obj.method("hello", "arg2");
 
f = obj.method;
f = function() { return f.apply(obj, arguments); }
f(); // то же самое вручную

this и замыкания @@

TreeGrid.prototype.method = (arg1, arg2) =>
{
    // Так делать нельзя, this всегда = контексту, в котором описан метод (обычно window)
    console.log(this);
};
 
TreeGrid.prototype.setListener = function()
{
    // Так делать нельзя, this будет = контексту вызова (кликнутому HTML-элементу)
    this.table.addEventListener('click', this.handleClick);
    // И так тоже делать нельзя
    this.table.addEventListener('click', function(event)
    {
        this.handleClick(event);
    });
    // А вот так можно (оп-па!)
    this.table.addEventListener('click', (event) => this.handleClick(event));
    // Ну и вот так тоже можно
    var self = this;
    this.table.addEventListener('click', function(event) { self.handleClick(event); });
};

Стандартные конструкции @@

  • if () {} else if () {} else {} (тело можно однострочное)
  • C-подобный for: for (var i = 0; i < 100; i++) {}
  • while () {}
  • do {} while ()
  • break, continue
  • C-подобный switch: switch (x) { case 1: case 2: break; default: alert('x'); }
  • try { throw 'x'; } catch(e) {} finally {}

Интересные конструкции @@

  • a == b (мягкое сравнение) и a === b (точное сравнение)
    "" == false, 0 == false, "1" == true, 1 == true, "5" == 5, null == undefined
  • regexp literals: var re = /<html[^<>]*>/i;
  • break label, continue label
  • Цикл по ключам: for (var i in obj) {}
  • Цикл по значениям (ES6): for (var i of obj) {}
function a(a1, a2)
{
label:
    for (var i of a1)
        for (var j of a2)
            if (!a2[j])
                continue label;
}

Приколы: приведение типов @@

Приколов в JS немного, но они есть.

// "+" переопределён для строк
console.log("100"+1 == "1001");
console.log("100"-1 == 99); // можно приводить к числу через a-0 или 1*a
 
// в ключах хеша true, false, null и undefined превращаются
// в строки "true", "false", "null" и "undefined"
// имхо, в PHP (true="1", false="", null="") сделано логичней
var a = {};
a[false] = 123;
console.log(a["false"] == 123);

Приколы: var @@

// var локальны в функциях!
function f()
{
    for (var i = 0; i < 100; i++)
    {
        // через 0.5сек 100 раз напечатает "100"
        setTimeout(function() { console.log(i); }, 500);
    }
}
 
// и определяются как будто в начале!
var a = 42;
function foo() { alert(typeof a); var a = 10; }
foo(); // --> не 42, а undefined!

Приколы: неоднозначный разбор @@

// эта функция, увы, вернёт undefined ({} станет блоком, а key: меткой)
// хеши начинать строго с той же строки!
function fn()
{
    return
    {
        key: "value"
    };
}
 
// (function() {})() - определение + вызов функции, типично, чтобы не засорять контекст
// точки с запятой опциональны, но лучше их ставить. иначе:
A.prototype.m = function()
{
} /* вот тут нужна ; */
 
(function() { B.prototype.m = ... })();
 
// Эквивалентно A.prototype.m = ( ( function(){} ) ( function() { B.prototype.m = ... } ) )();

ES6 @@ %%

Он же ES2015. А также ES2016, ES2017

http://es6-features.org/, https://babeljs.io/repl/

Фичи ES6 @@

Уже сказал про:

  • let/const
  • arrow functions
  • for .. of

Вычислимые свойства объектов @@

ES6

var b = "key";
var a = { [b+"_value"]: "v2" }; // выражение в []

ES5

var b = "key2";
var a = { key: "value" }; // ключи - только литералы, можно без кавычек
a[b] = 'v2'; // динамические ключи только так

Destructuring @@

«Деструктивное присваивание»

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; }, {});

Оператор распаковки @@

function f(...args) {}
 
f(...iterable);
 
// Кстати, в ES5 нельзя сделать apply конструктору. А тут:
new Object(...args);
 
// Распаковка массива
let arr = [ ...iterable, 4, 5, 6 ];
 
// слияние объектов. у каждого фреймфорка было своё - $.extend, Ext.extend, Object.assign
let merge_obj = { ...obj1, ...obj2, key: "value" };
 
// Распаковка в массив
[ a, b, ...other ] = [ 1, 2, 3, 4, 5 ];

Упрощённые названия ключей @@

ES6

obj = { x, y, a() { return 'abc'; } };

ES5

obj = { x: x, y: y, a: function a() { return 'abc'; } };

Классы (!) @@

«Наконец-то», скажете вы.

class Shape
{
    constructor (id, x, y)
    {
        this.id = id;
        this.move(x, y);
    }
    move (x, y)
    {
        this.x = x;
        this.y = y;
    }
}

Наследование и property @@

class Rectangle extends Shape
{
    constructor (id, x, y, width, height)
    {
        super(id, x, y);
        this._width = width;
        this._height = height;
    }
    static default()
    {
        return new Rectangle("default", 0, 0, 100, 200);
    }
    set width(width) { this._width = width; }
    get width() { return this._width; } 
    get area() { return this._width*this._height; }
}

Нюанс: IE8+, так как при трансляции в ES5 требуют Object.defineProperty().

Генераторы @@

Функции, из которых можно выйти и вернуться. Пожалуй, самая крутая из всех доработок!

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 }

Чем генераторы круты? @@

Они дают убрать лестницу колбэков! Пример лестницы (node-postgres):

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]); });

А теперь с генераторами @@

Генератор можно приостановить. Пишем обёртку и получаем coroutine:

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]); });

Промисы и async/await @@

То же, но стандартно — делается через промисы и async/await.

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));
}

Это уже не ES6 (2015), а 2016-2017; но Babel всё равно их поддерживает (и транслирует в генераторы).

Поддержка Promise @@

API с колбэками надо оборачивать. Это нетрудно, но надо знать, куда ставить колбэк:

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);
}

Нюансы с исключениями @@

Нюанс 1: Promise'ы nodejs глотают исключения

Решение — Bluebird, он бросает Unhandled rejection error

Нюанс 2: У асинхронных исключений нет вменяемого стека.

Стеки в духе:

    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)

Решение — опять-таки Bluebird + Promise.config({ longStackTraces: true }).

gen-thread @@

  • Ну, или забить пока на промисы
  • Юзать генераторы и мой gen-thread (в промисы он тоже умеет)
yield gen.p(<PROMISE>);

Но в итоге, конечно, все перейдут на Promise.

Модули ES6 @@

// lib/math.js
export function sum (x, y) { return x + y };
export var pi = 3.141593;
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default (x) => Math.exp(x);
// someApp.js
import exp, { pi, e } from "lib/mathplusplus";
console.log("e^{π} = " + exp(pi));

Оговорка: чёткой уверенности, что это лучше CommonJS, у меня нет. Но гибче, да.

Template strings @@

PHP-подобная строковая интерполяция.

let name = "John";
`Hello, ${name}`;

Кастомная интерполяция (для DSL, видимо):

get`http://example.com/foo?bar=${bar + baz}&quux=${quux}`;
// эквивалентно:
get([ "http://example.com/foo?bar=", "&quux=", "" ], bar + baz, quux);

Proxy @@

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";

Функции, локальные в блоках @@

(Block-scoped functions)

{
    function foo () { return 1 }
    foo() === 1
    {
        function foo () { return 2 }
        foo() === 2
    }
    foo() === 1
}

Итераторы для for .. of @@

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); }

Обзор инструментов @@

  • Пакетные менеджеры
  • Трансляторы
  • Сборка, преобразования и минификация
  • Отладка, профилировка
  • Редакторы, IDE

Системы модулей @@

  • AMD (RequireJS): Asynchronous Module Definition
  • CommonJS (node.js)
  • UMD (AMD+CommonJS, поддерживается и там, и там)
  • Модули ES6 (уже рассказал)

Наиболее актуальны CommonJS и ES6.

CommonJS @@

ИМХО — самый краткий и удобный синтаксис.

const pg = require('pg');
const gen = require('gen-thread');
 
module.exports.method = function() { /*...*/ };
 
// или даже:
module.exports = MyClass;
 
function MyClass()
{
}
/*...*/

AMD @@

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
    }
});

UMD @@

(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
    }
}));

Пакетный менеджер NPM @@

  • Использует систему модулей CommonJS
  • Ставит зависимости рекурсивно (node_modules/module1/node_modules/module2/…)
  • Можно их упростить: npm dedup

Пользоваться им не просто, а очень просто:

  • Установить модуль: npm install [-g] [--save] [--save-dev] module
  • Создать package.json для проекта: npm init
  • Зарегистрироваться/залогиниться: npm login
  • Опубликовать свой модуль: npm publish

Самих модулей столько, что аж страшно.

Bower @@

  • Ещё один пакетный менеджер
  • Ставится из npm O_o
  • Команды примерно те же: install, init… те же semver’ы
  • Основные отличия — AMD и плоское дерево зависимостей…
    то есть как будто вы просто сказали npm dedup
    только свалится, если что-то несовместимо по semver
⇒ Нафиг-нафиг.

Transpiler: Babel/Bublé @@

Как писать на ES6, если он не везде поддерживается?

Ответ — Babel!

  • REPL: https://babeljs.io/repl/
  • Командная строка: npm install babel-cli (babel-node)
  • browserify: babelify

Transpiler — транслятор JS в JS. Другие: Bublé, Traceur.

Трансляторы @@

В JS транслируют всё, что можно и нельзя

Сборка, упаковка и минификация @@

Актуальное:

  • browserify — упаковка npm-модулей (CommonJS) для браузера
  • webpack — универсальная система сборки (AMD / CommonJS / ES6 модулей) + CSS + картинки + whatever
  • stealjs — оптимизатор js для многостраничных сайтов
  • eslint — проверка на ошибки и стиль
  • gulp — запускатор скриптов сборки
  • uglifyjs — обфускатор

Чуть старее @@

  • grunt — запускатор скриптов сборки (часто grunt+bower)
  • bower — пакетный менеджер (рассказал выше)
  • requirejs — система загрузки и сборки AMD модулей
  • jshint — проверка на ошибки и стиль
  • yui-compressor, closure compiler — обфускаторы
  • rollup — система сборки для ES6 модулей (используется реже)

IDE, редакторы, отладка @@

  • Netbeans
    отладчик встроен
    (!) умеет живое обновление кода при отладке
  • Atom от GitHub
    сам написан на node.js + webkit (Electron)
    напоминает Sublime
    модульный; отладчик — отдельный пакет
  • Visual Studio Code
    мелкий и мягкий форк Atom’а
    встроен отладчик и поддержка typescript и C#

Отладка из консоли @@

nodejs debug app.js; Есть REPL

for (var i = 0; i < 5; i++)
{
    debugger; // брейкпоинт
    console.log(i);
}
console.log("done");

Отладка из node-inspector @@

$ 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

Node inspector ss.png

Браузерная часть @@

Ну, тут проблем нет совсем

  • F12 даже в IE
  • watchify
  • есть фокусы с live reload (react/redux)
  • chrome + netbeans connector

Профилировка @@

Тестирование @@

  • Для браузера: Karma
    Запускает обычные тесты, но в открытом приложении в браузере
  • В целом: Mocha, Chai, Sinon

Тайпчекеры: TypeScript, Flow @@

В целом очень похожи.

  • У Flow лучше вывод типов
  • У TypeScript есть public/private/protected
  • У Flow есть Nullable типы (и он автоматически проверяет на null)
  • TypeScript — надмножество JS
  • Flow — подмножество JS

TS null @@

function foo(num: number) {
    if (num > 10) {
        return 'cool';
    }
}
// cool
const result: string = foo(100);
console.log(result.toString());
// still cool?
console.log(foo(1).toString());
// error at runtime: "Cannot read property 'toString' of undefined"

Flow null @@

function foo(num: number) {
    if (num > 10)
        return 'cool';
}
 
// error: call of method `toString`.
// Method cannot be called on possibly null value
console.log(foo(100).toString());
 
// error: return undefined. This type is incompatible with string
function foo(num: number): string {
    if (num > 10)
        return 'cool';
}

TS vs Flow: generics @@

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;
}

TS vs Flow: присваивание элементов @@

OK в обоих

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'));

TS vs Flow: присваивание коллекций @@

// 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

Обзор фреймворков @@

  • Клиентские фреймворки «старые»
  • Клиентские фреймворки «новые»
  • Попытки мобильной разработки
  • Попытки игровых фреймворков

«Старые» @@

Заслуживают упоминания:

  • jQuery (фиг сдохнет, а надо бы)
  • ExtJS (тоже фиг сдохнет)
  • R.I.P: Yahoo UI, PrototypeJS
  • По-моему, сдыхает: Dojo

jQuery @@

«Меня зовут Мистер Свинья!»

Если скриптота — рас***дяйство, то jQuery — ВЕРХ рас***дяйства

Ибо РАСПОЛАГАЕТ к плохому коду:

  • Всё в кучу: объект-бог с кучей хелперов $ и такие же модули (например, DataTables)
  • $.extend(), $.ajax(), $("<html>"), $(".class")
  • Селекторы / операции над множествами: $(".class").each(e => $(e).hide())
    если элементов не найдётся, no problem, ничего не сдохнет
  • Страшные контроллеры, гвоздями прибитые к UI
  • Отсутствие возможностей оптимизации при динамической сборке UI
    Пример — WikiEditor

ExtJS @@

  • Desktop-like фреймворк, умеет кучу всего
  • Фундаментально тормозноват
  • Со стилизацией большие трудности
    Ну хоть тема в ExtJS >= 4 стала норм, можно смотреть без рвотных позывов
  • По моему опыту — писать на нём НЕ проще и НЕ быстрее, чем на HTML+JS
  • Data binding есть, но ограниченный — только свойств (а не иерархии) компонентов

«Новые» (компонентные) @@

  • Data binding
    Knockout, Ember, Backbone
    упомянем для истории; неактуальны
    AngularJS 1, 2
  • Virtual DOM
    React

И те, и другие — по сути, решают задачу шаблонизации на JS.

AngularJS 1 @@

  • Dirty checking
  • Все биндинги 2-way
  • Поэтому медленный
  • Есть компоненты («директивы») и контроллеры
  • На этом уже можно писать

Пример @@

<div ng-controller="LoanCalculator">
<div>
   Дата выдачи: <input name="" type="text" ng-model="props.start" /><br />
   Сумма: <input name="" type="text" ng-model="props.total" /><br />
   Процент: <input name="" type="text" ng-model="props.percent" /><br />
   Срок: <input name="" type="text" ng-model="props.months" /> месяцев<br />
   Штраф за просрочку: <input name="" type="text" ng-model="props.fine" /><br />
   Пеня % годовых на просрочку: <input name="" type="text" ng-model="props.penaltyPercent" /><br />
   <input type="button" value="Рассчитать" ng-click="recalc()" />
   <input type="button" value="Сбросить" ng-click="clear()" />
</div>
<table ng-if="payments.length" border="0">
<tr>
   <th>Дата</th>
   <th>Сумма</th>
   <th>Комментарий</th>
</tr>
<tr ng-repeat="payment in payments">
   <td><input name="" type="text" ng-model="payment.date" ng-change="clear_from($index+1)" /></td>
   <td><input name="" type="text" ng-model="payment.total" ng-change="clear_from($index+1)" /></td>
   <td>{{payment.text}}</td>
</tr>
<tr ng-if="clean">
   <th>Всего</th>
   <td>{{sum}}</td>
   <td>{{outsums}}</td>
</tr>
</table>
</div>

Angular 2 @@

  • Пофиксили скорость, разделив 1-way и 2-way биндинги
  • Перешли на TypeScript и всех агитируют
  • Обновились в целом
  • Больше «искаропки»
  • Но зато тяжелее. Зачем-то тянет за собой RxJS…

React: почему я за React? @@

  • JSX: на первый взгляд странно, на самом деле — круто и удобно!
  • Строго однонаправленный поток данных
    (2-way binding — как чуть сложнее, так проблема)
  • Правильная компонентность
    Убирает все сомнения в том, MV-что у нас — MVVM, MVC, M-V-VC, M-V-VC-VM…
    Контроллер занимается тем, чем и должен: работой с данными
  • Легковесный, простой, изящный. Учится за 1 вечер. Не принуждает к дополнительным компонентам.

Почему JSX — круто? @@

Даёт писать шаблоны прямо на JS! Удобно и безопасно.

Например, цикл:

  • Ember: {{# each}}
  • Angular 1: ng-repeat
  • Angular 2: ngFor
  • Knockout: data-bind="foreach"
  • React: ПРОСТО ИСПОЛЬЗУЙТЕ JS :) (обычный for или Array.map())
Angular 2 continues to put «JS» into HTML. React puts «HTML» into JS.

JSX @@

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>
    }
});

JSX и Virtual DOM @@

  • Как применять изменения быстро?
    (DOM — относительно медленный)
  • Храним копию DOM в виде json
  • После render() сравниваем
  • Изменения применяем к реальному DOM
⇒ Быстро и при этом гибко

Компоненты React @@

  • Обязательный метод ОДИН: render()
  • props и state (XML-атрибуты и внутреннее состояние)
  • children (тело элемента)
  • getDefaultProps(), getInitialState()
  • setState(). нет setProps() и setChildren()! (получает при render родителя)
  • componentWillMount(), componentDidMount(), componentWillUnmount()
  • componentWillReceiveProps(p), shouldComponentUpdate(p, s)
  • componentWillUpdate(p, s), componentDidUpdate(p, s)
  • propTypes, mixins, statics

А вот почему это красиво @@

ExtJS. Класс «панель». Где тут делать инициализацию?

Extjs-events.png

beforerender? render? afterrender? afterlayout? added? add? show? спойлер: нигде

Библиотеки для React @@

  • Контроллер: Flux
  • Или Redux (2 кб!)
  • react-router
  • react-router-redux
  • HTTP? superagent
  • Draft.js (rich editor)

О Redux @@

  • «State container»
  • Однонаправленный поток данных:
    Store → Компоненты → Действия → Диспетчер → Store
  • Компоненты «привязываются» к данным в Store
  • И к вызовам действий
  • Действие = reducer: (Состояние, Действие) → (НовоеСостояние).
→ Очень чёткое отделение контроллера

Как писать на React @@

Thinking in React

  1. Набросать (или взять у дизайнеров) HTML-макет
  2. Выделить компоненты
    Помним, что компонент — единица рендера
  3. Сделать статическую версию на React, всё через props
  4. Выделить состояние, определить владельца состояния
    Можно уже брать Flux/Redux
    Presentational vs Controller
  5. Научить менять состояние

Прочее @@

  • Мобильное
    • React Native: Virtual DOM над нативными компонентами
    • NativeScript: Тоже JS в отдельном потоке и свой XML-язык описания UI
    • Уверен, появятся ещё
  • https://html5gameengine.com/
  • WebGL: SceneJS, OSG.JS, Blend4Web

Демонстрация LikeOpera @@ %%

Клон Opera Mail на React и node.js

Вопросы @@ %%

?