Что такое скоуп в программировании
Перейти к содержимому

Что такое скоуп в программировании

  • автор:

Объяснение Scope (Chain) в JavaScript с помощью визуализации

Это перевод статьи Лидии Холли, где она разбирает такое понятие как Scope (Chain) с помощью визуализации. Давайте рассмотрим следующий код:

const name = "Lydia" const age = 21 const city = "San Francisco" function getPersonInfo() < const name = "Sarah" const age = 22 return `$is $ and lives in $` > console.log(getPersonInfo())

В коде мы вызываем функцию getPersonInfo , которая возвращает строку, содержащую значения переменных name , age и city :
Sarah is 22 and lives in San Francisco . Но функция getPersonInfo не содержит переменную city �� Как функция смогла получить значение переменной city ? Во-первых, пространство памяти настраивается для разных контекстов. По умолчанию global context( window в браузере, global в ноде) и local context для вызванной функции getPersonInfo . У каждого контекста также есть цепочка областей видимости (scope chain). Для функции getPersonInfo цепочка областей видимости (scope chain) выглядит так: scopeScope chain в основном представляет собой «цепочку ссылок» на объекты, которые содержат ссылки на значения и другие скоупы, на которые можно ссылаться в этом контексте выполнения. Scope chain создается при создании контекста выполнения, то есть создается в runtime. Однако давайте сосредоточимся на scope. В следующих примерах пары key/value в контекстах выполнения представляют ссылки, которые в scope как переменные. variableScope chain глобального контекста выполнения содержит ссылку на 3 переменные: name с значением Lydia , age с значением 21 , и city с значением San Francisco . В local context ссылки на 2 переменные: name с значением Sarah и age с значением 22 . Пытаясь получить доступ к переменным в функции getPersonInfo , движок сначала проверяет цепочку локального скоупа. В локальном scope chain содержиться ссылка на name и age. У переменной name значение Sarah и у переменной age значение 22 . Но что происходит когда мы пытаемся доступиться до city ? Для того чтобы найти значение переменной city , движок «спускается вниз по цепочке». В этом кейсе он ищет переменную города в внешнем скоупе, на который ссылается локальный скоуп. Мы можем спуститься по scope chain, но не можем подняться вверх по цепочке. (Хорошо, это может сбивать с толку, потому что некоторые люди говорят «вверх», а не «вниз», поэтому я просто перефразирую: вы можете перейти к внешним scope, но не к «более внутренним скоупам». Это можно визуализировать как водопад: waterfallИли даже глубже: scope globalВозьмем этот код в качестве примера. codeЭто почти то же самое, но есть одно большое отличие: теперь мы объявили city только в функции getPersonInfo , а не в global scope. Мы не вызывали функцию getPersonInfo , поэтому локальный контекст тоже не создается. Теперь давайте запросим доступ к значениям имени, возраста и города в глобальном контексте. Эта попытка выдаст ошибку ReferenceError! Не удалось найти ссылку на переменную с именем city в глобальной области видимости, и не было других скоупов для поиска, и она не может подняться по цепочке областей видимости. Таким образом, можно использовать область видимости как способ «защитить» свои переменные и повторно использовать имена переменных. Помимо глобальной и локальной областей, существует также block scope. Переменные, объявленные с помощью ключевого слова let или const, ограничены ближайшими фигурными скобками ( <> ).

const age = 21 function checkAge() < if (age < 21) < const message = "You cannot drink!" return message >else < const message = "You can drink!" return message >> 

block

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

Итог:

  1. Вы можете рассматривать «scope chain» как цепочку ссылок на значения, к которым мы можем получить доступ в текущем контексте.
  2. Scopes также позволяют повторно использовать имена переменных, которые определены ниже по scope chain, поскольку она может идти только вниз по цепочке областей видимости, но не вверх.

Комментарии:

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

Что такое скоуп в программировании

Область видимости или scope определяет контекст переменной, в рамках которого ее можно использовать. В Python есть два типа контекста: глобальный и локальный.

Глобальный контекст

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

name = "Tom" def say_hi(): print("Hello", name) def say_bye(): print("Good bye", name) say_hi() say_bye()

Здесь переменная name является глобальной и имеет глобальную область видимости. И обе определенные здесь функции могут свободно ее использовать.

Локальный контекст

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

def say_hi(): name = "Sam" surname = "Johnson" print("Hello", name, surname) def say_bye(): name = "Tom" print("Good bye", name) say_hi() say_bye()

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

Скрытие переменных

Есть еще один вариант определения переменной, когда локальная переменная скрывают глобальную с тем же именем:

name = "Tom" def say_hi(): name = "Bob" # скрываем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Tom

Здесь определена глобальная переменная name. Однако в функции say_hi определена локальная переменная с тем же именем name. И если функция say_bye использует глобальную переменную, то функция say_hi использует локальную переменную, которая скрывает глобальную.

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

name = "Tom" def say_hi(): global name name = "Bob" # изменяем значение глобальной переменной print("Hello", name) def say_bye(): print("Good bye", name) say_hi() # Hello Bob say_bye() # Good bye Bob

nonlocal

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

def outer(): # внешняя функция n = 5 def inner(): # вложенная функция print(n) inner() # 5 print(n) outer() # 5

Здесь вложенная локальная функция inner() выводит на консоль значение переменной n , которая определена во внешней функции outer(). Затем в функции outer() вызывается внутренняя функция inner().

При вызове функции outer() здесь мы ожидаемо увидим на консоли два раза число 5. Однако в данном случае вложенная функция inner() просто получает значение. Теперь возьмем другую ситуацию, когда вложенная функция присваивает значение переменной:

def outer(): # внешняя функция n = 5 def inner(): # вложенная функция n = 25 print(n) inner() # 25 print(n) outer() # 5 # 25 - inner # 5 - outer

При присвоении значения во вложенной функции: n = 25 будет создаваться новая переменная n, которая скроет переменную n из окружающей внешней функции outer. В итоге мы получим при выводе два разных числа. Чтобы во вложенной функции указать, что идентификатор во вложенной функции будет представлять переменную из окружающей функции, применяется выражение nonlocal :

def outer(): # внешняя функция n = 5 def inner(): # вложенная функция nonlocal n # указываем, что n - это переменная из окружающей функции n = 25 print(n) inner() # 25 print(n) outer() # 25

Какое правильное определение у SCOPE (Скоуп)

Часто сталкиваюсь с таким понятием как scope но не понимаю его. Скажите точное определение что такое scope и прошу приложить 2-3 примера по возможности на php .

Отслеживать
задан 17 мар 2016 в 15:25
user199588 user199588
409 2 2 золотых знака 6 6 серебряных знаков 14 14 бронзовых знаков

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

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

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

 test(); ?> 

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

Что такое скоуп в программировании

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

В Kotlin есть пять подобных функций: let , run , with , apply и also . Эти функции похожи по своему действию и различаются прежде всего по параметрам и возвращаемым результатам

let

Лямбда-выражение в функции let в качестве параметра it получает объект, для которого вызывается функция. Возвращаемый результат функции let представляет результат данного лямбда-выражения.

inline fun T.let(block: (T) -> R): R

Распространенным сценарием, где применяется данная функция, представляет проверка на null:

fun main() < val sam = Person("Sam", "sam@gmail.com") sam.email?.let< println("Email: $it") >// Email: sam@gmail.com // аналог без функции let //if(sam.email!=null) println("Email:$") val tom = Person("Tom", null) tom.email?.let < println("Email: $it") >// функция let не будет выполняться > data class Person(val name: String, val email: String?)

Допустим, мы хотим вывести значение свойства Email объекта Person. Но это свойство может хранить значение null (например, если электронный адрес у пользователя не установлен). С помощью выражения sam.email?. проверяем значение свойства email на null . Если email не равно null, то для строки в свойстве email вызывается функция let , которая создает временную область видимости и передает в нее значение свойства email через параметр it . Если же свойство email равно null, то функция let не вызывается.

Если лямбда-выражение вызывает лишь одну функцию, в которую передается параметр it, то можно сократить вызов — указать после оператора :: название вызываемой функции:

fun main() < val sam = Person("Sam", "sam@gmail.com") sam.email?.let(::println) // sam@gmail.com val tom = Person("Tom", "tom@gmail.com") tom.email?.let(::printEmail) // Email: tom@gmail.com >fun printEmail(email: String) < println("Email: $email") >data class Person(val name: String, var email: String?)

with

Лямбда-выражение в функции with в качестве параметра this получает объект, для которого вызывается функция. Возвращаемый результат функции with представляет результат данного лямбда-выражения.

inline fun with(receiver: T, block: T.() -> R): R

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

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

fun main() < val tom = Person("Tom", null) val emailOfTom = with(tom) < if(email==null) email = "$@gmail.com" email > println(emailOfTom) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае функция with получает объект tom, поверяет его свойство email — если оно равно null, то устанавливает его на основе его имени. В качестве результата функции возвращается значение свойства email.

run

Лямбда-выражение в функции run в качестве параметра this получает объект, для которого вызывается функция. Возвращаемый результат функции run представляет результат данного лямбда-выражения.

inline fun T.run(block: T.() -> R): R

Функция run похожа на функцию with за тем исключением, что run реализована как функция расширения. Функция run также принимает объект, для которого надо выполнить блок кода, в качестве параметра. Далее в самом блоке кода мы можем обращаться к общедоступным свойствам и методам объекта без его имени.

fun main() < val tom = Person("Tom", null) val emailOfTom = tom.run < if(email==null) email = "$@gmail.com" email > println(emailOfTom) // tom@gmail.com > data class Person(val name: String, var email: String?)

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

Реализация run как функции расширения упрощает проверку на null:

fun main() < val tom = Person("Tom", null) val validationResult = tom.email?.run ?: "undefined" println(validationResult) // undefined > data class Person(val name: String, var email: String?)

Также есть другая разновидность функции run() , которая просто позволяет выполнить некоторое лямбда-выражение:

fun main() < val randomText = run< "hello world">println(randomText) // hello world run < println("run function")>// run function >

apply

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

inline fun T.apply(block: T.() -> Unit): T
fun main() < val tom = Person("Tom", null) tom.apply < if(email==null) email = "$@gmail.com" > println(tom.email) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае, как и в примерах с функциями with и run , проверяем значение свойства email, и если оно равно null, устанавливаем его, используя свойство name.

Распространенным сценарием применения функции apply() является построение объекта в виде реализации вариации паттерна «Строитель»:

fun main() < val bob = Employee() bob.name("Bob") bob.age(26) bob.company("JetBrains") println("$($) - $") // Bob (26) - JetBrains > data class Employee(var name: String = "", var age: Int = 0, var company: String = "") < fun age(_age: Int): Employee = apply < age = _age >fun name(_name: String): Employee = apply < name = _name >fun company(_company: String): Employee = apply < company = _company >>

В данном случае класс Employee содержит три метода, которые устанавливают каждое из свойств класса. Причем каждый подобный метод вызывает функцию apply() , которое передает значение соответствующему свойству и возвращает текущий объект Employee.

also

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

inline fun T.also(block: (T) -> Unit): T

Эта функция аналогична функции apply за тем исключением, что внутри also объект, над которым выполняется блок кода, доступен через параметр it :

fun main() < val tom = Person("Tom", null) tom.also < if(it.email==null) it.email = "$@gmail.com" > println(tom.email) // tom@gmail.com > data class Person(val name: String, var email: String?)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *