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

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

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for Ulric Wilfred 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

Avatar for Ulric Wilfred

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(); Нет смысла использовать его в циклах.