Var, let или const? Проблемы областей видимости переменных и ES6

https://alistapart.com/article/fixing-variable-scope-issues-with-ecmascript-6
  • Перевод
Области видимости в JavaScript всегда были непростой темой, особенно в сравнении с более строго организованными языками, такими, как C и Java. В течение многих лет области видимости в JS особенно широко не обсуждались, так как в языке попросту не было средств, которые позволяли бы существенно повлиять на сложившуюся ситуацию. Но в ECMAScript 6 появились некоторые новые возможности, которые позволяют разработчикам лучше контролировать области видимости переменных. Эти возможности в наши дни уже очень хорошо поддерживают браузеры, они вполне доступны для большинства разработчиков. Однако новые ключевые слова для объявления переменных, учитывая ещё и то, что старое ключевое слово var никуда не делось, означают не только новые возможности, но и появление новых вопросов. Когда использовать ключевые слова let и const? Как они себя ведут? В каких ситуациях всё ещё актуально ключевое слово var? Материал, перевод которого мы сегодня публикуем, направлен на исследование проблемы областей видимости переменных в JavaScript.



Области видимости переменных: обзор


Область видимости переменной — это важная концепция в программировании, которая, правда, может запутать некоторых разработчиков, особенно — новичков. Область видимости переменной — это та часть программы, в которой к этой переменной можно обратиться.

Взгляните на следующий пример:

var myVar = 1;

function setMyVar() {
  myVar = 2;
}

setMyVar();

console.log(myVar);

Что выведет метод console.log? Ответ на этот вопрос никого не удивит: он выведет 2. Переменная myVar объявлена за пределами какой-либо функции, что говорит нам о том, что она объявлена в глобальной области видимости. Следовательно, любая функция, объявленная в той же области видимости, сможет обратиться к myVar. На самом деле, если речь идёт о коде, выполняемом в браузере, доступ к этой переменной будет даже у функций, объявленных в других файлах, подключённых к странице.

Теперь взглянем на следующий код:

function setMyVar() {
  var myVar = 2;
}

setMyVar();

console.log(myVar);

Внешне его изменения, по сравнению с предыдущим примером, незначительны. А именно, мы всего лишь поместили объявление переменной внутрь функции. Что теперь выведет console.log? На самом деле, ничего, так как эта переменная не объявлена и при попытке обратиться к ней будет выведено сообщение о необработанной ошибке ReferenceError. Произошло так из-за того, что переменную, с помощью ключевого слова var, объявили внутри функции. В результате область видимости этой переменной ограничивается внутренней областью видимости функции. К ней можно обратиться в теле этой функции, с ней могут работать функции, вложенные в эту функцию, но извне она недоступна. Если нам надо, чтобы некоей переменной могли бы пользоваться несколько функций, находящихся на одном уровне, нам надо объявлять эту переменную там же, где объявлены эти функции, то есть — на один уровень выше их внутренних областей видимости.

Вот одно интересное наблюдение: код большинства веб-сайтов и веб-приложений не относится к творчеству какого-то одного программиста. Большинство программных проектов являются результатами командной разработки, и, кроме того, в них используются сторонние библиотеки и фреймворки. Даже если разработкой некоего сайта занимается один программист, обычно он пользуется внешними ресурсами. Из-за этого обычно не рекомендуется объявлять переменные в глобальной области видимости, так как нельзя заранее знать, какие переменные будут объявлять другие разработчики, код которых будет использоваться в проекте. Для того чтобы обойти эту проблему, можно использовать некоторые приёмы, в частности — паттерн «Модуль» и IIFE при применении объектно-ориентированного подхода к JavaScript-разработке, хотя того же эффекта позволяет достичь инкапсуляция данных и функций в обычных объектах. В целом же можно отметить, что переменные, область видимости которых выходит за пределы той, которая им необходима, обычно представляют собой проблему, с которой надо что-то делать.

Проблема ключевого слова var


