Область видимости переменной


Содержание

Область видимости переменных, константы

C# — Руководство по C# — Область видимости переменных, константы

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

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

Поле, также известное как переменная-член класса, находится в области видимости до тех пор, пока в этой области находится содержащий поле класс.

Локальная переменная находится в области видимости до тех пор, пока закрывающая фигурная скобка не укажет конец блока операторов или метода, в котором она объявлена.

Локальная переменная, объявленная в операторах цикла for, while или подобных им, видима в пределах тела цикла.

Конфликты областей видимости локальных переменных

Использование в больших программах одних и тех же имен переменных в разных частях программы является обычной практикой. Это нормально до тех пор, пока области видимости этих переменных не перекрываются и находятся в совершенно разных частях программы, таким образом исключая любую неоднозначность. Однако следует иметь в виду, что локальные переменные с одним и тем же именем не могут быть объявлены дважды в одном и том же контексте, поэтому вы не сможете поступить так, как показано ниже:

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

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

Вот другой пример:

Если вы попытаетесь скомпилировать это, то получите следующее сообщение об ошибке:

ScopeTest.cs (12,15) : error CS0136: A local variable named ‘3’ cannot be declared in this scope because it would give a different meaning to ‘j’, which is already used in a ‘parent or current’ scope to denote something else

Дело в том, что переменная j, которая определена перед началом цикла for, внутри цикла все еще находится в области видимости и не может из нее выйти до завершения метода Main(). Хотя вторая переменная j (недопустимая) объявлена в контексте цикла, этот контекст вложен в контекст метода Main(). Компилятор не может различить эти две переменных, поэтому не допустит объявления второй из них.

Конфликты областей видимости полей и локальных переменных

В некоторых случаях два идентификатора с одинаковыми именами (хотя и не совпадающими полностью уточненными именами) и одинаковой областью видимости можно различить, и тогда компилятор допускает объявление второй переменной. Причина в том, что C# делает принципиальное различие между переменными, объявленными на уровне типа (полями) и переменными, объявленными в методах (локальными). Рассмотрим следующий фрагмент кода:

Этот код компилируется, несмотря на то, что здесь в контексте метода Main() присутствуют две переменных с именем j: переменная j, определенная на уровне класса и существующая до тех пор, пока не будет уничтожен класс (когда завершится метод Main(), а вместе с ним и программа), и переменная j, определенная внутри Main(). В данном случае новая переменная с именем j, объявленная в методе Main(), скрывает переменную уровня класса с тем же именем. Поэтому когда вы запустите этот код, на дисплее будет отображено число 30.

Константы

Как следует из названия, константа — это переменная, значение которой не меняется за время ее существования. Предваряя переменную ключевым словом const при ее объявлении и инициализации, вы объявляете ее как константу:

Ниже перечислены основные характеристики констант:

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

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

Константы всегда неявно статические. Однако вы не должны (и фактически не можете) включать модификатор static в объявление константы.

Использование констант в программах обеспечивает, по крайней мере, три преимущества:

Константы облегчают чтение программ, заменяя «магические» числа и строки читаемыми именами, назначение которых легко понять.

Константы облегчают модификацию программ. Например, предположим, что в программе C# имеется константа SalesTax (налог с продаж), которой присвоено значение 6 процентов. Если налог с продаж когда-нибудь изменится, вы можете модифицировать все вычисления налога, просто присвоив новое значение этой константе, и не понадобится просматривать код в поисках значений и изменять каждое из них, надеясь, что оно нигде не будет пропущено.

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

Область видимости объектов

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

Локальные и глобальные переменные

Время жизни объекта может быть глобальным и локальным.

Глобальными называют объекты, объявление которых дано вне функции. Они доступны (видимы) во всем файле, в котором они объявлены. В течение всего времени выполнения программы с глобальным объектом ассоциирована некоторая ячейка памяти.

Локальными называют объекты, объявление которых дано внутри блока или функции. Эти объекты доступны только внутри того блока, в котором они объявлены. Объектам с локальным временем жизни выделяется новая ячейка памяти каждый раз при осуществлении описания внутри блока. Когда выполнение блока завершается, память, выделенная под локальный объект, освобождается, и объект теряет своё значение.

Область видимости локальной переменной k — функция autofunc() . Каждый раз при входе в функцию с идентификатором k ассоциируется некоторая ячейка памяти, в которую помещается значение равное 1.

Результат выполнения программы

Та же программа, но с использованием глобального объекта

Результат выполнения программы

С помощью глобальных переменных можно организовать обмен информацией между функциями. При этом вызываемая функция не будет принимать значения глобальных переменных в качестве формальных аргументов. Однако в этом случае существует опасность случайного изменения глобальных объектов другими функциями.

Модификация объектов

Модификация или видоизменение объектов в языке Си применяется для того, чтобы изменить диапазон значений или область действия объекта. Ключевые слова, которые применяются для модификации, называются модификаторами .

Модификатор unsigned предназначен для того, чтобы объявлять беззнаковую целочисленную переменную, тем самым изменив диапазон представления этой переменной.

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

Пример окна проекта, состоящего из двух файлов

Модификатор static позволяет связать с идентификатором фиксированный адрес (ячейку памяти). Если объект расположен по некоторому фиксированному адресу, то он называется статическим .

