Slide 1

Slide 1 text

shaman.sir, Animatron, 2012 Постигаем функциональный JavaScript (супер-дупер лекция, которая сделает тебя победителем) (особенно, если ты будешь умничкой)

Slide 2

Slide 2 text

? (ты пребываешь в тихом безмыслии и не знаешь, что спросить) (что ж, тогда я расскажу тебе, как пройдёт лекция) Обо мне. Структура функции. Лямбды. Именованные функции. Вызов по месту. this. call и apply. Распостранённые ошибки и трюки. Отложенные функции. Частичные применения. Композиции функций. Объекты, с точки зрения функций.

Slide 3

Slide 3 text

Кто же этот парниша? (в смысле я) Я — shaman.sir, он же Котенко Антон, опытный веб-разработчик на JavaScript. Моя домашняя страничка находится по адресу http://shamansir.github.com, а мой блог — по адресу http://shamansir.github.com/blog; мой e-mail таков: [email protected].

Slide 4

Slide 4 text

Что есть функция? Итак,..

Slide 5

Slide 5 text

аргументы тело результат Что есть функция?

Slide 6

Slide 6 text

Как она выглядит в JavaScript? function(x, y, z) { return ω; }

Slide 7

Slide 7 text

На самом деле, этот пример сам является лямбдой (λ), функцией без имени, вызванной на месте... function(x, y, z) { return ω; }

Slide 8

Slide 8 text

function(x, y, z) { return ω; } На самом деле, этот пример сам является лямбдой (λ), функцией без имени, вызванной на месте...

Slide 9

Slide 9 text

Вот несколько именованных (напротив) вариантов... var foo = function(x, y, z) { return ω; } function foo(x, y, z) { return ω; } bar.foo = function(x, y, z) { return ω; } bar.prototype.foo = function(x, y, z) { return ω; }

Slide 10

Slide 10 text

var foo = function(x, y, z) { return ω; } function foo(x, y, z) { return ω; } bar.foo = function(x, y, z) { return ω; } bar.prototype.foo = function(x, y, z) { return ω; } Вот несколько именованных (напротив) вариантов...

Slide 11

Slide 11 text

...И как их можно вызвать. foo(x, y, z); foo(x, y, z); bar.foo(x, y, z); new bar().foo(x, y, z);

Slide 12

Slide 12 text

var a = foo(x, y, z); var a = foo(x, y, z); var a = bar.foo(x, y, z); var a = new bar().foo(x, y, z); ...И как их можно вызвать.

Slide 13

Slide 13 text

С помощью лямбды, ты можешь передавать одну функцию в любую другую.

Slide 14

Slide 14 text

С помощью лямбды, ты можешь передавать одну функцию в любую другую. function(x, y, z) { return ω; } function filter(xs, f) { ... f(xs[i]) ... } filter([...], function(x, y, z) { return ω; });

Slide 15

Slide 15 text

Ты можешь вызвать лямбду на месте, (function(x, y, z) { return ω; })(25, 6, 11) передав ей реальные значения

Slide 16

Slide 16 text

Ты можешь вызвать лямбду на месте, (function(x, y, z) { return x+y+z; })(25, 6, 11) ➟ 42 передав ей реальные значения

Slide 17

Slide 17 text

Ты можешь вернуть фунцию из лямбды и вызвать её потом

Slide 18

Slide 18 text

(function(z, a, b) { var x = a + b; return function(y) { return x + y + z; }; })(11, 20, 5)(6); ➟ 42 Ты можешь вернуть фунцию из лямбды и вызвать её потом

Slide 19

Slide 19 text

Любая функция может быть вызвана с определённым состоянием this var foo = function(z) { return this.x+this.y+z; }; foo.call({x: 25, y: 6}, 11); ➟ 42 foo.apply({x: 25, y: 6}, [11]); ➟ 42 Array.prototype.push.call({length: 6}, 'a'); ➟ { length: 7, 6: 'a' }

Slide 20

Slide 20 text

Любая функция может быть вызвана с определённым состоянием this var foo = function(z) { return this.x+this.y+z; }; foo.call({x: 25, y: 6}, 11); ➟ 42 foo.apply({x: 25, y: 6}, [11]); ➟ 42 Array.prototype.push.call({length: 6}, 'a'); ➟ { length: 7, 6: 'a' }

Slide 21

Slide 21 text

И даже лямбда! (function(z) { return this.x+this.y+z; }).call({x: 25, y: 6}, 11); ➟ 42

Slide 22

Slide 22 text

Из этого можно сделать вывод, что значение this всегда зависит от места, в котором ты делаешь вызов

Slide 23

Slide 23 text

Новая область видимости создаётся только для функций, не для блоков Так что никаких вложенных областей для if-ов, for-ов и всякого такого.

Slide 24

Slide 24 text

Частая ошибка for (var i = 0; i < 42; i++) { other_func(..., function() { console.log(i); }); }); По своей лямбда-природе, внутренняя функция может быть вызвана в любой момент после выполнения цикла — она может быть не вызвана вообще или быть вызвана несколько раз, но только other_func знает, когда поскольку твоя переменная i является всего лишь указателем на значение, независимо от момента, когда лямбда будет вызвана, оно уже будет содержать 42

Slide 25

Slide 25 text

Частая ошибка for (var i = 0; i < 42; i++) { other_func(..., (function(actual_i) { return function() { console.log(actual_i); }); })(i)); }); Таким образом ты создаёшь оборачивающую лямбду, которая вызывается сразу и сохраняет текущее значение i в некую переменную, скажем actual_i решение

Slide 26

Slide 26 text

Теперь переходим к интересной части

Slide 27

Slide 27 text

Отложенные функции

Slide 28

Slide 28 text

Нужно достичь 'foofoobarxbee' parse( twice(str('foo')), str('bar'), any_char(), str('bee') );

Slide 29

Slide 29 text

pipe( process('foo.sh'), process('bar.sh'), stream_to('file.text') ); (синхронный, один вызов за другим, на данный момент, но асинхронная версия грядёт в этой самой презентации) Нужно достичь

Slide 30

Slide 30 text

Способ function twice(f) { var chunk = f(); return (search(str, pos, chunk) && search(str, pos+chunk.length, chunk); } twice = def(twice);

Slide 31

Slide 31 text

Инструмент function def(f) { return function() { return (function(f, args) { return function() { return f.apply(null, args); }; })(f, arguments); } }

Slide 32

Slide 32 text

Что происходит?

Slide 33

Slide 33 text

Пример с парсером http://codepen.io/shamansir/pen/HskmE

Slide 34

Slide 34 text

Другой пример http://codepen.io/shamansir/pen/kBzJe

Slide 35

Slide 35 text

Частичные применения

Slide 36

Slide 36 text

Нужно достичь foo(a, b, c) == foo(a)(b, c) (ты прав, если сейчас думаешь о встроенной Function.bind — здесь мы её по-своему имплементируем)

Slide 37

Slide 37 text

Способ var foo_c = bind(foo), foo_c_ctx = ctx_bind(foo); foo_c(1, 4, 5)(2, 3, 7) foo_c_ctx(1, 4)(2, 3, 8)({ foo: 'bar' });

Slide 38

Slide 38 text

Инструмент function bind(f) { return function() { return (function(args_p1) { return function() { return (function(args_p2) { return f.apply(null, as_arr(args_p1).concat(as_arr(args_p2))); })(arguments); }; })(arguments); }; }

Slide 39

Slide 39 text

Инструмент function ctx_bind(f) { return function() { return (function(args_p1) { return function() { return (function(args_p2) { return function(ctx) { return f.apply(ctx || null, as_arr(args_p1).concat(as_arr(args_p2))); } })(arguments); }; })(arguments); }; }

Slide 40

Slide 40 text

Что происходит?

Slide 41

Slide 41 text

Пример http://codepen.io/shamansir/pen/xCrgz

Slide 42

Slide 42 text

Цепочки функций

Slide 43

Slide 43 text

Нужно достичь queue(load_file)( ['file1', 'file2', 'file3'] ); (этот вариант выглядит асинхронным и он действительно вполне может быть асинхронным, каждый вызов может быть таковым, и все они вызываются в указанном строгом порядке)

Slide 44

Slide 44 text

Способ function load_file(name, next) { setTimeout(function() { .... next(/* file */); }, 1000); }

Slide 45

Slide 45 text

Инструмент function queue(f) { return function(values) { function __next() { if (!values.length) return; f(values.shift(), __next); } __next(); } } (мы опускаем обработку ошибок, чтобы предоставить тебе более чистый взгляд на происходящее, но ты можешь свободно получить on_error или on_complete вместе с f и передать их внутрь через __next)

Slide 46

Slide 46 text

Что происходит?

Slide 47

Slide 47 text

Пример http://codepen.io/shamansir/pen/AaHqy

Slide 48

Slide 48 text

Композиции функций

Slide 49

Slide 49 text

Нужно достичь compose(open_file, read_file, show_file, close_file)('test.file'); (эта передаёт в каждую функцию коллбэк в виде комбинированных последующих вызовов, что позволяет каждой из них решить, вызывать его по завершению асинхронного или синхронного процесса)

Slide 50

Slide 50 text

Нужно достичь compose(open_file, read_file, show_file, close_file)('test.file'); (а ещё она передаёт каждый предшествующий результат в следующую функцию, вот почему она такая МОЩНАЯ)

Slide 51

Slide 51 text

Способ function open_file(name, callback) { ... // callback may be called asynchronously } function read_file(handle, callback) { ... // callback may be called asynchronously } function show_file(contents, callback) { ... // callback may be called asynchronously } function close_file(handle, callback) { ... // callback may be called asynchronously }

Slide 52

Slide 52 text

Инструмент function compose() { var fs = arguments, fl = fs.length, fi = fl, stack = []; if (!fi) throw new Error('Please pass some functions'); while (fi--) { stack[fi] = (function(f, cb) { return function(res) { f(res, cb); } })(fs[fi], stack[fi+1] || null); } return function(initial) { stack[0](initial); } }

Slide 53

Slide 53 text

Что происходит?

Slide 54

Slide 54 text

Пример http://codepen.io/shamansir/pen/Funwt

Slide 55

Slide 55 text

Если вам нужны генераторы в JS, (да, те самые, которые с yield) обращайтесь к И если вы посмотрите в исходный код, то сможете уложить даже их в пару строк. Lyfe.js

Slide 56

Slide 56 text

о библиотеке Lyfe.js Если вам нужны генераторы в JS, (да, те самые, которые с yield) обращайтесь к И если вы посмотрите в исходный код, то сможете уложить даже их в пару строк.

Slide 57

Slide 57 text

Большинство функциональных приёмов занимают несколько строк Возможно, в этих случаях вам не нужны специальные библиотеки.

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

Теперь, когда ты постиг функции, ты можешь начинать постигать объекты. Просто считай их состояниями или экземплярами и никогда (пожалуйста, никогда) не назначай методов объектам, которые ты в будущем собираешься обходить циклами.

Slide 60

Slide 60 text

Объект с данными — это хэш. { val1: 3, val2: '17', val3: function... } Такие объекты ты сможешь спокойно обойти в цикле. (и такие объекты легко друг с другом смешивать)

Slide 61

Slide 61 text

Объект, созданный по некоему прототипу — это специальный экземпляр function A() { this.a = smth; } A.prototype.method = function() {} var foo = new A(); Нет смысла использовать его в циклах.

Slide 62

Slide 62 text

И не забывай об утках.

Slide 63

Slide 63 text

Конец. Все рисунки нарисованы shaman.sir при помощи iOS-приложения Paper. А кто слушал — молодец.

Slide 64

Slide 64 text

Вопросы.