Итак, мы разобрались с понятием «область видимости». Теперь перейдём к более сложным вещам. Взгляните на следующий код:

function varTest() {
  for (var i = 0; i < 3; i++) {
    console.log(i);
  }
  console.log(i);
}

varTest();

Что попадёт в консоль после его выполнения? Понятно, что внутри цикла будут выводиться значения увеличивающегося счётчика i: 0, 1 и 2. После того, как цикл завершается, программа продолжает выполняться. Теперь мы пытаемся обратиться к той же самой переменной-счётчику, которая была объявлена в цикле for, за пределами этого цикла. Что из этого выйдет?

В консоль, после обращения к i за пределами цикла, попадёт 3, так как ключевое слово var действует на уровне функции. Если объявить переменную с использованием var, то обратиться к ней в функции можно и после выхода из той конструкции, где она была объявлена.

Это может превратиться в проблему тогда, когда функции усложняются. Рассмотрим следующий пример:

function doSomething() {
  var myVar = 1;
  if (true) {
    var myVar = 2;
    console.log(myVar);
  }
  console.log(myVar);
}

doSomething();

Что попадёт в консоль теперь? 2 и 2. Мы объявляем переменную, инициализируем её числом 1, а затем пытаемся переопределить ту же переменную внутри выражения if. Так как два эти объявления существуют в одной и той же области видимости, мы не можем объявить новую переменную с тем же именем, даже хотя мы, очевидно, хотим сделать именно это. В результате первая переменная перезаписывается внутри выражения if.

Вот в этом-то и заключается самый большой недостаток ключевого слова var. Область видимости переменных, объявленных с его использованием, оказывается слишком большой. Это может привести к непреднамеренной перезаписи данных и к другим ошибкам. Большие области видимости часто ведут к появлению неаккуратных программ. В целом, переменная должна иметь область видимости, ограниченную её нуждами, но не превышающую их. Хорошо было бы иметь возможность объявлять переменные, область видимости которых не так велика, как при использовании var, что позволило бы, при необходимости, применять более стабильные и лучше защищённые от ошибок программные конструкции. Собственно говоря, такие возможности нам даёт ECMAScript 6.

Новые способы объявления переменных


Стандарт ECMAScript 6 (новый набор возможностей JavaScript, известный ещё как ES6 и ES2015) даёт нам два новых способа объявления переменных, отличающихся ограниченной, по сравнению с var, областью видимости и имеющих ещё некоторые особенности. Это — ключевые слова let и const. И то и другое даёт нам так называемую блочную область видимости. Это означает, что область видимости при их использовании может быть ограничена блоком кода, таким, как цикл for или выражение if. Это даёт разработчику больше гибкости в выборе областей видимости переменных. Рассмотрим новые ключевые слова.

▍Использование ключевого слова let


Ключевое слово let очень похоже на var, основное отличие — ограниченная область видимости переменных, объявляемых с его помощью. Перепишем один из вышеприведённых примеров, заменив var на let:

function doSomething() {
  let myVar = 1;
  if (true) {
    let myVar = 2;
    console.log(myVar);
  }
  console.log(myVar);
}

doSomething();

В данном случае в консоль попадут числа 2 и 1. Происходит это из-за того, что выражение if задаёт новую область видимости для переменной, объявленной с помощью ключевого слова let. Это приводит к тому, что вторая объявленная переменная — это совершенно самостоятельная сущность, не связанная с первой. С ними можно работать независимо друг от друга. Однако это не значит, что вложенные блоки кода, вроде нашего выражения if, полностью отрезаны от переменных, объявленных с помощью ключевого слова let в той области видимости, в которой находятся они сами. Взгляните на следующий код:

function doSomething() {
  let myVar = 1;
  if (true) {
    console.log(myVar);
  }
}

doSomething();