Объект, который располагается в произвольном месте оперативной памяти, называется динамическим . Если необходимо динамический объект сделать статическим, то используется модификатор static . Переменные, объявленные с использованием модификатора static сохраняют свои значения при входе и выходе из функции, однако не являются глобальными.

Переменная k в функции autofunc() зафиксирована в оперативной памяти. Инициализация k проводится только один раз — при первом вызове функции. При повторном обращении к функции autofunc() инициализация переменной k не будет производиться. Значение переменной k и ее адрес сохраняются в оперативной памяти, однако эта переменная не будет доступна из других функций.

Результат выполнения программы

Модификатор register предназначен для того, чтобы поместить переменную в один из регистров общего назначения центрального процессора при наличии свободного регистра. Благодаря этому повышается скорость работы с данными. Это необходимо для создания управляющих программ, где требуется высокая скорость обработки данных.


Время жизни и область видимости переменной

Лабораторная работа №4

Программирование пользовательских функций.

Цель работы:

1. Изучить методы создания и использования функций.

  1. Получить навыки работы с функциями и передачи им параметров по значению.

Теоретические сведения.

Функцией называется выделенная последовательность инструкций, предназначенных для решения определенной задачи. Ранее мы уже использовали библиотечные функции ввода-вывода printf() и scanf(), в данной лабораторной работе познакомимся с правилами создания своих (пользовательских) функций.

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

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

общем случае она выполняет следующие действия:

· выполняет инструкции, согласно заложенному алгоритму;

· может возвращать результат в вызывающую программу.

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

Время жизни и область видимости переменной.

Время жизни и область видимости переменной связаны с блоком программы, в языке С различают два вида программных блоков:

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

Глобальная переменная существует на протяжении всего времени выполнения программы.

Локальная переменная существует только во время выполнения блока, в котором она определена.

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

Переменная может быть видима в пределах:

· во всех модулях (если программа располагается в нескольких файлах).

Область видимости переменной зависит от того, в каком месте программы (на каком уровне) она объявлена. Если переменная объявляется вне всех блоков программы (обычно вначале программы, до функции main()), то это внешнее объявление (глобальная переменная).

Внутри блока или функции объявляются локальные переменные.

При каждом входе в блок (или функцию) локальная переменная создается (для неё выделяется память), а при выходе из блока переменная уничтожается.

Пример 1:

int q=0; // глобальная переменная q

q++;// глобальные переменные доступны во всех блоках и функциях

int k,l; // локальные переменные вложенного блока

i++; // локальные переменные из объемлющего блока (функция main) доступны во вложенном блоке

>//_____________конец вложенного блок

k++ // ошибка. переменной k уже нет

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Для студента самое главное не сдать экзамен, а вовремя вспомнить про него. 10039 — | 7504 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Переменные

Переменные

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

Файл 1 Файл 2
auto double int struct
break else long switch
register typedef char extern
return void case float
unsigned default for signed
union do if sizeof
volatile continue enum short
while inline

А также ряд других слов, специфичных для данной версии компилятора, например far, near, tiny, huge, asm, asm_ и пр.

Например, правильные идентификаторы
a, _, _1_, Sarkasm, a_long_variable, aLongVariable, var19, defaultX, char_type
неверные
1a, $value, a-long-value, short

Си — регистрозависимый язык. Переменные с именами a и A, или end и END, или perfectDark и PerfectDarK – это различные переменные.

Типы переменных

Т ип переменной определяет

  • 1) Размер переменной в байтах (сколько байт памяти выделит компьютер для хранения значения)
  • 2) Представление переменной в памяти (как в двоичном виде будут расположены биты в выделенной области памяти).

В си несколько основных типов. Разделим их на две группы — целые и числа с плавающей точкой.

Целые

  • char — размер 1 байт. Всегда! Это нужно запомнить.
  • short — размер 2 байта
  • int — размер 4 байта
  • long — размер 4 байта
  • long long — размер 8 байт.

Здесь следует сделать замечание. Размер переменных в си не определён явно, как размер в байтах. В стандарте только указано, что

Указанные выше значения характерны для компилятора VC2012 на 32-разрядной машине. Так что, если ваша программа зависит от размера переменной, не поленитесь узнать её размер.


Теперь давайте определим максимальное и минимальное число, которое может хранить переменная каждого из типов. Числа могут быть как положительными, так и отрицательными. Отрицательные числа используют один бит для хранения знака. Иногда знак необходим (например, храним счёт в банке, температуру, координату и т.д.), а иногда в нём нет необходимости (вес, размер массива, возраст человека и т.д.). Для этого в си используется модификатор типа signed и unsigned. unsigned char — все 8 бит под число, итого имеем набор чисел от 00000000 до 11111111 в двоичном виде, то есть от 0 до 255 signed char от -128 до 128. В си переменные по умолчанию со знаком. Поэтому запись char и signed char эквивалентны.

Таб. 1 Размер целых типов в си.

Тип Размер, байт Минимальное значение Максимальное значение
unsigned char 1 255
signed char
( char )
1 -128 127
unsigned short 2 65535
signed short
( short )
2 -32768 32767
unsigned int
( unsigned )
4 4294967296
signed int
( int )
4 -2147483648 2147483647
unsigned long 4 4294967296
signed long
( long )
4 -2147483648 2147483647
unsigned long long 8 18446744073709551615
signed long long
( long long )
8 -9223372036854775808 9223372036854775807

