Pull to refresh

Comments 54

То же самое, что и выводом типов ;) Я хочу заинтересовать людей, чтобы они начали смотреть по сторонам. Ступившие на скользкую дорожку функционального программирования рано или поздно знакомятся с Haskell. И ленивостью, и pattern matching, и монадами с функторами. И другими страшными словами.
Небольшое замечание: вместо того, чтобы добавлять обертку вокруг jQuery.ajax можно было бы сделать аналогичное на базе jQuery.Deferred. Собственно вызов jQuery.ajax и возвращает jQuery.Deferred. Правда тогда бы было труднее показать преимущества map/reduce :)
а чем не угодил такой кусок:
for (key in arr){ return arr[key]; }
и зачем надо было городить foreach (через численный), который в принципе реализован нативно?
Много просто лишено смысла в реализации: все сводится к тому что «мне красиво и нравится, поэтому функционального программирования тут нет»
Не универсальное решение.
В чистом JS это сработает, но некоторые фреймворки могут добавить свой функционал в Array.prototype и тогда при итерировании вы этот атрибут тоже обойдете.
У автора мешанина JQuery и чистого с последующим сравнением того, что сравнивать, по сути, нельзя.
Добавление в Array.prototype атрибутов, которые можно обойти с помощью for(key in arr) — феерический бред, т.к. есть объекты, а есть массивы.
Не понял про бред, поясните.

Array.prototype.newProp = "newprop"
var b = new Array();
b.push(0);
b.push(1);
for (var i in b) console.log(b[i]);

Выводит

0
1
newprop
Я про то, что надобность данных структур по-моему сильно преувеличена.
Массив данных должен являться массивом данных, а не объектом хранения дополнительных структур, функций и т.д., т.к. для этого есть объекты.
Если использовать, например, тот же es5-shim, то в старых браузерах код будет работать некорректно
Возможно я не прав.
Это в идеале.
А реальности можно словить че нить интересное.
Например в старых версия IE (не знаю как сейчас), у массива не было функции indexOf, и некоторые для исправления этой проблемы поступали очень просто:

if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(item) {
    ...
  }
}
Вероятно этим и не угодил:
var a = [1, 2], i;
Array.prototype.someFunc = function() {};
for (i in a) {
    console.log(i, ' > ' , a[i]);
}


P.S. То есть надо деллать дополнительную проверну на a.hasOwnProperty(i)
P.P.S. Насколько я помню, конструкция с in не гарантирует верную последовательность перебора. Она может быть такой исключительно по совпадению.
for(var i =0; l< arr.length; i<l; ++i){
    console.log(arr[i]);
}


А не так должно быть?

for(var i=0, l = arr.length; i<l; ++i){
    console.log(arr[i]);
}


И так в нескольких местах оператору for 4 терма передается.
По поводу ФП на javascript — все хорошо, но количество слов function иногда задалбывает

Лично мне приятнее видеть, когда асинхронные запросы выполняются в виде. Не знаю, как остальным, так скобок нет грядки в конце выражения

SomeAsyncronousController.SomeMethodAsync(args).then(function(callback) { });
Думаю, стоит упомянуть, что ни map, ни reduce, ни forEach не работают в IE < 9.
Есть куча библиотек, которые либо расширяют стандартные объекты, либо вводят подобный функционал через внешние объекты, например, underscore.js
SugarJS тут больше подходит. underscore.js немного портит выразительность.
А Sugar портит методами, не описанными в специффикации.
Да, не работают, но чукча не писатель, чукча переводчик :)

Внизу кстати правильно подсказали про underscore.js.
Или es5-shim для совместимости.
Очень интересно, однако я смог осилить ровно половину. Через пол годика приду дочитывать.
Это перевод 45-минутной презентации. Полгодика на один школьный урок? Я думаю вы лукавите )
Возможно. В новом для меня мире программирования время течет абсолютно непредсказуемо.
В JavaScript для функционального программирования важно понимать несколько ключевых особенностей языка:
1. функции — вызываемые объекты (их можно возвращать и передавать)
2. функция не проверяет тип и кол-во аргументов в нее переданных (имена аргументов формальны)
3. каждая функция образует лексическое замыкание (и это замыкание путешествует с ней)
4. this и прототипы

Дальше все прочее можно написать уже исходя из потребностей. Можно даже таких слов как «Функции высшего порядка» и «Частичное применение функций» не знать :)
Да, эти нюансы очень важны, но их редко где можно встретить описанными вместе. Да еще и в контексте функционального программирования. На ум приходит только Secrets of Javascript Ninja.

По поводу производительности функций:
— Браузеры оптимизируют итераторы (сворачивают функции) так что ваша функция формально может и не вызываться (если вы, конечно, не делаете нечто извращенное и оптимизация не проходит). Так что пишите .map() .forEach() для холодных и теплых циклов — берегите глаза от for ;-)
— Наследование через замыкания дороже прототипного наследования (Scope функции может создаваться каждый раз при вызове функции). Опять же это заметно только в очень горячих функциях.
Что есть наследование через замыкания?
Ох я уж с этим наследованием наговорил чего-попало. Хотел сказать конструкторы через фактори дороже обычных через new (но не фатально).
var createSmth = function (myPrivate) {
    myPrivate = myPrivate || 0;

    return {
        setMyPrivate: function (val) {
            myPrivate = b;
        } 
    };
};