В этом примере в консоль попадёт число 1. У кода, находящегося внутри выражения if есть доступ к переменной, которую мы создали за его пределами. Поэтому он и выводит её значение в консоль. А что произойдёт, если попытаться перемешать области видимости? Например, сделать так:

function doSomething() {
  let myVar = 1;
  if (true) {
    console.log(myVar);
    let myVar = 2;
    console.log(myVar);
  }
}

doSomething();

Может показаться, что первый вызов console.log выведет 1, но на самом деле при попытке выполнить этот код появится ошибка ReferenceError, которая сообщает нам о том, что переменная myVar для данной области видимости не определена или не инициализирована (текст этой ошибки различается в разных браузерах). В JavaScript существует такое явление, как поднятие переменных в верхнюю часть их области видимости. То есть, если в некоей области видимости объявляют переменную, JavaScript резервирует место для неё ещё до того, как будет выполнена команда её объявления. То, как именно это происходит, различается при использовании var и let.

Рассмотрим следующий пример:

console.log(varTest);
var varTest = 1;

console.log(letTest);
let letTest = 2;

В обоих случаях мы пытаемся воспользоваться в переменной до её объявления. Но команды вывода данных в консоль ведут себя по-разному. Первая, использующая переменную, которая позже будет объявлена с помощью ключевого слова var, выведет undefined — то есть то, что будет записано в эту переменную. Вторая же команда, которая пытается обратиться к переменной, которая позже будет объявлена с помощью ключевого слова let, выдаст ReferenceError и сообщит нам, что мы пытаемся использовать переменную до её объявления или инициализации. В чём дело?

А дело тут в том, что перед выполнением кода механизмы, ответственные за его выполнение, просматривают этот код, выясняют, будут ли в нём объявляться какие-то переменные, и, если это так, осуществляют их поднятие с резервированием места для них. При этом переменные, объявленные с помощью ключевого слова var, инициализируются значением undefined в пределах своей области видимости, даже если к ним обращаются до того, как они будут объявлены. Основная проблема тут заключается в том, что значение undefined в переменной не всегда указывает на то, что переменной пытаются воспользоваться до её объявления. Взгляните на следующий пример:

var var1;
console.log(var1);

console.log(var2);
var var2 = 1;

В данном случае, несмотря на то, что var1 и var2 объявлены по-разному, оба вызова console.log выведут undefined. Дело здесь в том, что в переменные, объявленные с помощью var, но не инициализированные, автоматически записывается значение undefined. При этом, как мы уже говорили, переменные, объявленные с помощью var, к которым обращаются до момента их объявления, так же содержат undefined. В результате, если в подобном коде что-то пойдёт не так, нельзя будет понять, что именно является источником ошибки — использование неинициализированной переменной или использование переменной до её объявления.

Место для переменных, объявляемых с помощью ключевого слова let, резервируется в их блоке, но, до их объявления, они попадают во временную мёртвую зону (TDZ, Temporal Dead Zone). Это приводит к тому, что ими, до их объявления, пользоваться нельзя, а попытка обращения к такой переменной приводит к ошибке. Однако система точно знает причину проблемы и сообщает об этом. Это хорошо видно на данном примере:

let var1;
console.log(var1);

console.log(var2);
let var2 = 1;

Здесь первый вызов console.log выведет undefined, а второй вызовет ошибку ReferenceError, сообщая нам о том, что переменная пока не объявлена или не инициализирована.

В результате, если при использовании var появляется undefined, мы не знаем причину подобного поведения программы. Переменная может быть либо объявлена и неинициализирована, либо может быть ещё не объявлена в данной области видимости, но будет объявлена в коде, который расположен ниже команды обращения к ней. При использовании ключевого слова let мы можем понять — что именно происходит, а это гораздо полезнее для отладки.

▍Использование ключевого слова const