sizeof

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

(Я думаю ясно, что переменные могут иметь любое валидное имя). Эту программу можно было написать и проще

В си один и тот же тип может иметь несколько названий
short === short int
long === long int
long long === long long int
unsigned int === unsigned

Типы с плавающей точкой

  • float — 4 байта,
  • long float — 8 байт
  • double — 8 байт
  • long double — 8 байт.

Здесь также приведены значения для VC2012, по стандарту размер типов float Таб. 2 Размер типов с плавающей точкой в си.

Тип Размер, байт Количество значащих знаков мантиссы Минимальное значение Максимальное значение float 4 6-7 1.175494351 E – 38 3.402823466 E + 38 double 8 15-16 2.2250738585072014 E – 308 1.7976931348623158 E + 308

Переполнение переменных

Си не следит за переполнением переменных. Это значит, что постоянно увеличивая значение, скажем, переменной типа int в конце концов мы «сбросим значение»

Вообще, поведение при переполнении переменной определено только для типа unsigned: Беззнаковое целое сбросит значение. Для остальных типов может произойти что угодно, и если вам необходимо следить за переполнением, делайте это вручную, проверяя аргументы, либо используйте иные способы, зависящие от компилятора и архитектуры процессора.

Постфиксное обозначение типа

П ри работе с числами можно с помощью литер в конце числа явно указывать его тип, например

  • 11 — число типа int
  • 10u — unsigned
  • 22l или 22L — long
  • 3890ll или 3890LL — long long (а также lL или Ll)
  • 80.0f или 80.f или 80.0F — float (обязательно наличие десятичной точки в записи)
  • 3.0 — число типа double

Экспоненциальная форма записи также по умолчанию обозначает число типа double.

Следующий код, однако, не будет приводить к ошибкам, потому что происходит неявное преобразование типа

Шестнадцатеричный и восьмеричный формат

В о время работы с числами можно использовать шестнадцатеричный и восьмеричный формат представления. Числа в шестнадцатиричной системе счисления начинаются с 0x, в восьмеричной системе с нуля. Соответственно, если число начинается с нуля, то в нём не должно быть цифр выше 7:

Экспоненциальная форма представления чисел

Э кспоненциальной формой представления числа называют представление числа в виде M e ± p , где M — мантиса числа, p — степень десяти. При этом у мантисы должен быть один ненулевой знак перед десятичной запятой.
Например 1.25 === 1.25e0, 123.5 === 1.235e2, 0.0002341 === 2.341e-4 и т.д.
Представления 3.2435e7 эквивалентно 3.2435e+7
Существеут и другое представление («инженерное»), в котором степень должна быть кратной тройке. Например 1.25 === 1.25e0, 123.5 === 123.5e0, 0.0002341 === 234.1e-6, 0.25873256 === 258.73256e-3 и т.д.

Объявление переменных

В си переменные объявляются всегда в начале блока (блок — участок кода ,ограниченный фигурными скобками)

При объявлении переменной пишется её тип и имя.

Можно объявить несколько переменных одного типа, разделив имена запятой

Здесь объявлены переменные a и b внутри функции main, и переменная z внутри тела цикла. Следующий код вызовет ошибку компиляции

Это связано с тем, что объявление переменной стоит после оператора присваивания. При объявлении переменных можно их сразу инициализировать.
int i = 0;
При этом инициализация при объявлении переменной не считается за отдельный оператор, поэтому следующий код будет работать

Начальное значение переменной

О чень важно запомнить, что переменные в си не инициализируются по умолчанию нулями, как во многих других языках программирования. После объявления переменной в ней хранится «мусор» — случайное значение, которое осталось в той области памяти, которая была выделена под переменную. Это связано, в первую очередь, с оптимизацией работы программы: если нет необходимости в инициализации, то незачем тратить ресурсы для записи нулей (замечание: глобальные переменные инициализируются нулями, почему так, читайте в этой статье).

Если выполнять эту программу на VC, то во время выполнения вылетит предупреждение
Run-Time Check Failure #3 — The variable ‘i’ is being used without being initialized.
Если нажать «Продолжить», то программа выведет «мусор». В многих других компиляторах при выполнении программы не будет предупреждения.

Область видимости переменной

П еременные бывают локальными (объявленными внутри какой-нибудь функции) и глобальными. Глобальная переменная видна всем функциям, объявленным в данном файле. Локальная переменная ограничена своей областью видимости. Когда я говорю, что переменная «видна в каком-то месте», это означает, что в этом месте она определена и её можно использовать. Например, рассмотрим программу, в которой есть глобальная переменная

Будет выведено
foo: 100
bar: 333
Здесь глобальная переменная global видна всем функциям. Но аргумент функции затирает глобальную переменную, поэтому при передаче аргумента 333 выводится локальное значение 333.
Вот другой пример

Программа выведет 555. Также, как и в прошлом случае, локальная переменная «важнее». Переменная, объявленная в некоторой области видимости не видна вне её, например

Этот пример не скомпилируется, потому что переменная y существует только внутри своего блока.
Вот ещё пример, когда переменные, объявленные внутри блока перекрывают друг друга