// или что-то такое

function makeF() {
  var x = 11;
  var y = 22;
  return function (what) {
    switch (what) {
      case "x": return x;
      case "y": return y;
    }
  }
}

var f = makeF();
f("x");

В общем всякие фактори функций и куча вложенных функций это дороже обычного new и нескольких неизменных функций.
А это:
var totalLength = buffers.
    map(function (buffer) {return buffer.length; }).
    reduce(function(sum, curr){return sum+curr;}, 0);

Разве нельзя заменить на:
var totalLength = buffers.
    reduce(function(sum, buffer){return sum + buffer.length;}, 0);
?
Конечно можно. Но чем проще каждый шаг, тем проще понимать, что происходит. Особенно для тех, кто не сильно знаком с reduce.

Можно ведь и
function combine(scripts, callback){
    async.map(scripts, ajax, function(contents){
        callback(contents.join(""));
    });
}


заменить на

function combine(scripts, callback){
    async.map(scripts, ajax, function(contents){
        callback(contents.reduce(function(result, current) { return result + current},""));
    });
}


Чтобы совсем функцоинально. Но будет ли понятнее? :)
Ваш пример вообще не коррелирует с тем, что я предложил.
Вы предлагаете расписать один шаг так, как он там внутри представляется, я же предложил два шага в один записать.
Разделение на map и reduce было сделано специально. Давайте представим, что buffer.length не просто buffer.length, а какое-то тяжелое вычисление. Которое надо раскинуть на кластер машин для ускорения.

В случае с явно выделенным map это будет просто и писать и читать. Если же все запихнуть в один reduce- это будет сложнее для восприятия.

Композиция функций поэтому и получилась такой мощной, что сначала мы разделили все на элементарные блоки. Их проще писать, их проще понимать, их проще тестировать.
>>Под циклом будем понимать стандартное лобовое решение. Примерно такое
>>for(var i =0; i
Парсер пожрал.
>>Под циклом будем понимать стандартное лобовое решение. Примерно такое
>>for(var i = 0; i < n; ++1){

В очередной раз убедился, что на js может писать только «исполин духа и корифей».
Ах да. Плохо, что нельзя удалять комментарии.
Для полного погружения в функциональное программирование на JavaScript:


И еще один интересный пост про оптимизация хвостовой рекурсии: Tail call optimization on it’s way… Who gives a frak?!
7uiip[poop
'p[;
yiio
gthhhjkipk kooo,l,k,p'\
Спешу заметить, что пост начинается в ошибочного примера (с функцией add). Говорится, что «Можно тот же код записать по-другому.». На самом деле эти формы записи имеют достаточно серьезные отличия, и, вообще говоря, работают по-разному. Объяснение которых уходит корнями спецификацию языка. О этих отличиях, кстати, любят спрашивать на собеседованиях. Кому интересно разобраться в данном вопросе, рекомендую почитать про Function Statement и Function Definition, например у Дмитрия Сошникова: dmitrysoshnikov.com/ecmascript/ru-chapter-5-functions/

> var str = «Mentioned by » + tweeps.map(prop («name»)).join(", ");

Есть очень интересная вещь под названием «вызов по имени» (в RightJS, кстати, используется), позволяет заменить .map(prop(«name»)) на .map(«name»).
Собственно, реализацию этой возможности можно уместить ровно в одну строку.
Главное понимать, когда строка это свойство, когда — название функции, а когда — просто строка.
Писал библиотеку для glomper.com реализующую функциональный подход при разработке «богатых приложений» на js github.com/freeart/brisk
> С чем тут могут быть проблемы? С областями видимости Javascript. В этом языке область видимость не поблочная, а функциональная. то есть все 3 функции будут видеть одно и то значение переменной i. Поскольку цикл отработает раньше, чем придут ответы от сервера, все три функции будут работать с i == 3;
Просто замените var на let.
va ul = cull.dom.el("ul", className:"bands"},
                    cull.dom.el("li", "Tom Waits"));

очепятка, нету {
Статья настолько простая, что уже сложная.

«Порадовала» масса опечаток.
Неплохо бы что-то сделать с ясностью формулировок:

Одним из больших плюсов Javascript является то, что функции в нем являются полноценными объектами. Настоящие First Class Citizen.
В отличие, например от Java, где функция отдельно от объекта существовать не может.

По факту сказано — в JS функция является объектом, (то есть фактически, отдельно от объекта существовать не может), в отличие от Java где отдельно от объекта существовать не может...
Хотелось бы обратить внимание, что замена

for (var i =1; l = arr.length; i< l; ++i){
}

на foreach.

далеко не эквивалентна в общем случае. Стандарт языка не гарантирует что порядок вывода будет совпадать в обоих конструкциях, более того в разных реализациях (разных браузерах) присутствуют собственные эффекты.
Ну тут надо понимать, что пытаемся сделать

Я бы и foreach не использовал, а брал бы map/fold функции или их аналоги.
Only those users with full accounts are able to leave comments. Log in, please.