Давай посмотрим, посмотрим

Когда кто-то начинает программировать, одна из основных концепций, с которой они знакомятся, - это разрешение имен, также известное как область видимости. Области очень важны, потому что они определяют, видима переменная / метод или нет. Кроме того, области видимости помогают создать аккуратное и чистое глобальное пространство имен, потому что вы можете «скрыть» свои переменные в подпространстве.

После изучения основ программирования области действия по-прежнему остаются очень важными, потому что они необходимы для понимания того, что происходит в более продвинутой концепции, метапрограммировании. Многие средства метапрограммирования работают, особенно в Ruby, путем изменения кода области видимости, под которым выполняется. Например, методы class_eval или instance_eval изменят область видимости на другой объект / класс.

Чтобы лучше понять эту концепцию, давайте подробнее рассмотрим области применения. Какие типы прицелов существуют и какой уровень прицелов обычно используется?

Что такое объем?

Определение таково: область действия - это часть программы.

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

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

Лексическая область видимости

Насколько я знаю, это тот тип, который реализуют все современные языки. Лексическая область видимости означает, что контекст исходного кода - это область видимости. Например:

Все ваши переменные определены в контексте вашего текущего метода. Внутри блока мы получаем доступ к переменным из метода, который находится на один уровень выше. Но когда вы читаете код, становится очень ясно, каково значение method_variable из-за контекста.

Динамическое определение объема

Эта реализация больше не используется на многих языках. Некоторые из них, которые все еще используются и реализуют такое определение области видимости, - это Perl (только если пользователь соглашается), Bash, Emacs Lisp и LaTeX.

Итак, как работает динамическая область видимости? Разрешение имен зависит от среды выполнения. Давайте откорректируем приведенный выше пример, чтобы увидеть разницу. (Помните, что Ruby не использует динамическую область видимости, поэтому это псевдо-код Ruby, и он не будет работать в вашей консоли).

В языке с динамической областью видимости среда выполнения ищет переменную в текущем блоке, а затем ищет переменную в стеке вызовов выше. В нашем примере метод multiply_by_two имеет переменную multiplicand с инициализированным 2 и метод multiply_by_three с 3. Результат test_method действительно зависит от метода, который он вызывает.

В этом примере недостаток очевиден. Динамическое определение области сводит на нет преимущества ссылочной прозрачности, поскольку она зависит от стека вызовов / времени выполнения, и очень сложно определить значение переменной.

Какие у нас обычно уровни обзора?

Уровни объема

Глобальный

Как следует из названия, глобальная область видимости - это когда переменная или метод видны во всем приложении. Это не обязательно плохо для небольших скриптов, но по мере роста вашего приложения широкое использование глобальных переменных затруднит сопровождение вашего кода.

Кроме того, глобальные переменные особенно сложно понять, когда дело касается многопоточности.

Модуль

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

Файл

В области видимости файла переменная видна только внутри файла. Этот уровень в основном используется в C (иногда и в C ++). В C можно определить переменную в файле, но не экспортировать ее, поэтому другие функции не могут ее использовать. Если разработчик хочет использовать переменную в файловой области, он должен определить ее в начале файла, и это не должно быть внутри функции.

Область видимости файла очень похожа на область видимости модуля в том особом случае, когда файл является модулем.

Функция

В области видимости функции функция определяется в переменной и доступна в любом подблоке этой функции.

Именно так мы использовали переменную method_variable в нашем примере для лексической области видимости.

В функциях JavaScript область видимости функции является уровнем области видимости по умолчанию для каждой переменной, которая инициализируется ключевым словом var (если переменная инициализируется без какого-либо ключевого слова, по умолчанию она становится глобальной переменной). Почему так? Потому что JavaScript неявно помещает любую из этих переменных в начало функции.

Здесь переменная x все еще доступна, хотя она определена в подблоке функции.

Неявное изменение порядка переменных с var используется, чтобы заставить работать забавный код:

Современные версии Chrome или Firefox больше не работают таким образом, но все же переменная, определенная с помощью var, видна во всей функции, а не только в блоке.

Блокировать

Функцию можно разделить на подблоки. Во многих языках типа C блок окружен {} - в случае Ruby мы чаще используем do end.

Выражение

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

Это может выглядеть так:

let val x = some_function() in x * x end

Здесь мы используем переменную x в качестве кеша, поэтому нам не нужно вызывать функцию some_function() дважды.

Примечание. Ключевое слово let также существует в нефункциональных языках - например, в JavaScript или Rust - но в этих языках это не имеет ничего общего с областью видимости переменной.

Связанные концепции

Динамическая отправка очень тесно связана с определением объема. Это описывает, как разрешать переменные / методы экземпляра для объектов (большую часть времени).

Закрытие

Иногда замыкания сбивают с толку, когда дело доходит до области видимости. Почему это так? Потому что, вообще говоря, они выполняются не в том месте, где они закодированы.

Тем не менее, они имеют лексическую область видимости, потому что контекст кода важен для разрешения имен, а не для среды выполнения.

Заключение

Если мы внимательно рассмотрим область видимости нашего кода и будем помнить о контексте, в котором он выполняется, мы сможем написать лучший код и не запутаться, когда будем использовать предметно-ориентированные языки (которые изменяют области видимости) - что-то довольно распространенное. в сообществе Ruby, особенно в Rails, но не так уж редко и в JavaScript.