Программа выведет
30
20
10
Глобальных переменных необходимо избегать. Очень часто можно услышать такое. Давайте попытаемся разобраться, почему. В ваших простых проектах глобальные переменные выглядят вполне нормально. Но представьте, что у вас приложение, которое

  • 1) Разрабатывается несколькими людьми и состоит из сотен тысяч строк кода
  • 2) Работает в несколько потоков

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

Безусловно, есть ситуации, когда глобальные переменные упрощают программу, но такие ситуации случаются не часто и не в ваших домашних заданиях, так что НЕ СОЗДАВАЙТЕ ГЛОБАЛЬНЫХ ПЕРЕМЕННЫХ!
Переменные могут быть не только целочисленными и с плавающей точкой. Существует множество других типов, которые мы будем изучать в дальнейшем.

Время существования и область видимости переменных в C++

технические науки

  • Дмитриев Владислав Леонидович , кандидат наук, доцент, доцент
  • Башкирский государственный университет

  • ЯЗЫК ПРОГРАММИРОВАНИЯ С++
  • ПРОГРАММИРОВАНИЕ
  • ОБЛАСТЬ ВИДИМОСТИ ПЕРЕМЕННОЙ
  • МОДИФИКАТОРЫ

Похожие материалы

Каждая переменная, объявленная в программе, имеет две важнейшие характеристики: время существования и область видимости. Эти характеристики взаимосвязаны и влияют на использование переменной в программе. Объявление переменной вводит ее имя в область видимости. Это означает, что имя может использоваться только в определенном месте программы.

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

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

Переменная называется глобальной, если она объявлена вне любой функции (даже main()), класса или пространства имен. Область видимости глобальных переменных начинается от места их объявления до конца файла, содержащего объявление.

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

Локальную и глобальную переменные можно объявлять с одним и тем же именем. При этом объявление имени переменной в блоке скрывает объявление этого же имени во внешнем блоке или глобальное имя переменной. После выхода из блока имя внешней по отношению к этому блоку переменной восстанавливает свой смысл.

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

Тем не менее, обращение из какого-либо блока к глобальной переменной с тем же именем, что и в данном блоке, может быть организовано посредством использования операции разрешения области видимости (::). Операция (::) дает доступ к глобальной переменной, даже если под тем же именем в области действия объявлена локальная переменная. Приведем пример, поясняющий только что сказанное:

Результат выполнения программы будет следующий:

Способа обращения к скрытой локальной переменной не существует. Как вариант в случае необходимости получения доступа к локальной переменной из вложенного блока можно рассматривать использование указателя:

Управлять областью видимости и временем существования переменных (или других объектов) можно либо изменением места объявления переменной в программе, либо используя модификаторы (классы памяти) auto, register, extern, static, mutable. Если класс памяти не указан явным образом, он определяется компилятором исходя из контекста объявления переменной. Более подробно о продолжительности хранения данных в памяти можно прочитать в [1].

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

Регистровая (register) переменная отличается от автоматической только памятью, выделяемой для ее хранения. Регистровая переменная хранится (по возможности) в регистрах процессора, и поэтому доступ к такой переменной осуществляется гораздо быстрее (переменная класса памяти auto хранится в оперативной памяти). Если свободные регистры памяти отсутствуют, то регистровая переменная становится автоматической.

Внешняя (extern) переменная является глобальной переменной, однако модификатор extern означает, что переменная будет объявлена (без слова extern) в другом месте программы или другом файле, где ей и будет выделена память. Поэтому такой класс памяти используется для создания переменных, доступных во всех модулях программы, в которых они объявлены. Пример:

Статической (static) переменной (или константе) выделяется память после ее объявления и сохраняется до конца выполнения программы. Время жизни такой переменной — постоянное. Такие переменные при объявлении по умолчании инициализируются нулевыми (арифметические типы) или пустыми значениями. В зависимости от места объявления, статические переменные могут быть как глобальными, так и локальными.

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

В данном случае переменная s в функции sum описана как static, поэтому она не уничтожается после каждого вызова функции, и, тем самым, позволяет наращивать свое значение при каждом новом вызове функции. В результате выполнения программы на экране будет отображено следующее содержимое:

Сравните результат выполнения программы для случая, если вместо строки

Ключевое слово mutable задается в контексте использования спецификатора const. С его помощью можно указать, что некоторый элемент структуры (или класса) может быть изменен, даже если переменная типа структуры (или класса) объявлена со спецификатором const. В примере ниже показано, как это может быть реализовано.

Здесь спецификатор const структуры CL предотвращает изменение ее элементов, однако спецификатор mutable, предшествующий ее элементам name1, percent и sum, снимает с данных элементов такое ограничение.

Замечание. Если при описании объекта (или переменной) инициализатор не задан, то глобальным объектам, объектам из пространства имен и локальным статическим объектам присваивается нулевое значение соответствующего типа.

Список литературы

  1. Прата С. Язык программирования С++. Лекции и упражнения, 5-е изд.: Пер. с англ. – М.: Вильямс, 2007. – 1184 с.

