Pull to refresh

Comments 32

Спасибо. Из статьи для себя подчеркнул три важных вещи:
1) группировать по функционалу
2) условия if(err) reject
3) примеры со стрелочными функциями и ненужными переменными.
ES5 тяжело давался всегда, поэтому ES6 освоил быстро и с большим удовольствием!
На счёт arrow функций:

1. Стоит помнить, что если собираетесь поддерживать IE 11 или Safari до 10й ветки, то использовать их не получится. Ещё некоторые браузеры под Android могут подсунуть свинью, включая довольно популярный UC Browser. Opera Mini тоже всё ещё используют (~3%!) и там их тоже нет. Если тебе не интересны 10~15% владельцев мобильных телефонов, то можешь использовать arrow-функции. Иначе стоит подождать, пока их поддержку сделают как минимум в UC Browser.

2. Они не инициализируют this до момента вызова. Это можно применить для создания функций, обращающихся к this в теле конструктора класса, созданного на основе другого класса, и до вызова super в конструкторе наследуемого класса (в наследуемом классе нельзя вызывать this до вызова super — кидает ошибку). Хотя зачем так вообще может понадобиться сделать для меня загадка и, наверное, лучше так не делать.

3. Данный тип функций невозможно использовать в качестве конструктора. У них даже prototype нет, чтоб это было возможно.

4. В теле этих функций нельзя использовать arguments, new.target и super. Все они наследуются из контекста вызова функции. Последние два в них вообще бессмысленно вызывать в виду пункта 3. Если умудритесь сделать все функции arrow-функциями, то попытка обратиться к arguments и вовсе приведёт к ошибке так-как
эта переменная не будет создана ни в одном контексте. В любом случае лучше не используйте arguments внутри arrow-функций если только не хотите умышленно запутать свой код.
И да, arrow-функции фактически наследуют this из родительской функции:
(function(){(function(){console.log(this)})();(()=>console.log(this))();}.bind({}))()

Выдаст в консоли:
Window {...}
Object {}
ИМО:
Насчет стиля кода по ES6, тут как по мне, нужно в меру. Мало того, что некотый «сахар» дороже в вызове, дак еще и уменьшает читаемость в некоторых случаях, особенно, когда код превращается в микс из букв и специальных символов.
Не всегда выпиливание «лишних» переменных способствует восприятию. Когда видишь только агрегирование данных, без промежуточных переменных, куда сложнее держать в голове всю цепочку действий, тогда как с промежуточными переменными легче понимать что творится в коде. Сразу же, сюда — о пункте про if(!value) — чтобы оперировать с отдельной инвертированной переменной, ее нужно объявить, что противоречит пункту о ненужных переменных. Как то автор(не перевода) не смог себе не противоречить.
В остальном конечно все здраво, хотя есть парочка примеров, не сильно вписывающихся.
Когда видишь только агрегирование данных, без промежуточных переменных, куда сложнее держать в голове всю цепочку действий, тогда как с промежуточными переменными легче понимать что творится в коде.

Не холивора ради, но таки подобные задачи еще лучше решаются через декомпозицию. Если ваша функция состоит из нескольких шагов, которые совершенно очевидно делятся на части пресловутыми промежуточными переменными, то вы очевидно сможете разбить ее на несколько функций, каждая из которых будет удовлетворять условию.
То есть переменные бывают ненужными, а функции нет? Превратить десяток «длинных» функций в сотню однострочных — нормально?
Все бывает ненужным, даже весь код целиком.
Декомпозиция сама по себе не подразумевает вынесение каждой строчки в отдельную функцию, вы просто доводите мой пример до абсурда и он, разумеется, теряет свой смысл.

Смотрите, допустим у нас есть некоторых размеров функция, в которой присутствует несколько локальных переменных. Насколько я понял, автор оригинальной статьи называет «лишними» те переменные, от объявления которых можно избавиться, никоим образом не повлияв на рантайм приложения.

Например:
var buzz = function () {
  var data = getSomeData();
  var result = doSomething(data);
  return result;
}


Некоторые линтеры сами предложат заменить это на
var buzz = function () {
  return doSomething(getSomeData());
}


А в каком-нибудь абстрактном функциональном языке это будет выглядеть и вовсе так:
buzz = getSomeData * doSomething


Но за этими getSomeData и doSomething вовсе не обязана стоять одна строчка кода (в таком случае ее и так можно заинлайнить, наверное).

