Параметр уловлен JavaScript уже определен
Я пытаюсь понять, почему я получаю следующую ошибку, а не как ее обойти.
Передача следующего кода в JSLint или JSHint дает ошибку "err" уже определено.
/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: true, windows: true, bitwise: true, newcap: true, strict: true, maxerr: 50, indent: 4 */
function xyzzy() {
"use strict";
try { /*Step 1*/ } catch (err) { }
try { /*Step 2*/ } catch (err) { }
}
Очевидное предположение здесь состоит в том, что catch
ведет себя или должно вести себя как функция. Таким образом, err
не является глобальной переменной или локальной переменной для xyzzy
, а параметром для блока catch
.
При просмотре ECMA-262 Standard, раздел 12.14, описывающий инструкцию try
указывает, что предложение catch
принимает идентификатор, привязанный к исключению. Кроме того, правило семантического производства для catch
относится к параметру, который передал вызывной идентификатор в качестве аргумента.
Это, по-видимому, подсказывает случайному читателю, что приведенный выше код действителен и что, возможно, в инструментах lint есть ошибка.
Даже IntelliJ строгий анализ проверки кода JavaScript не сообщает об этом проблема с переопределением err
.
Больше беспокойства, если речь идет о переменной неопределенности, то можно предположить, что err
истекает кровью в глобальное пространство, что создает целый ряд других проблем, и вместо этого следует объявить его перед, вот так:
/*jslint white: true, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: true, plusplus: true, windows: true, bitwise: true, newcap: true, strict: true, maxerr: 50, indent: 4 */
function xyzzy() {
"use strict";
var err; // DECLARE err SO IT IS CERTAINLY LOCAL
try { /*Step 1*/ } catch (err) { }
try { /*Step 2*/ } catch (err) { }
}
Но это приводит только к двум ошибкам о err
в каждом из операторов catch, что делает проблему хуже и потенциально представляет затенением переменных.
Инструменты lint показывают, что каждый блок catch
вводит не только его собственную лексическую область, но и новую переменную. Это не может быть правильно.
Простое создание err1
, err2
,... для успокоения инструментов статического анализа просто скрывает симптом и не способствует созданию более чистого кода.
Гугеры JavaScript. Является ли это ошибкой в инструменте lint, темном углу с спецификацией JavaScript или фундаментальном непонимании того, что происходит здесь?
ОБНОВЛЕНИЕ: Написал Douglas Crockford, автору JSLint, и, оказывается, есть очень веская причина для этого предупреждения. См. Ответ ниже.
Ответы
Ответ 1
Написал Дуглас Крокфорд, автор JSLint, об этой проблеме.
В конце концов, оказывается, очень важная причина...
Дуглас пишет:
Перемещаемые переменные не отображаются правильно, поэтому я рекомендую вам использовать другое имя в каждом из них.
Если вы посмотрите на fooobar.com/questions/180010/..., вы заметите, что PleaseStand начал касаться этого. Не все браузеры, особенно исторические, обрабатывают область видимости правильно или последовательно.
JSLint распознает, что ваш код может работать в одном браузере, но не в другом, оставляя очень неприятную и тонкую ошибку для отслеживания. Предупреждение реально.
Используя другое имя, которое, да, не кажется чистым или кратким вообще, потому что это не так, это универсальный способ не работать в этой проблеме.
Спасибо Дугласу за разъяснение! Тайна решена.
Ответ 2
В спецификации достаточно ясно, что любое имя, определяемое оператором catch, будет ничего, кроме теневого окружающего имени. Кроме того, я бы не рассматривал эти ошибки как не что иное, как предупреждение. Просто используя чистую интуицию, я считаю, что это просто чрезмерный анализ со стороны дизайнера этих инструментов Линта.
Поскольку блок catch вводит новую область, одноименное имя просто затеняет любые похожие имена в охватывающей области. Это не обязательно плохо, если вы знаете о семантике. Если вы кодируете в предположении, что приложение err
будет доступно, чем вам нужно будет изменить свои предположения.
Спецификация
Производственный блок Catch: catch (Identifier) оценивается следующим образом:
- Пусть C - это параметр, который был передан этой продукции.
- Пусть oldEnv - это исполняемые контексты выполнения LexicalEnvironment.
- Пусть catchEnv будет результатом вызова NewDeclarativeEnvironment, передающего oldEnv в качестве аргумента.
- Вызвать конкретный метод CreateMutableBinding для catchEnv, передав значение идентификатора String в качестве аргумента.
- Вызвать конкретный метод SetMutableBinding для catchEnv, передавая идентификаторы, C и false в качестве аргументов. Обратите внимание, что последний аргумент в этой ситуации несуществен.
- Задайте текущие контексты выполнения LexicalEnvironment для catchEnv.
- Пусть B - результат оценки блока.
- Установите текущие контексты выполнения LexicalEnvironment на oldEnv.
- Возврат B.
ПРИМЕЧАНИЕ Независимо от того, как элемент управления оставляет блок, LexicalEnvironment всегда восстанавливается в прежнее состояние.
Ответ 3
Отметьте этот ответ:
JSLint жалуется на мой try/catch
Как уже упоминалось, try
открывает новый блок-область. См. https://developer.mozilla.org/en/JavaScript/Reference/Scope_Cheatsheet
Действительно, верхняя часть документа объясняет, что это не все стандартное, но в ES5, раздел 12.14 раздел о выполнении catch
блок четко определяет описание MDC как стандартное:
Независимо от того, как элемент управления покидает блок, LexicalEnvironment всегда восстанавливается до прежнего состояния.
Ответ 4
У вас может возникнуть проблема с повторным использованием одного и того же имени параметра при последовательных попытках, если впоследствии вам понадобится обратиться к ошибке.
Если было вызвано более одного улова, только последнее будет доступно для выражения finally или функции.
jsLint консервативен - если вы можете предотвратить зависание с уникальной переменной, почему бы не использовать его?