Электронное периодическое издание зарегистрировано в Федеральной службе по надзору в сфере связи, информационных технологий и массовых коммуникаций (Роскомнадзор), свидетельство о регистрации СМИ — ЭЛ № ФС77-41429 от 23.07.2010 г.

Соучредители СМИ: Долганов А.А., Майоров Е.В.

JavaScript — Область видимости и контекст выполнения

Статья в разработке!

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

Область видимости (scope). Цепочка областей видимости

Область видимости определяет доступность (видимость) переменных и функций вида function declaration statement.

Области видимости создаются во время выполнения JavaScript программы (сценария).

В языке JavaScript (до ECMAScript 2015) выделяют 2 области видимости:

  • глобальная (переменная или функция, созданная в этой области видимости, может быть доступна из любой точки программы);
  • локальная или функциональная (переменная или функция, созданная в этой области видимости, может быть доступна только внутри неё).


Локальная или функциональная область видимости создаётся во время вызова функции. При этом локальная область у каждого вызова функции, даже одной и той же, будет своя.

[[Scope]] — это скрытое внутреннее свойство функции, которое она получает во время вызова. Данное свойство содержит ссылку на ту область видимости, в которой данная функция была объявлена.

В приведённом примере во время вызова функции outputNum будет создана локальная область, а также установлено в качестве значения [[Scope]] область в которой данная функция была объявлена.

Т.к. переменной num нет в текущей области, то интерпретатор посредством [[Scope]] перейдёт в область (в данном случае глобальную) и попытается найти её там. В этой области (глобальной) она есть. А это значит, что в качестве num будет использоваться значение переменной num , находящейся в глобальной области видимости.

JavaScript всегда начинает поиск переменной или функции с текущей области видимости . Если она в ней не будет найдена, то интерпретатор переместится к следующей области , указанной в [[Scope]] , и попробует отыскать её там. После этого действия повторяются , т.е. при отсутствии искомой переменной или функции в просматриваемой области видимости, интерпретатор перемещается к следующей области посредством [[Scope]] и пытается обнаружить её там.

В результате поиск всегда заканчивается одним из двух нижеприведённых сценариев:

Первый сценарий: интерпретатор встретил искомое имя идентификатора в какой-нибудь области. В этом случае он берёт его значение. Поиск искомого имени идентификатора в других областях видимости, т.е. дальнейшие перемещения по [[Scope]] , не выполняются.

Второй сценарий: интерпретатор не нашёл нужный идентификатор. В этом случае он бросает ошибку о том, что данный идентификатор не определён. Данный сценарий может возникнуть только тогда, когда интерпретатор в поиске переменной или функции дошёл до глобальной области видимости и не нашёл её в ней.

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

Последовательность областей видимости, которые интерпретатор использует при разрешении имени идентификатора, называется в JavaScript цепочкой областей видимости (scope chain).

В этом примере интерпретатор при разрешении переменной color дойдёт от текущей до глобальной области видимости:

В этом примере значение переменной drink будет взято из локальной области, созданной во время вызова внешней функции:

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

При разрешении имени, интерпретатор начинает поиск, начиная с текущей области видимости. Поэтому этот пример выведет в консоль текст «Дима».

Если в функции displayName не была бы объявлена переменная name, то интерпретатор не обнаружил бы её в области, созданной во время вызова этой функции. В этом случае он взял бы значение из области видимости, созданной во время вызова функции getName. В результате в консоль был бы выведен текст «Иван».

В этом примере функция f1 объявлена в глобальной области видимости. Поэтому свойство [[Scope]] функции f1, во время её вызова, будет содержать ссылку на глобальную область видимости даже несмотря на то, что данная функция вызвана в локальной области видимости, созданной во время вызова функции f2.

Поднятие (hoisting) функций и переменных

Функции вида function declaration statement можно использовать в JavaScript до их объявления. Это происходит из-за того что они поднимаются (hoisting) или другими словами «перемещаются» в начало текущего контекста.

Но поднятие в JavaScript выполняется не только функций вида function declaration statement, но и переменных, объявленных с помощью ключевого слова var. При этом поднятие осуществляется только самого объявления переменной.

Вышеприведённый код после поднятия будет аналогичен следующему:

Локальные и глобальные переменные

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

Иными словами, глобальные переменные — это переменные, объявленные вне тела какой-либо функции, а локальные — это переменные, объявленные внутри тела какой-либо функции.

В JavaScript до ECMAScript 2015 (6 версия) блочных областей видимости не было. Т.е. любая переменная созданная с помощью ключевого слова var внутри блока будет видима и за его пределами.

В ECMAScript 2015 (6) были введены ключевые слова let и const. Они предназначены для создания переменных и констант, видимость которых будет ограничено блоком в котором они объявлены. Блочная область видимости в JavaScript определяется посредством фигурных скобок.

Например, переменная subject не будет видна за пределами блока:

Кроме этого переменные, объявленные с помощью let и константы, созданные посредством const в JavaScript не поднимаются (hoisting). Т.е. к ним нельзя обратиться до их непосредственного объявления.

Контекст функции. Ключевое слово this

Под контекстом функции понимается объект , в рамках которого выполняется функция. Обратиться к этому объекту внутри функции можно с помощью ключевого слова this .

Если функция не является методом объекта, то this будет указывать на window (глобальный объект).

В строгом режиме this в вышеприведённом примере будет равно undefined .

