Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Постигаем функциональный JavaScript

Ulric Wilfred
December 12, 2012

Постигаем функциональный JavaScript

Лёгкое (я надеюсь), но подробное (я верю), введение в функции JavaScript. Пара сниппетов, пара живых и хороших примеров, вам понравится.

Ссылки на примеры:
Отложенные функции: http://codepen.io/shamansir/pen/HskmE, http://codepen.io/shamansir/pen/kBzJe
Частичные приложения: http://codepen.io/shamansir/pen/xCrgz
Очереди функций: http://codepen.io/shamansir/pen/AaHqy
Композиции функций: http://codepen.io/shamansir/pen/Funwt

Lyfe.js: http://bitbucket.org/balpha/lyfe
Статья о lyfe.js: http://balpha.de/2011/06/introducing-lyfe-yield-in-javascript

Ulric Wilfred

December 12, 2012
Tweet

More Decks by Ulric Wilfred

Other Decks in Programming

Transcript

  1. ? (ты пребываешь в тихом безмыслии и не знаешь, что

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

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

    без имени, вызванной на месте... function(x, y, z) { return ω; }
  4. function(x, y, z) { return ω; } На самом деле,

    этот пример сам является лямбдой (λ), функцией без имени, вызванной на месте...
  5. Вот несколько именованных (напротив) вариантов... 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 ω; }
  6. 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 ω; } Вот несколько именованных (напротив) вариантов...
  7. ...И как их можно вызвать. foo(x, y, z); foo(x, y,

    z); bar.foo(x, y, z); new bar().foo(x, y, z);
  8. 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); ...И как их можно вызвать.
  9. С помощью лямбды, ты можешь передавать одну функцию в любую

    другую. function(x, y, z) { return ω; } function filter(xs, f) { ... f(xs[i]) ... } filter([...], function(x, y, z) { return ω; });
  10. Ты можешь вызвать лямбду на месте, (function(x, y, z) {

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

    return x+y+z; })(25, 6, 11) ➟ 42 передав ей реальные значения
  12. (function(z, a, b) { var x = a + b;

    return function(y) { return x + y + z; }; })(11, 20, 5)(6); ➟ 42 Ты можешь вернуть фунцию из лямбды и вызвать её потом
  13. Любая функция может быть вызвана с определённым состоянием 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' }
  14. Любая функция может быть вызвана с определённым состоянием 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' }
  15. Новая область видимости создаётся только для функций, не для блоков

    Так что никаких вложенных областей для if-ов, for-ов и всякого такого.
  16. Частая ошибка for (var i = 0; i < 42;

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

    i++) { other_func(..., (function(actual_i) { return function() { console.log(actual_i); }); })(i)); }); Таким образом ты создаёшь оборачивающую лямбду, которая вызывается сразу и сохраняет текущее значение i в некую переменную, скажем actual_i решение
  18. pipe( process('foo.sh'), process('bar.sh'), stream_to('file.text') ); (синхронный, один вызов за другим,

    на данный момент, но асинхронная версия грядёт в этой самой презентации) Нужно достичь
  19. Способ function twice(f) { var chunk = f(); return (search(str,

    pos, chunk) && search(str, pos+chunk.length, chunk); } twice = def(twice);
  20. Инструмент function def(f) { return function() { return (function(f, args)

    { return function() { return f.apply(null, args); }; })(f, arguments); } }
  21. Нужно достичь foo(a, b, c) == foo(a)(b, c) (ты прав,

    если сейчас думаешь о встроенной Function.bind — здесь мы её по-своему имплементируем)
  22. Способ 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' });
  23. Инструмент 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); }; }
  24. Инструмент 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); }; }
  25. Нужно достичь queue(load_file)( ['file1', 'file2', 'file3'] ); (этот вариант выглядит

    асинхронным и он действительно вполне может быть асинхронным, каждый вызов может быть таковым, и все они вызываются в указанном строгом порядке)
  26. Инструмент function queue(f) { return function(values) { function __next() {

    if (!values.length) return; f(values.shift(), __next); } __next(); } } (мы опускаем обработку ошибок, чтобы предоставить тебе более чистый взгляд на происходящее, но ты можешь свободно получить on_error или on_complete вместе с f и передать их внутрь через __next)
  27. Нужно достичь compose(open_file, read_file, show_file, close_file)('test.file'); (эта передаёт в каждую

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

    каждый предшествующий результат в следующую функцию, вот почему она такая МОЩНАЯ)
  29. Способ 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 }
  30. Инструмент 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); } }
  31. Если вам нужны генераторы в JS, (да, те самые, которые

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

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

    Просто считай их состояниями или экземплярами и никогда (пожалуйста, никогда) не назначай методов объектам, которые ты в будущем собираешься обходить циклами.
  34. Объект с данными — это хэш. { val1: 3, val2:

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

    A() { this.a = smth; } A.prototype.method = function() {} var foo = new A(); Нет смысла использовать его в циклах.