Опять же, насколько я понял, автор любит функциональное программирование, а во многих функциональных языках переменные отстутсвуют в принципе. Если себя «программировать» под их регулярное использование, то изучение этих языков может стать большой проблемой.
Почему я полюбил «лишние» переменные.
Потому что сегодня result сразу возвращается как есть. А завтра он будет обработан и заброшен в структуру, которая и будет возвращаться. Поэтому для себя я принял правило — если в каком-то результате есть смысл, его лучше сохранить. Компилятор сам потом всё оптимизирует, а вот код рефакторить будет проще.
А еще можно поставить бряку и увидеть, какой результат в него записался.
Да, и отладка, разумеется.
А вынесение подобных результатов в отдельные абстракции (напомню, что я говорил именно про это, а не про вред промежуточных переменных) позволит вам также покрыть это тестами, что значительно упростит и рефакторинг, и дебаг, и поддержку кода.
К сожалению, это слишком часто доходит до ситуации, когда каждое выражение оборачивается в отдельную функцию, а половина из них еще и разносится по разным файлам. Разбираться в написанном становится очень некомфортно.
По мне лучше функция из нескольких шагов, но в пределах одного экрана, чем прыгать по файлам и функциям не теряя контекст.
У меня только один вопрос, а причем тут JS?
Неужели эти общие принципы нельзя применить к другим языкам?
Мне почему-то кажется (т.е. лень проверять) что на том же Питоне все примеры можно практически один к одному переписать.
(а так — солидарен, хотя имхо и вполне очевидно во многом)

ES5 имеет более очевидный синтаксис. Стрелки ес6 мой глаз видит как выражения, а не как функции. А присваивания в аргументах стрелок, да еще и без return вообще выглядит как бардак. Единственная полезность стрелок — сохранение this. IMHO.

> Единственная полезность стрелок — сохранение this

Не, ну и компактный синтаксис для простых коллбеков — которые сами по длине сравнимы с «function(){}».

Юзаю и бед не знаю.
Забыл уже когда писал не стрелочную функцию

Согласен практически со всем за исключением «избыточного кода».

Ликвидация «избыточного» кода далеко не всегда приводит к улучшению читаемости. Кроме того, иногда бывает так — сразу после сокращения кажется, что код стал гораздо чище и элегантнее. Но когда ты возвращаешься к нему через пару месяцев, то краткость превращается в ребус, на расшифровку которого требуется ощутимое лишнее время.

Например, приводимый в статье пример с каррированием. Приведенный в статье код, на мой взгляд, плохо читается. Двойная стрелка
const add2 = a => b => a + b
потребует некоторого времени на осмысление, а вот следующая строчка
const inc = add2(1) 
и совсем нехороша: через пару месяцев, взглянув на нее, сможете ли вы с ходу сказать, что inc — функция?

и совсем нехороша: через пару месяцев, взглянув на нее, сможете ли вы с ходу сказать, что inc — функция?

А вы правда думаете, что люди, которые пишут такой код — поддерживают его целых два месяца?
Хуже — люди которые пишут такой код, через пару месяцев уже теряют к нему интерес и поддерживать его приходится другим.
Кто пишут — может и нет, а вот те, кому он достанется после них — запросто.

А чего здесь такого? Вот пример с Github: библиотека recompose, там большая часть кода основана на каррировании.


Если это нормально оформить в документации, то проблем не возникает.

UFO just landed and posted this here

В том конкретном примере, что я привел, достаточно один раз понять, почему в проекте пишется a => b => {} вместо (a, b) => {} и после этого код читается вполне неплохо.

Недостаточно, потому что встречая в коде func(a)(b) ты не будешь знать — это уже итоговое значение, или всё ещё функция, которая ждет недостающий аргумент

Typescript вам в помощь! С ним такие вещи узнаются легко.


Если же типов нет, то поможет здравая логика, например соглашение, что все функции из модуля Х возвращают еще одну функцию, например add(1)(2). Если же arity у каждой функции разное, то работать с этим неудобно, с этим соглашусь

UFO just landed and posted this here

А что именно вы хотите? Без строгих типов точно узнать, что вернется из функции, не получится, будь там каррирование или нет.

И часто вы видите типизированный код в мире JS? Я как раз TS очень даже пользуюсь. Да вот только хипстеры, которые пишут add(1)(2) обычно считают типизацию порождением сатаны.

Бывает, вижу код на TS, бывает и с каррированием, в основном с библиотеках из компонентах для React. И тайпинги для подобных штук, имеются.


Не стоит обобщать ваш личный опыт на состояние сообщества в целом.

Вы думаете я не знаю про DefinitelyTyped? Вот только оно, обычно поддерживается сообществом, не всегда есть, те, что есть не обязательно полные, а те, что полные — не обязательно на последнюю версию. Я уже молчу про всякие беты.

И, обычно, на такое получаешь бредовый ответ из разряда: «ну так помоги сообществу, напиши сам». Напишу, конечно, куда я денусь, но это рак JS, что авторы библиотек не спешат их типизировать и почему это меня не должно возмущать?

Я как раз разрабатываю на React и TS, так что прекрасно знаю «состояние сообщества».
Бесточечные функции в меру — это замечательно, но для js, насколько мне известно, нет утилит для перевода функций в бесточечную форму (вроде pointfree для хаскеля) и, что важнее, нет утилит для обратного перевода (вроде pointful) на случай если кто-то из команды слишком увлёкся комбинированием point-free функций.
Sign up to leave a comment.