Если функция – это метод объекта, то this внутри тела функции будет указывать на него.

Указание контекста функции. Методы call и apply

В JavaScript можно явно указать контекст, в котором необходимо вызвать функцию.

Первый способ — это использовать метод call .

Второй способ — это использовать метод apply . Данный метод аналогичен call . Единственное отличие apply от call заключается в том, что аргументы в нём указываются посредством массива. Этот вариант передачи аргументов является очень удобным, особенно тогда когда их количество заранее неизвестно.

В качестве контекста методам call и apply , кроме объекта, можно ещё установить специальные значения null и undefined .

Не в строгом режиме в этом случае this будет указывать на объект window.

В строгом режиме this будет равно null.

Привязка функции к контексту (метод bind)

Функция (метод) в JavaScript не сохраняет контекст (объект) к которому она относится.

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

В результате вызова getModelMouse будет возвращена ошибка, т.к. контекст, к которому относится данный метод, будет потерян.

Метод bind предназначен для явной привязки контекста ( this ) к функции. Он в отличие от методов call и apply не вызывает функцию. Механизм этого метода заключается в создании функции-обёртки, которая будет устанавливать необходимый контекст целевой функции.

Кроме этого, эта функция-обёртка будет результатом, который метод bind будет возвращать в результате своего выполнения.

Метод bind также позволяет задать аргументы, которые необходимо передать функции в момент её вызова. Данные аргументы будут переданы целевой функции до аргументов, которые ей будут установлены явно при её вызове.

Синтаксис метода bind :

Например, изменим вышеприведённый пример. А именно, не просто сохраним ссылку на метод, а выполним это с привязкой его к объекту mouse:

В этом примере привяжем функцию changeScore к разным объектам. Функция changeScore, привязанная к первому объекту будет доступна по идентификатору changeScoreUser0001, а ко второму — по changeScoreUser0002.


В этом примере, рассмотрим как осуществляется передача аргументов функции, привязанной к некоторому объекту с помощью bind.

JavaScript — Цепочка областей видимости (пример 1)

В локальной области видимости, созданной во время выполнения функции sayHi можно получить значение переменной myName, myName1 и myName2. Переменная myName находится в текущей области выполнения,

  • myName2 — расположена в текущем контексте;
  • myName1 — расположена в родительской функции sayHello ;
  • myName — расположена в глобальном контексте.

А вот в функции sayHello() доступны только 2 переменные:

  • myName1 — расположена в текущем контексте;
  • myName — расположена в глобальном контекст.

Рассмотрим ещё один пример:

Функция sayHi() выведет в консоль «Привет, Дима». Это обусловлено тем, что переменная name существует в текущем контексте.

Функция sayHello() отобразит в консоли запись «Привет, Женя». Переменная name найдена в родительском (глобальном) контексте.

В языке JavaScript есть небольшая хитрость, которая заключается в том, что в функции можно всегда получить доступ к глобальной переменной, даже если имя этой переменной совпадает с локальной. Осуществляется это с помощью указания перед именем глобального контекста, т.е. window . JavaScript — Получение доступа к глобальной переменной через window

Ключевое слово var

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

Если Вы не используете строгий режим, то JavaScript позволяет создавать переменные без ключевого слова var . С такими переменными необходимо быть очень осторожными. Это связано с тем, что такая переменная вне зависимости от контекста, в котором она создана, автоматически становится глобальной. А это может привести к непредсказуемым результатам.

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

Если избавиться от ключевого слова var , то тогда переменная будет объявлена как глобальная переменная:

Некоторые особенности языка JavaScript при работе с переменными и функциями

У языка JavaScript есть одна интересная особенность, которая связана с тем как браузер понимает код, в котором использование переменной идёт до её объявления.

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

В этом примере вывод значения переменной myName в консоль идёт до её объявления и инициализации.

Как браузер понимает этот код? Браузер объявления переменных всегда поднимает вверх их функциональной области видимости.

Т.е. браузер будет видеть вышепредставленный код следующим образом:

В результате в консоли отобразится сообщение «Привет, undefined».

Кроме переменных, вверх их функциональной области видимости браузер поднимает ещё функции. Т.е. код JavaScript позволяет вызвать функцию до её объявления.

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

Поэтому следует придерживаться следующих правил:

  • Переменные и функции объявлять до их использования.
  • По возможности не использовать глобальные переменные или свести их количество к минимуму.

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

В JavaScript есть три области видимости: глобальная, область видимости функции и блочная. Область видимости переменной – это участок исходного кода программы, в котором переменные и функции видны и их можно использовать. Глобальную область видимости иначе ещё называют кодом верхнего уровня.

Глобальные переменные

Переменная, объявленная вне функции или блока, называется глобальной . Глобальная переменная доступна в любом месте исходного кода:

Локальные переменные

Переменная, объявленная внутри функции, называется локальной . Локальная переменная доступна в любом месте внутри тела функции, в которой она была объявлена. Локальная переменная создаётся каждый раз заново при вызове функции и уничтожается при выходе из неё (при завершении работы функции):

Локальная переменная имеет преимущество перед глобальной переменной с тем же именем, это означает, что внутри функции будет использоваться локальная переменная, а не глобальная:

Блочные переменные