Ключевое слово const очень похоже на let, но у них есть одно важное различие. Это ключевое слово используется для объявления констант. Значения констант после их инициализации менять нельзя. Нужно отметить, что это относится лишь к значениям примитивных типов, воде строки или числа. Если константа является чем-то более сложным, например — объектом или массивом, внутреннюю структуру подобной сущности модифицировать можно, нельзя лишь заменить её саму на другую. Взгляните на следующий код:

let mutableVar = 1;
const immutableVar = 2;

mutableVar = 3;
immutableVar = 4;

Этот код будет выполняться вплоть до последней строки. Попытка назначить новое значение константе приведёт к появлению ошибки TypeError. Именно так ведут себя константы, но, как уже было сказано, объекты, которыми инициализируют константы, можно менять, они могут подвергаться мутациям, что способно приводить к неожиданностям.

Возможно вам, как JavaScript-разработчику, интересно, почему иммутабельность переменных — это важно. Константы — это новое явление в JavaScript, в то время как они являются важнейшей частью таких языков, как C или Java. Почему эта концепция так популярна? Дело в том, что использование констант заставляет нас думать о том, как именно работает наш код. В некоторых ситуациях изменение значения переменной может нарушить работу кода, например, если в ней записано число Пи и к ней постоянно обращаются, или если в переменной имеется ссылка на некий HTML-элемент, с которым постоянно нужно работать. Скажем, вот константа, в которую записана ссылка на некую кнопку:

const myButton = document.querySelector('#my-button');

Если код зависит от ссылки на HTML-элемент, то нам нужно обеспечить неизменность этой ссылки. В результате можно говорить, что ключевое слово const идёт не только по пути улучшений в сфере области видимости, но и по пути ограничения возможности модификации значений констант, объявленных с использованием этого ключевого слова. Вспомните, как мы говорили о том, что переменная должна иметь именно такую область видимости, которая ей нужна. Эту мысль можно продолжить, выдвинув рекомендацию, в соответствии с которой у переменной должна быть лишь такая возможность изменяться, которая нужна для правильной работы с ней, и не более того. Вот хороший материал на тему иммутабельности, из которого можно сделать важный вывод, в соответствии с которым использование иммутабельных переменных заставляет нас тщательнее обдумывать свой код, что ведёт к улучшению чистоты кода и к снижению числа неприятных неожиданностей, возникающих при его работе.

Когда я только начал использовать ключевые слова let и const, я, в основном, применял let, прибегая к const лишь тогда, когда запись нового значения в переменную, объявленную с помощью let, могла бы навредить программе. Но, всё больше узнавая о программировании, я изменил своё мнение по этому вопросу. Теперь мой основной инструмент — это const, а let я использую только тогда, когда значение переменной нужно перезаписывать. Это заставляет меня думать о том, действительно ли необходимо менять значение некоей переменной. В большинстве случаев делать этого не нужно.

Нужно ли нам ключевое слово var?


Ключевые слова let и const способствуют применению более ответственного подхода к программированию. Существуют ли ситуации, в которых всё ещё нужно ключевое слово var? Да, существуют. Есть несколько ситуаций, в которых это ключевое слово нам ещё пригодится. Как следует поразмыслите над тем, о чём мы сейчас поговорим, прежде чем менять var на let или const.

▍Уровень поддержки ключевого слова var браузерами


Переменные, объявленные с помощью ключевого слова var, отличаются одной очень важной особенностью, отсутствующей у let и const. А именно, речь идёт о том, что это ключевое слово поддерживают абсолютно все браузеры. Хотя поддержка браузерами let и const весьма хороша, однако, существует риск того, что ваша программа попадёт в браузер, их не поддерживающий. Для того чтобы понять последствия подобного происшествия, нужно учитывать то, как браузеры обходятся с неподдерживаемым JavaScript-кодом, в противовес, например, тому, как они реагируют на непонятный им CSS-код.

