Комментарии 76
представляющие название событий. Зачем придумывать все заного для js и ломать устоявшиеся каноны? Иммутабельность, это совсем не константы, это стиль\архитектура.
Дело в том, что const
— это не константа, а именно read-only переменная. Как правило, константам можно присваивать только скалярные значения, переменной же const
в JS можно присвоить любой объект. Кроме того, const
можно использовать и в циклах, если это перечисление. Например следующий код будет прекрасно работать:
const fruits = ["apple", "banana", "orange"];
for (const fruit of fruits) {
console.log(fruit);
// fruit = "dog" вызовет: TypeError: Assignment to constant variable.
}
Еще одна причина почему лучше везде где возможно использовать const
, это интроспекция в IDE. Например, в TypeScript следующая проверка будет работать:
const eventHandler: Function | null = getHandler();
if (eventHandler === null) {
return;
}
// с этого места eventHandler имеет тип Function
// ... много кода
// можно безопасно вызывать
// eventHandler - точно не null
eventHandler();
А если же заменить const eventHandler
на let eventHandler
, то код не скомпилируется, поскольку между первичной проверкой на null
и вызовом eventHandler()
мог присвоиться null
уже где-то в другом месте.
Кстати, похожее декларирование переменных есть в Kotlin, только там используются ключевые слова var
, val
.
https://kotlinlang.org/docs/reference/basic-syntax.html#defining-variables
И Ваш пример с функцией просто идеально иллюстрирует то о чем я говорю. В то время как первый пример иллюстрирует иммутабельность.
ИммутабельностьДа, про иммутабельность там ни к селу, ни к городу. Видимо, автор хотел, чтобы из поисковика к нему приходили и на это слово.
На протяжении этой статьи мы рассмотрим разницу между var, let и const, а также смежные темы такие как: “область видимости функции против блочной области видимости“, “поднятие” переменных и иммутабельность.
И так и не распарсил это… Что имелось ввиду?
> Следующие различие связано с “поднятием”. Ранее мы сказали, что определение “поднятия” это: “Интерпретатор JavaScript назначает объявленным переменным значение undefined во время фазы называемой “Создание”".
Воды больше чем в моем дипломе.
Предложу свой вариант: "всегда используйте const, кроме случаев когда не можете, тогда используйте let"
всегда используйте const, кроме случаев когда не можете, тогда используйте let
а потом сидите и гадайте, что реально является константой и на то есть причины, а что чисто случайно оказалось неизменным и из-за правила бездумного использования const было помечено как константа.
Цитата из этой статьи:
Действительно ли вы хотели сообщить, что ответ никогда не должен меняться, или просто так совпало, что вы нигде его не меняли?
Бывает множество привязок, которые нам ни разу не доводится изменять, но против изменения которых мы ничуть не возражаем. На деле, пожалуй, это справедливо для большинства ссылочных переменных. Это не главное, что обычно заботит ваш код.
Но автоматически навязывая это правило для каждой привязки, независимо от того, что на самом деле имел в виду автор, мы по сути выбрасываем на помойку возможность сообщить что-то полезное.
Простой пример: я написал код в котором всё неизменяемое бездумно отмечено константами, кто-то чуть доработал мой код и теперь одна из привязок стала реальной константой, дальше я дорабатываю этот код начиная менять эту переменную и что-то где-то ломаю. У автора предыдущего изменения просто не было возможности сообщить о том, что он теперь против изменения этой привязки. Ну не писать же ему так:
const a = 5; // точняк константа
Выглядит как-то глупо. Да и я не виноват в этой поломке, ведь при повсеместном использовании const замена его на let, при доработке кода, становится обычной практикой. Ну не устраивать же каждый раз полный анализ близлежащего кода пытаясь выяснить не является ли эта привязка реальной константой. Виноват здесь исключительно человек навязавший использование const по умолчанию.
Моё мнение — const по умолчанию хорош только при чисто функциональном стиле программирования. В остальных случаях лучше воспользоваться правилами из той же статьи:
1.Используйте const только в самой верхней области видимости
За всю жизнь не припомню случая, чтобы привязка, созданная внутри функции, расстроилась от своего изменения.
2.Старайтесь не использовать let в самой верхней области видимости
С другой стороны, если вам понадобился let в самой верхней области видимости, это знак, что у вас что-то типа глобального синглтон-состояния, от которого могут быть проблемы.
Вы так пишете, как будто всегда можно поменять let-переменную и ничего нигде от этого не поломается...
Кстати, вот вам привязка которая "расстраивается" от своего изменения:
const $element = $(...);
$element.on("foo", e => {
$element.bar();
});
const на let можно менять только после анализа всех мест использования.
вот я про то и говорю, что если бы const ставился только там где он действительно нужен, то этот анализ и не требовалось бы повторять постоянно. Он был бы проделан один раз тем кто этот const поставил. А так сиди и догадывайся каждый раз, просто так здесь const поставлен или в этом действительно есть смысл.
не следует считать что все вокруг так же делают.
как вы делаете такие выводы? Вы блондинка? Окей, разжую, я имел ввиду, что при обдуманном изменении let-переменной действительно ничего не сломается.
Вы так пишете, как будто всегда можно поменять let-переменную и ничего нигде от этого не поломается...
Да, ничего не сломается если всё сделано правильно и это действительно была переменная, а не константа.
Так там как раз пример правильного использования const. Там константа и кто-то правильно запретил её менять. Вы привели пример к этому тексту:
За всю жизнь не припомню случая, чтобы привязка, созданная внутри функции, расстроилась от своего изменения.
Я его не писал и процитировал не как какой-то аргумент, а как часть правил при которых большинство констант обычно сами отмечаются правильно. Зря процитировал, сам не согласен с этим предложением, ну а вы нашли к чему придраться.
при обдуманном изменении let-переменной действительно ничего не сломается.А типичная ошибка «пропустить один знак
=
в сравнении» — это обдуманное изменение? Мне кажется, использование const — лучше, чем постоянно писать Йода-сравнения.У меня линтер заставляет обёртывать такое в дополнительные скобки:
if ((a = b)) {
это и при чтении кода помогает, сразу видно, что там что-то не совсем обычно. Можно настроить чтобы совсем не пропускал такое.
(a = b) ? 1 : 2;
vs a == b ? 1 : 2;
Разве это не какое-то известное правило? Раньше, когда использовал javascript, у меня eslint подсвечивал такое заставляя добавлять скобки, причём я специально не настраивал это, то есть у многих должно работать как-то так же. Сейчас typescript как выяснилось не ругается, но при форматировании сам добавляет скобки. В данном случае добавил так:
let a = 5;
let b = 10;
console.log((a = b ? 1 : 2));
В целом вопрос написания const по дефолту мне напоминает холивар о написании классов final по дефолту. Суть идеи состоит в том, что класс для того чтобы его можно было эффективно наследовать должен быть заранее подготовлен к этому на этапе дизайна. Человек который пишет класс должен подумать о том как его будут наследовать и расширять. Если он об этом не думал (не было такой задачи) надо ставить final и такой класс не наследовать. Если понадобится то вернуться, передизайнить и убрать final.
Также и с const. Чтобы переменную можно было в любой момент брать и менять нужно заранее об этом подумать, чтобы нельзя было перевести приложение в некорректное состояние. Если программист об этом не подумал, следует объявить переменную const. Если кому-то понадобится её менять, пусть придет, подумает и поставит let.
Так я не против их изменения, если другой программист ниже по коду захочет перезаписать созданную мной переменную, то пусть перезапишет, мой код это не сломает, а значит переменная уже! подготовлена для дальнейшего использования. Так зачем же её помечать константой, запрещая её дальнейшее использование.
Мой опыт говорит о том, что код должен хорошо делать то, что от него требуется и предусматривать расширение в тех направлениях которые уже запланированы или хотя бы точно известны. Проектировать в рассечете на то что может понадобиться тут или там как правила не требуется, т.к. результат оказывается переусложенным и избыточным, и время потрачено лишнее.
Если вы не планируете её менять, зачем писать код с учетом этого?
потому что я не планирую запрещать её дальнейшее использование, оно ничего не ломает. Мне кажется я повторил это уже раз 20. Вы точно попытались осмыслить?
В 90% (оценочное суждение из головы) случаев никто ваш код трогать не будет никогда. В 9.5% случаев его удалят целиком не читая
у меня другой опыт: пишется один раз, дорабатывается 10 раз, читается 100 раз. Вы реально постоянно всё с нуля переписываете?
2. Да, честно говоря достаточно редко приходится точечно исправлять код (если это не багофикс). Такое что приходится прийти в какой-то метод и дописать пару строк не часто бывает. Чаще всего заменять какими-то блоками\компонентами, ну или как минимум функциями. При этом зачастую приходится провести какой-то нанорефакторинг в рамках которого исправить let на const или наоборот не составляет труда.
3. Чтение const упрощает значительно. Читая метод можно просто запомнить что в такой-то переменной лежит то-то и не думать об этом. Если переменная let то приходится держать в голове что там что-то может измениться.
Я в целом прекрасно вас понимаю, и некоторое время назад считал также как вы, но попробовал и проникся. Дело в том что вы относитесь к const как к чему-то особенному. Т.е. «по умолчанию переменные надо создавать как let, а const надо помечать только константы которые точно не должны меняться, выделять их таким образом» или что-то вроде того. А вы попробуйте применить этот подход на практике и увидите что почти все переменные в вашей программе можно объявить как const. И в этот момент произойдет сдвиг в сознании и осознание того факта, что на самом деле const это модификатор по умолчанию, и только редкие, единичные переменные должны быть помечены как let. И именно они должны привлекать внимание и вызывать настороженность, а не наоборот.
Ну и в конце я хотел бы заметить что на этот вопрос конечно же нет правильного ответа и это просто вопрос подхода и договоренности в команде, но я рекомендовал бы вам попробовать такой стиль.
я рекомендовал бы вам попробовать такой стиль
если что, то я уже больше 10 лет повседневно работаю с javascript/typescript и перепробовал очень многие вещи. Повсеместный const — одна из первых фич, которые я попробовал при появлении babel и навязал её использование не в одном проекте. Так что я то как раз полноценно с обоими вариантами поработал).
Такие вещи действительно нужно именно пробовать и порой достаточно длительное время, приведу хороший пример: первый рабочий день в новой компании, открываю код и, о ужас:
x == 1
Какое равенство нужно использовать в сравнениях, двойное или тройное? Уверен 99% смело ответят, что тройное, а оставшийся процент — неопределившиеся новички. Я тоже был исключительно за тройное. Спрашиваю — "Ребят, что за г в этом файле?", — "А у нас везде двойное равенство", — "Вроде компания серьёзная, а вы тут дикие какие-то. Вам Интернет отключили? Весь мир исключительно тройное использует!". Мне объяснили как нужно использовать двойное равенство не получая с этого стандартных минусов и в чём с этого будет плюс. И вроде понятно всё объяснили, там и объяснять то нечего, но я подумал: "ересь какая-то", говорю: — "Ну пофиг, буду делать как у вас тут принято, мне же не жениться на местном коде, но я остался при своём мнении", — "Окей, попробуй, посмотрим, что ты скажешь через пол года". Больше это не обсуждалось, но менее чем через пол года при рефакторинге личного кода я добровольно заменял тройное равенство на двойное. Тоже дикий совсем стал). До сих пор с удовольствием пользуюсь этой фичей в личных проектах, но ни разу даже не пытался где либо её пропихнуть, просто знаю, за такие мысли сразу камнями закидают, никто даже на секунду не задумается про попробовать, просто категоричное нет от всей команды. А там как раз была целая команда с удовольствием использующая эту фичу и как мне кажется большинство приходило в неё с теми же мыслями, что и я.
Та же ситуация с повсеместным const, все его используют, а тут какой-то вася какую-то ересь порет, и ладно бы объяснили и успокоился, но нет же, не унимается. Уже вон и в карму наплевали, действительно, да что вообще этот вася возомнил о себе!)). Мне вот как-то и в голову не приходило плевать в карму за отличающееся от моего мнение которое человек пытается спокойно обосновывать. Ну да пофиг, мне ж за карму на хабре не платят, хоть в минус загоняйте. Интересно другое: если завтра какой-нибудь фейсбук/гугл начнёт предлагать подобную ересь и использовать её у себя, то ситуация будет кардинально отличаться — нехотя, но начнут пробовать, а там глядишь и через пару лет станет общественным стандартом. Легчайше! Всего несколько лет назад кто бы мог подумать, что писать js, html и css в одном файле будет считаться нормой. Вот я бы такое предложил? Да сожгли бы!). Вот и получается победа общественного мнения и хайпа на собственным мозгом каждого.
Меня же та ситуация с двойным равенством научила спокойней относится ко всяким странным идеям, в каждом новом проекте я стараюсь попробовать что-нибудь новое, пусть и странное, хотя чаще это относится к каким-то архитектурным решениям, а не к такой мелочи, что мы здесь обсуждаем. Просто хотел показать альтернативный вариант, но, к сожалению, у многих это вызывает лишь желание меня заткнуть.
а потом сидите и гадайте
Не нужно конструкциям придумывать альтернативный смысл, тогда и гадать не придется.
Есть там альтернативный смысл или нет, но кода может быть просто много:
const $element = $(...);
// много кода
$element.on("foo", e => {
$element.bar();
});
// много кода
// а здесь вам стала интересна константа $element
имя для новой переменной придумывать не хочется, может эта сгодится? Как вы это поймёте? Ведь const при его повсеместном использовании не значит ровно ничего.
Да даже если кода не много, пара секунд потраченных на его беглый осмотр явно больше, чем просто увидеть отметку const которая расставляется обдумано.
но кода может быть просто много
Если кода много, то нужен рефакторинг.
Кстати, насчет вашего примера: он кажется валидным даже для логической константности: внешний код подписывается на события, а сам элемент остается неизменным.
Ведь const при его повсеместном использовании не значит ровно ничего.
Он значит неизменность ссылки. Это может быть полезно и, как минимум, не хуже
let
.пара секунд потраченных на его беглый осмотр явно больше, чем просто увидеть отметку const которая расставляется обдумано.
Вам придется оценивать ее «обдуманность», что сводит на нет пользу этих предположений.
Он значит неизменность ссылки. Это может быть полезно и, как минимум, не хуже let.
такая себе польза от знания того, что ссылка нигде не меняется, теряя при этом возможность знать от своего коллеги, что её нельзя менять по каким-то причинам. Ну не меняется она и что? Я для себя не нашёл пользы. Можете привести пример, в какой ситуации вам это что-то даёт?
Вам придется оценивать ее «обдуманность», что сводит на нет пользу этих предположений.
помечено константой — не трогай, создай новую привязку, польза остаётся. Если же нужно как-то поработать именно с этой константой, то да, нужно будет оценить остался ли запрет на изменение.
А замыкания в JS используются часто.
А замыкания в JS используются часто.
а так ли уж часто? Просмотрел сейчас несколько десятков файлов в текущем проекте, да, колбеки есть, но это всегда чистые функции ничего не использующие и не меняющие вне себя. Можно представить ситуацию вроде такой (абстрактный код):
class SomeView extends View {
onBtnClick() {
let btn = $('btn');
btn.disable();
request.get(...).then(res => {
btn.enable();
// ...
});
}
}
вроде замыкание, но такого в коде по-хорошему вообще не должно быть. Вот как мне теперь переопределить только обработку ответа при наследовании? Соответственно переписываем так:
class SomeView extends View {
initialize() {
this.btn = $('btn');
}
onBtnClick() {
this.btn.disable();
request.get(...).then(this.onResponse);
}
@autobind
onResponse(res) {
this.btn.enable();
// ...
}
}
Опа-на и нет замыкания)). Причём избавлялись от него не ради избавления от замыкания, а по совершенно другой причине и такая причина в ООП всегда находится.
Плюс есть async/await, то есть даже если точно не нужно выносить обработку в отдельный метод, то так:
class SomeView extends View {
async onBtnClick() {
let btn = $('btn');
btn.disable();
let res = await request.get(...);
btn.enable();
// ...
}
}
В результате в примерно 35 просмотренных крупных файлов въюшек и стора я не увидел ни одного замыкания с не константой. В файлах сборки тоже не нашёл такого, в основном колбеки в виде чистых функций. Максимум встречаются замыкания с константами:
const gulp = require('gulp');
gulp.task('some-task', () => {
return gulp. // ...
но это опять же к нашей ситуации не относится.
В результате
Если ссылка никогда не меняется — её безопасно использовать в любых замыканиях.
совсем слабенький аргумент получается. По крайней мере для не функционального стиля, но я про это сразу написал:
Моё мнение — const по умолчанию хорош только при чисто функциональном стиле программирования. В остальных случаях лучше воспользоваться правилами из той же статьи:
В любом случае это пока единственный хоть сколько-то весомый аргумент за повсеместный const из тех что пока прозвучали.
Моё мнение — const по умолчанию хорош только при чисто функциональном стиле программирования. В остальных случаях лучше воспользоваться правилами из той же статьи:
В любом случае это пока единственный хоть сколько-то весомый аргумент за повсеместный const из тех что пока прозвучали.
Сомнительно. Я вот больше в сторону ООП тяготею. Я там тоже редко нужно использовать не-конст. А уж анонимные функции — от и подавно. К примеру, на реакте я бы написал как-то так:
class MyComponent extends React.Component {
render () {
return <button onClick={this.activate} />;
}
activate = (e) => {
// do
}
}
То есть опять же — не через замыкание.
В тех редких случаях, когда в этом есть необходимость — лучше явно указать, что вот эта вот переменная — изменяется.
Значит если взять любую переменную в любом методе, то перейдя в место после её последнего использования можно делать с ней всё что угодно, хоть слона туда засовывай, ничего гарантированно не сломается.Зачем?
То есть на первый взгляд вообще пофигу let или constНет, с точки зрения ясности кода — не пофиг. Понимание, что переменная один раз получает значение и больше не изменяется — очень поможет поддержке.
дальнейшей доработке кода будет постоянно заставлять заменять его на letНет, не будет. Изменение переменных нужно крайне редко, потому слово «постоянно» не подходит и потому его лучше указать явно. А еще оно закладывается в момент объявления переменной, а не делается внезапно из старой константной переменной.
Пользы же он будет давать ровно нольНет, не ноль.
В результате приходим к варианту повсеместного использования только letНет, не приходим.
То есть const где-то ещё совсем не должен встречатьсяНет, должен
Ну как бы и я про тоже, что замыканий с не константами в ООП получается совсем мало.Я так понял, что вы согласились с тем, что конст нужен только в чисто-функциональном стиле
теряя при этом возможность знать от своего коллеги, что её нельзя менять по каким-то причинам.
Ваш коллега устроился вчера, это компонент из легаси, вы со старыми привычками перешли работать в новую команду, а там такое мнение никто не разделяет или пишут, как попало…
Чтобы создать и поддерживать такую возможность, вам потребуется слишком много усилий. Стоят ли они того?
пример, в какой ситуации вам это что-то даёт
Вы по неосмотрительности не затрёте ссылку, которую коллега решил сделать неизменяемой (для этого даже не нужно смотреть на объявление). Вы будете уверены в том, что присваивание выполняется лишь раз или же наоборот —
let
укажет на переиспользуемую природу ссылки. Может, как триггер: «А не фигню ли я делаю? Зачем здесь пересоздавать/перезапрашивать объект?»из легаси
ну в легаси всё что угодно может твориться, здесь сложно что-то оценивать.
не затрёте ссылку, которую коллега решил сделать неизменяемой
как не затру, вот я нахожусь в каком-то месте кода, вижу $element, вижу, что это константа, если const расставлялся осмысленно, то да, не затру, ведь мне явно сказано не трогать, а так я лишь знаю, что пока ещё не менялась и всё, скорей всего можно менять просто заменив const на let. Ладно, я проверю точно ли можно так делать, но вот что там делает коллега с его хотфиксом я не знаю. А дальше получаем:
из легаси
ну в легаси всё что угодно может твориться
Условностям здесь доверять нельзя. Перелопачивать будет слишком дорого, останется исключением из правил,
ведь мне явно сказано не трогать
Вам это еще рантайм подскажет, выбросив ошибку.
А дальше получаем: из легаси
Чтобы не получать разночтений с коллегой о том, кто что понял, как следует стайлгайду и прочее, как мне кажется, лучше не переизобретать смысл на соглашениях, а следовать примеру стандартной библиотеки JS и именовать трушные константы в верхнем регистре (
SCREAMING_SNAKE_CASE
).// много кодана хотябы одну строчку кода) уже 7 значимых строк кода (не считая пустые). Мне кажется не стоит писать методы в которых больше восьми, ну максимум десяти, значимых строк. Лучше выделять из них дочерние функции. А в небольшой функции и запутаться сложнее.
больше восьми, ну максимум десяти
маленькое у вас ограничение. Я обычно ограничиваю себя высотой небольшого (macbook) экрана, в этом случае уже не важно значимые строки или нет.
function discountPrices (prices, discount) { let discounted = [] for (let i = 0; i < prices.length; i++) { let discountedPrice = prices[i] * (1 - discount) let finalPrice = Math.round(discountedPrice * 100) / 100 discounted.push(finalPrice) } console.log(i) // 3 console.log(discountedPrice) // 150 console.log(finalPrice) // 150 return discounted } discountPrices([100, 200, 300], .5) // NOT OK: ReferenceError: i is not defined
Мы получили ReferenceError: i is not defined. Что говорит нам о том, что переменная, объявленная при помощи let, ограничена областью видимости блока, а не функции.
тут discountPrices — за пределами функции.
а i и finalPrice за пределами блока и комментом обозначены как выдающие значение, но объявлены через let в другом блоке
Скопирую сюда:
let a = [];
for(let i = 0;i<4;i++){
a.push(function(){
return(i);
});
i++;
}
console.log(a.length);
console.log(a[0]());
console.log(a[1]());
i
в «шапке» цикла и i
в теле — это разные переменные с двумя копированиями значений на каждую итерацию.let a = [];
for(let i = 0;i<14; i++){
a.push(function(){
return(i);
});
i++;
}
console.log(a.map(x => x())); // [1, 3, 5, 7, 9, 11, 13]
Первая итерация — создался i, он равен нулю, добавили функцию с замыканием на i, увеличили i, теперь он равен 1
Вторая итерация, создан новый i, который равен старому, равен единице, увеличили на 1, теперь он равен 2, добавили функцию с замыканием на i, увеличили i, теперь он равен 3
Третья итерация ...
P.S.
let arr = [];
for(let i = 0, i1 = 0, b = { a: 0}; i < 4; i++) {
let c = {...b};
arr.push(()=>[i++,b.a++,c.a++, i1++]);
i1++;
}
console.log(arr.map(x => x().join(', ')));
console.log(arr.map(x => x().join(', ')));
Когда использовать var, let и const в Javascript [перевод статьи Tyler’а McGinnis]