Переменная, объявленная внутри блока с помощью ключевого слова let , называется блочной . Блочная переменная доступна в любом месте внутри блока, в котором она была объявлена:

Повторное объявление

Если с помощью ключевого слова var повторно объявить переменную с тем же именем (в той же области видимости), то ничего не произойдёт:

Если повторное объявление сопровождается инициализацией, то такая инструкция действует как обычное присваивание нового значения:

Если с помощью ключевого слова let повторно объявить переменную с тем же именем (в той же области видимости), то будет вызвана ошибка:

Цепочка областей видимости

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

В этом коде три области видимости: глобальная, область видимости функции foo() и область видимости функции bar() . В глобальной области видимости определены переменная num и функция foo() . В области видимости функции foo() определены переменная num2 и функция bar() , в ней также доступна переменная num из глобальной области видимости. Область видимости функции bar() содержит одну переменную num3 , которая доступна только внутри функции bar() . В области видимости функции bar() также доступны переменные из двух других областей, потому что они являются родительскими по отношению к ней. Цепочка областей видимости для этого примера представлена на рисунке ниже:

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

Цепочка областей видимости упорядочена. Интерпретатор производит поиск идентификаторов в цепочке областей видимости по направлению наружу, но не внутрь. Это означает, что поиск имени начинается с той области видимости, где было выполнено обращение к идентификатору. Если имя идентификатора обнаруживается, поиск прекращается. Если в текущей области видимости найти имя не удалось, выполняется поиск в следующей (во внешней) области видимости и т. д. Таким образом, будет использован идентификатор из той области видимости, в которой был найден. Если идентификатор не будет найден ни в одной из областей видимости JavaScript сгенерирует ошибку:

Если в теле функции необъявленной переменной присвоить значение то, в момент вызова функции, если в глобальной области видимости нет переменной с таким именем, будет создана новая глобальная переменная:

Подъём объявлений


В JavaScript объявленные переменные доступны в любом месте относительно своей области видимости, это означает, что переменные оказываются видимы ещё до того, как будут объявлены в коде. Эта особенность JavaScript неофициально называется подъёмом : программный код ведёт себя так, как если бы объявления переменных неявно поднимались (без инициализации) на самый верх относительно своей области видимости.

Рассмотрим следующий фрагмент кода:

Посмотрев на код, можно было бы подумать, что первый alert должен вывести строку «глобальная», потому что объявление локальной переменной str ещё не было выполнено. Однако, на деле выводится значение undefined . Благодаря подъёму объявлений функция выше эквивалентна реализации, приведённой ниже, в которой объявление переменной поднято в начало функции:

Тоже самое касается и глобальной области видимости, переменная объявленная снизу, доступна наверху:

Область видимости переменной

Область видимости переменной — это контекст, в котором эта переменная определена. В большинстве случаев все переменные PHP имеют только одну область видимости. Эта единая область видимости охватывает также включаемые (include) и требуемые (require) файлы. Например:

Здесь переменная $a будет доступна внутри включенного скрипта b.inc . Однако определение (тело) пользовательской функции задает локальную область видимости данной функции. Любая используемая внутри функции переменная по умолчанию ограничена локальной областью видимости функции. Например:

= 1 ; /* глобальная область видимости */

function test ()
<
echo $a ; /* ссылка на переменную локальной области видимости */
>

Этот скрипт не сгенерирует никакого вывода, поскольку выражение echo указывает на локальную версию переменной $a , а в пределах этой области видимости ей не было присвоено значение. Возможно вы заметили, что это немного отличается от языка C в том, что глобальные переменные в C автоматически доступны функциям, если только они не были перезаписаны локальным определением. Это может вызвать некоторые проблемы, поскольку люди могут нечаянно изменить глобальную переменную. В PHP, если глобальная переменная будет использоваться внутри функции, она должна быть объявлена глобальной внутри определения функции.

Ключевое слово global

Сначала пример использования global:

Пример #1 Использование global

function Sum ()
<
global $a , $b ;

Вышеприведенный скрипт выведет 3. После определения $a и $b внутри функции как global все ссылки на любую из этих переменных будут указывать на их глобальную версию. Не существует никаких ограничений на количество глобальных переменных, которые могут обрабатываться функцией.

Второй способ доступа к переменным глобальной области видимости — использование специального, определяемого PHP массива $GLOBALS . Предыдущий пример может быть переписан так:

Пример #2 Использование $GLOBALS вместо global

function Sum ()
<
$GLOBALS [ ‘b’ ] = $GLOBALS [ ‘a’ ] + $GLOBALS [ ‘b’ ];
>

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

Пример #3 Суперглобальные переменные и область видимости

function test_global ()
<
// Большинство предопределенных переменных не являются
// «супер», и чтобы быть доступными в локальной области
// видимости, функции требуют указания ‘global’.
global $HTTP_POST_VARS ;

echo $HTTP_POST_VARS [ ‘name’ ];

// Суперглобальные переменные доступны в любой области
// видимости и не требуют указания ‘global’.
// Суперглобальные переменные доступны, начиная с PHP 4.1.0, а
// использование HTTP_POST_VARS считается устаревшим.
echo $_POST [ ‘name’ ];
>
?>