Если браузер не поддерживает какую-то возможность CSS, то это, в основном, ведёт к некоторым искажениям того, что будет выведено на экран. Сайт в браузере, который не поддерживает какие-то из используемых сайтом стилей, будет выглядеть не так, как ожидается, но им, весьма вероятно, можно будет пользоваться. Если же вы используете, например, let, а браузер это ключевое слово не поддерживает, то ваш JS-код там просто не будет работать. Не будет — и всё. Учитывая то, что JavaScript является одной из важных составных частей современного веба, это может стать серьёзнейшей проблемой в том случае, если вам надо, чтобы ваши программы работали бы в устаревших браузерах.

Когда говорят о поддержке сайтов браузерами, обычно задаются вопросом о том, в каком браузере сайт будет работать оптимально. Если же речь идёт о сайте, функционал которого основан на использовании let и const, то похожий вопрос придётся ставить иначе: «В каких браузерах наш сайт работать не будет?». И это — гораздо более серьёзно, чем разговор о том, использовать или нет display: flex. Для большинства веб-сайтов число пользователей с устаревшими браузерами не будет достаточно большим, чтобы об этом стоило бы беспокоиться. Однако если речь идёт о чём-то вроде интернет-магазина, или сайтов, владельцы которых покупают рекламу, это может быть весьма важным соображением. Прежде чем использовать новые возможности в подобных проектах, оцените уровень риска.

Если вам нужно поддерживать по-настоящему старые браузеры, но вы хотите при этом использовать let, const и другие новые возможности ES6, одним из вариантов решения проблемы является применение JavaScript-транспилятора наподобие Babel. Транспиляторы обеспечивают перевод нового кода в то, что будет понятно старым браузерам. Применяя Babel, можно писать современный код, использующий самые свежие возможности языка, а затем преобразовывать его в код, который могут выполнять устаревшие браузеры.

Звучит слишком хорошо, чтобы быть правдой? На самом деле, использование транспиляторов таит в себе некоторые неприятные особенности. Так, это значительно увеличивает объём готового кода, если сравнить его с тем, что можно было бы получить, написав его вручную. Как результат, растёт объём файлов. Кроме того, если вы начали использовать некий транспилятор, ваш проект оказывается привязанным к нему. Даже если вы пишете ES6-код, который совершенно правильно обрабатывается Babel, отказ от Babel приведёт к тому, что вам придётся перепроверять весь код, тщательно тестировать его. Если ваш проект работает как часы, эта идея вряд ли понравится тем, кто занимается его разработкой и поддержкой. Тут придётся задаваться некоторыми вопросами. Когда планируется переработать кодовую базу? Когда поддержка чего-то вроде IE8 уже не будет иметь значения? Возможно, ответы на эти вопросы и не повлияют на отказ от транспилятора, но их, в любом случае, надо учитывать, решаясь на столь серьёзный шаг.

▍Использование var для решения одной специфической задачи


Есть ещё одна ситуация, в которой ключевое слово var может то, чего не могут другие. Это довольно специфическая задача. Рассмотрим следующий код:

var myVar = 1;

function myFunction() {
  var myVar = 2;
  // Внезапно оказалось, что нам нужна переменная myVar из глобальной области видимости!
}

Итак, тут мы объявляем переменную myVar в глобальной области видимости, но позже теряем к ней доступ, так как объявляем такую же переменную в функции. Вдруг оказывается, что нам нужна и переменная из глобальной области видимости. Подобная ситуация может показаться надуманной, так как первую переменную, например, можно просто передать функции, или можно переименовать одну из них. Но вполне может случиться так, что ни то ни другое вам не доступно. И вот тут нам и пригодятся возможности var.

var myVar = 1;

function myFunction() {
  var myVar = 2;
  console.log(myVar); // 2
  console.log(window.myVar); // 1
}

Когда переменная объявляется в глобальной области видимости с использованием var, она автоматически привязывается к глобальному объекту window. Ключевые слова let и const этого не делают. Эта особенность однажды выручила меня в ситуации, когда сборочный скрипт проверял JS-код перед объединением файлов, а ссылка на глобальную переменную в одном из файлов (который, после сборки, был бы объединён с другими) выдавала ошибку, что не давало собрать проект.

Надо отметить, что использование этой возможности ведёт к написанию неаккуратного кода. Чаще всего подобную проблему решают гораздо изящнее, получая в итоге более чистый код и меньшую вероятность возникновения ошибок. Речь идёт о том, что переменные, в виде свойств, записывают в собственный объект:

let myGlobalVars = {};
let myVar = 1;
myGlobalVars.myVar = myVar;

function myFunction() {
  let myVar = 2;
  console.log(myVar); // 2
  console.log(myGlobalVars.myVar); // 1
}

Безусловно, тут придётся писать больше кода, но этот подход делает код, используемый для решения неких неожиданных проблем, понятнее. В любом случае, бывает так, что рассматриваемая возможность ключевого слова var оказывается полезной, хотя, прежде чем прибегать к ней, стоит поискать другой, более понятный, способ решения проблемы.

Итоги


Итак, что выбрать? Как расставить приоритеты? Вот некоторые соображения по этому поводу:

  • Вы собираетесь поддерживать IE10 или по-настоящему старые браузеры? Если вы даёте на этот вопрос положительный ответ и не собираетесь пользоваться транспиляторами — отказывайтесь от новых возможностей и используйте var.
  • Если вы можете позволить себе использование новых возможностей JavaScript, начать стоит с того, чтобы везде, где раньше применялось ключевое слово var, использовать const. Если где-то нужна возможность перезаписи значения переменной (хотя, если вы попытаетесь переписать свой код, то эта возможность может вам и не понадобиться) — используйте let.

Новые ключевые слова let и const, появившиеся в ECMAScript 6, дают нам больше возможностей по контролю за областью видимости переменных (и констант) в коде веб-сайтов и веб-приложений. Они заставляют нас больше думать о том, как именно работает код, а такие размышления хорошо влияют на то, что у нас получается. Конечно, прежде чем воспользоваться чем-то новым, стоит взвесить все «за» и «против» в применении к конкретной задаче, но, используя let и const, вы сделаете свои проекты стабильнее и приготовите их к будущему.

Уважаемые читатели! Согласны ли вы с рекомендацией, в соответствии с которой ключевое слово const стоит сделать основной заменой ключевому слову var, прибегая к let лишь в тех случаях, когда значение переменной, по объективным причинам, нужно перезаписывать?

RUVDS.com

927,93

RUVDS – хостинг VDS/VPS серверов

Поделиться публикацией
Комментарии 39
    +15
    Шел 2018 год, const и let все еще считаются новыми возможностями.
      –3
      Ну так в основном ТЗ заказчика выглядит примерно так — «Хочу красиво, быстро, да еще чтобы в IE7 работало, потому как у меня парк машин на Win XP и обновлять его ближайшие пару сотен лет я не планирую»
        +3
        IE7 все еще не основание не использовать современную версию языка. К счастью, у babel есть транспилеры даже в es3, а недостающий рантаймовый функционал, типа тех же ES6 Proxy, можно нахреначить либо через нативные расширения браузеров, либо тотальным опесочиванием.
          0
          А о таком варианте как-то не подумал сразу) Спасибо за уточнение) Но опять же — лично мне на это плевать, ибо стараюсь обходиться константами и чистыми функциями когда пишу на js, а учитывая что приходится делать это достаточно редко, то проблем особых не испытываю
            0
            Ну es6 не ограничивается только константами. Это и итераторы, и стрелочные функции, и выведение имен, и генераторы, и промисы, и асинхронные функции…
              0
              Само собой. Я сейчас обсуждение веду в рамках статьи просто, тут об этих возможностях es6 ни слова, вот и я не упоминаю
          0
          в основном

          хм, я такого не встречал уже… лет 8 точно. Где вы таких находите?

            0
            Спросите это у нашего руководства. Кстати это и стало основной причиной моего ухода, осталось еще 2 недели отработать и с радостью свалю. Но факт остается фактом — конкретно на моем текущем месте работы примерно так ситуация и выглядит, потому как фирма ориентирована на рынок СНГ
          +4

          Исключительно в блоге компании RUVDS.

            +1
            Это уже не говоря о том, что они какими-то мутными целями оправдывают написание говнокода с var. Как будто нет бабеля на let-const и нельзя код писать нормально. Короче статья ради статьи реально.

            Вот тут очень толково и сжато написано если кому надо: learn.javascript.ru/es-modern
            +1
            Как по мне — использовать var в 2018 году уже не имеет смысла, разве что для поддержки древних браузеров, которые не могут в ES6. Ну и как кому, лично я вообще предпочитаю по возможности приближаться к функциональщине при работе с js, а при таком подходе в принципе ни let, ни var не нужны, достаточно const, но это уже вкусовщина
              +1
              Как быстро, однако, сейчас становятся "древними" браузеры, выпущенные в феврале 2016 года. Всего 2,5 года — и ты уже "замшелая древность".
                0
                Современные браузеры обновляются автоматически. Новая версия раскатывается на всех пользователей в течение нескольких недель.

                Если у вас до сих пор стоит старый Хром, значит у вас что-то сломалось.
                  0
                  Сразу понятно, что под Presto Вы не разрабатываете.
                    +1

                    А зачем? Последняя версия вышла 5 лет назад. Этот движок уже устарел по любому критерию

                      –2
                      Я написал, когда вышла последняя версия Оперы на этом движке. 16 февраля 2016 года.
                      Вы ошиблись в 2 раза.
                      И Вы напрасно думаете, что его никто не использует вообще.
                        +3

                        И правда, был релиз в 16 году. Но это было исправление безопасности, а не какое-то развитие с новыми фичами.


                        В моем представлении такой продукт выглядит скорее мертвым, чем живым.

                      0
                      Presto ещё живой?
                    0
                    Не везде автоматически. В компаниях на рабочих местах часто только волею администратора. А ещё версия прибита может быть из-за какого-то софта, который, например, не хочет нормально работать в хроме 49+.
                      0
                      И на серверах какие-то хедлесс или под селениумом не обновляются часто годами.
                        0
                        Прибитая гвоздями версия ради энтерпрайзного продукта — такое случается, но это скорее исключение из правил.

                        А вот тестовые браузеры под Selenium лучше своевременно обновлять, чтобы не было ложных срабатываний на баги, которых у пользователей никогда не будет.
                          0
                          Программно управляемые браузеры это не только тестовые фермы, но и различные агрегаторы, например, парсящие ваши SPA. Но и для тестовых ферм целесообразно использовать те версии браузеров, которые используют конкретно ваши пользователи, если вы работаете на высококонкурентном рынке и пользователь скорее уйдёт к конкурентам чем будет ради вас обновлять браузер. Например, профессиональные ресурсы для бухгалтеров или кадровиков часто имеют очень большой процент старых браузеров.
                            0
                            но и различные агрегаторы, например, парсящие ваши SPA

                            Если агрегатор с более старым браузером может парсить сайт, то проблем нет. А вот даунгрейдить фичи только чтобы ублажить агрегатор, вместо заботы о конечных пользователях — это странный труд.


                            Я знаю, что GoogleBot использует Chrome 41. Но для меня это повод задуматься о полноценном сервер-рендеринге, а не ограничивать себя в let/const.


                            если вы работаете на высококонкурентном рынке и пользователь скорее уйдёт к конкурентам чем будет ради вас обновлять браузер

                            Что-то я не верю в анекдот "один вебмастер не использовал es6, и его продажи удвоились за счет пользователей с завирусованных компьютеров, пользующихся Амиго или Chrome 46". Все-таки у подавляющего большинства пользователей обновления включены.

                              0
                              Я не про даунгрейд специальный, а про отсутствие апгрейда :) А серверный рендеринг может оказаться довольно сложной технической задачей. Не, если у вас сервер на ноде и фронт фреймворк, поддерживающий рендеринг из коробки, то чуть ли не парой строк в конфиге можно решить. А если сервер на том же PHP, а фронт фреймворк про ноду плохо осведомлён, то одним из вариантов серверного рендеринга будет как раз создание фермы с браузерами :)

                              В целом может быть у подавляющего большинства и включены. Но, как по мне, прежде всего за счёт индивидуальных пользователей. Корпоративный сектор более консервативен по разным причинам и если ваш ресурс ориентирован на него, то, скорее всего, статистика использования браузеров будет отличаться от общемировой или региональной (даже тут специфика есть — если в регионе плохо с Интернетом, трафик дорогой и(или) скорости низкие, то новых версий будет заметно меньше в статистике даже на чисто гиковских ресурсах).

                0
                как пример, у нас поддержка ие11+… и вот даже при данном стечении обстоятельств, мы можем юзать только let и const, и то с некоторыми ограничениями в виде циклов))))
                  0
                  Особенно удобно использовать let и const в developer тулзах. После игр с новыми волшебными классами я окончательно убедился в том что старое хотя бы предсказуемо и стабильно работает. При том у меня есть сильное подозрение что let и const по факту нужны только для генерации исключений в момент компиляции и приводят только к деоптимизации кода на низком уровне путём создания лишних замыканий в тех местах где они могут быть не нужны.
                    0
                    Если сможете это подозрение оформить в виде статьи с замерами, было бы замечательно.
                      0
                      Это не имеет смысла. Спецификация не описывает реализацию, а это значит что идеальный jit сделает оптимальный код. Ресёрчем я могу только подтвердить подозрения. И это будут не замеры, а сравнение генерируемого V8 байткода.
                      –1
                      let, кстати, местами совершенно неочевидно работает. Например, в for'е.
                        0
                        А можно поточнее? Я лично стараюсь юзать иммутабельные переменные и let использую в for-е в качестве счетчика и пока не замечал некорректного поведения
                          0
                          А знаете, что в for in/of можно const использовать? :)
                            0
                            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]());
                            >2
                            >1
                            >3

                              0
                              Благодарю за пример, познавательно) Но зачем лишний раз инкрементить i? Сразу не заметил и не мог понять прикола. А так да, вы пропускаете лишнюю итерацию, все логично
                                0
                                Долго искать в стандартах, но действует примерно так — для каждого прогона тела цикла создаются копии переменных, в них копируются текущие значения, а после прогона тела их значения копируются обратно в переменные (по крайней мере, Бабель как-то так делает).
                                Сделано это, как я понял, для упрощения стандартных действий с циклами — чтобы и замыкать можно было каждый раз на новое значение переменной, и со счётчиками можно было работать как раньше, меняя их вручную.
                                  0
                                  Благодарю, кстати этот же код с var работает совсем некорректно. Сам на js пишу редко и каждый раз такие вещи заставляют улыбаться. Так что по факту let сделал из этого кода хоть что-то работоспособное)
                                    0
                                    этот же код с var работает совсем некорректно
                                    C var он работает неудобно, но понятно — функции в массиве замкнуты на одну и ту же переменную и обе возвращают одно и то же значение.
                                      0
                                      С замыканиями работает вполне корректно:
                                      var a = [];
                                      for(var i = 0;i<4;i++){
                                        a.push(function(i){
                                          return function(){
                                            return i;
                                          };
                                        }(i));
                                        i++;
                                      }
                                      console.log(a.length);
                                      console.log(a[0]());
                                      console.log(a[1]());
                                        0
                                        Так он всегда корректно работает — согласно стандарту. Просто немного не так, как от него можно было ожидать.

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое