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);
Нужно достичь
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.
А кто слушал — молодец.