Использование ключевого слова global вне функции не является ошибкой. Оно может быть использовано в файле, которые включается внутрь функции.

Использование статических (static) переменных

Другой важной особенностью области видимости переменной является статическая переменная. Статическая переменная существует только в локальной области видимости функции, но не теряет своего значения, когда выполнение программы выходит из этой области видимости. Рассмотрим следующий пример:

Пример #4 Демонстрация необходимости статических переменных

Эта функция довольно бесполезна, поскольку при каждом вызове она устанавливает $a в и выводит . Инкремент переменной $a ++ здесь не играет роли, так как при выходе из функции переменная $a исчезает. Чтобы написать полезную считающую функцию, которая не будет терять текущего значения счетчика, переменная $a объявляется как static:

Пример #5 Пример использования статических переменных

Теперь $a будет проинициализирована только при первом вызове функции, а каждый вызов функции test() будет выводить значение $a и инкрементировать его.

Статические переменные также дают возможность работать с рекурсивными функциями. Рекурсивной является функция, вызывающая саму себя. При написании рекурсивной функции нужно быть внимательным, поскольку есть вероятность сделать рекурсию бесконечной. Вы должны убедиться, что существует адекватный способ завершения рекурсии. Следующая простая функция рекурсивно считает до 10, используя для определения момента остановки статическую переменную $count :

Пример #6 Статические переменные и рекурсивные функции

function test ()
<
static $count = 0 ;

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

Пример #7 Объявление статических переменных

function foo () <
static $int = 0 ; // верно
static $int = 1 + 2 ; // неверно (поскольку это выражение)
static $int = sqrt ( 121 ); // неверно (поскольку это тоже выражение)

Статические объявления вычисляются во время компиляции скрипта.

Ссылки с глобальными (global) и статическими (static) переменными

Движок Zend Engine 1, лежащий в основе PHP 4, оперирует модификаторами переменных static и global как ссылками. Например, реальная глобальная переменная, внедренная в область видимости функции указанием ключевого слова global, в действительности создает ссылку на глобальную переменную. Это может привести к неожиданному поведению, как это показано в следующем примере:

function test_global_noref () <
global $obj ;
$obj = new stdclass ;
>

test_global_ref ();
var_dump ( $obj );
test_global_noref ();
var_dump ( $obj );
?>

Результат выполнения данного примера:

Аналогично ведет себя и выражение static. Ссылки не хранятся статично:

echo ‘Статический объект: ‘ ;
var_dump ( $obj );
if (!isset( $obj )) <
// Присвоить ссылку статической переменной
$obj = &new stdclass ;
>
$obj -> property ++;
return $obj ;
>

echo ‘Статический объект: ‘ ;
var_dump ( $obj );
if (!isset( $obj )) <
// Присвоить объект статической переменной
$obj = new stdclass ;
>
$obj -> property ++;
return $obj ;
>

$obj1 = get_instance_ref ();
$still_obj1 = get_instance_ref ();
echo «\n» ;
$obj2 = get_instance_noref ();
$still_obj2 = get_instance_noref ();
?>

Результат выполнения данного примера:

Статический объект: NULL
Статический объект: NULL

Статический объект: NULL
Статический объект: object(stdClass)(1) <
[«property»]=>
int(1)
>

Этот пример демонстрирует, что при присвоении ссылки статической переменной она не запоминается, когда вы вызываете функцию &get_instance_ref() во второй раз.

Урок №48. Локальные переменные, область видимости и продолжительность

Обновл. 15 Апр 2020 |

Этот материал является продолжением урока №15.

Область видимости и продолжительность

Прежде чем мы начнём, нам нужно сначала разобраться с двумя терминами: область видимости и продолжительность. Область видимости определяет, где можно использовать переменную. Продолжительность (или ещё «время жизни») определяет, где переменная создаётся и где уничтожается. Эти две концепции связаны между собой.

Переменные, определённые внутри блока, называются локальными переменными. Локальные переменные имеют автоматическую продолжительность: они создаются (и инициализируются, если необходимо) в точке определения и уничтожаются при выходе из блока. Локальные переменные имеют локальную область видимости (или ещё «блочную»), то есть они входят в область видимости с точки объявления и выходят в самом конце блока, в котором определены.

Область видимости в PHP

Допустим, мы написали множество функций, внутри которых используется множество переменных. А после этих функций мы напишем какой-нибудь код, скажем $a = 5 .

Откуда нам знать, что $a не была задана в какой-нибудь функции? Что если её изменение приведёт к некорректной работе скрипта?

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

Глобальная и локальная область видимости

Функции в PHP имеют локальную область видимости. Это означает, что внутри функции видны только те переменные, которые были объявлены внутри функции, либо переданы в неё:

Глобальные переменные в функциях

Глобальные переменные можно использовать внутри функций с помощью ключевого слова global :

Использование глобальных переменных является плохим тоном. Они делают код неочевидным и запутанным, функции начинают зависеть от внешних факторов.
Хороший программист никогда не использует глобальные переменные.

Константы в функциях

В отличие от переменных, константы видны в любой области видимости:

Если константы используются только для хранения каких-либо настроек сайта (пути к файлам, реквизиты доступа и т.п.), то использование констант не считается плохим тоном.

Илон Маск рекомендует:  Функции для работы с реестром
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL