Что представляет собой краткое введение в лексическое определение объема?
Что представляет собой краткое введение в лексическое определение объема?
Ответы:
Я понимаю их на примерах. :)
Во-первых, лексическая область (также называемая статической областью ) в C-подобном синтаксисе:
void fun()
{
int x = 5;
void fun2()
{
printf("%d", x);
}
}
Каждый внутренний уровень может получить доступ к своим внешним уровням.
Есть еще один способ, называемый динамической областью действия, используемый первой реализацией Lisp , опять же в C-подобном синтаксисе:
void fun()
{
printf("%d", x);
}
void dummy1()
{
int x = 5;
fun();
}
void dummy2()
{
int x = 10;
fun();
}
Здесь fun
можно либо доступ x
в dummy1
или dummy2
, или любой x
в любой функции, вызов fun
с x
объявленной в нем.
dummy1();
напечатает 5,
dummy2();
напечатает 10.
Первый называется статическим, потому что он может быть выведен во время компиляции, а второй называется динамическим, потому что внешняя область является динамической и зависит от цепного вызова функций.
Я считаю, что статический обзор легче для глаз. В конце концов, большинство языков пошли по этому пути, даже Lisp (можно сделать и то, и другое?) Динамическая область видимости похожа на передачу ссылок на все переменные в вызываемую функцию.
В качестве примера того, почему компилятор не может определить внешнюю динамическую область действия функции, рассмотрим наш последний пример. Если мы напишем что-то вроде этого:
if(/* some condition */)
dummy1();
else
dummy2();
Цепочка вызовов зависит от условий выполнения. Если это правда, то цепочка вызовов выглядит так:
dummy1 --> fun()
Если условие ложно:
dummy2 --> fun()
Внешняя область действия fun
в обоих случаях - это вызывающая сторона плюс вызывающая сторона вызывающей стороны и т. Д. .
Стоит только упомянуть, что язык Си не допускает вложенных функций и динамических областей видимости.
JavaScript
. Поэтому я думаю, что это не должно быть отмечено как принятый ответ. Лексическая сфера конкретно в JS отличается
for
цикла это типичная проблема. Лексическая область действия для JavaScript есть только на уровне функций, если не используется ES6 let
или const
.
Давайте попробуем самое короткое определение:
Лексическая область видимости определяет, как имена переменных разрешаются во вложенных функциях: внутренние функции содержат область действия родительских функций, даже если родительская функция вернулась .
Это все, что нужно сделать!
var scope = "I am global";
function whatismyscope(){
var scope = "I am just a local";
function func() {return scope;}
return func;
}
whatismyscope()()
Приведенный выше код будет возвращать «Я просто местный». Не вернется «Я глобальный». Потому что функция func () считает, где изначально было определено, что находится в области действия whatismyscope.
Он не будет беспокоиться о том, как он вызывается (глобальная область действия / даже из другой функции), поэтому значение глобальной области действия, которое я являюсь глобальным, не будет напечатано.
Это называется лексической областью видимости, где « функции выполняются с использованием цепочки областей действия, которая действовала, когда они были определены » - в соответствии с Руководством по определению JavaScript.
Лексическая сфера - очень очень мощная концепция.
Надеюсь это поможет..:)
Лексическая (AKA static) область действия относится к определению области действия переменной на основе исключительно ее положения в текстовом корпусе кода. Переменная всегда относится к ее среде верхнего уровня. Это хорошо понимать по отношению к динамическому объему.
Область действия определяет область, в которой доступны функции, переменные и тому подобное. Доступность переменной, например, определяется в ее контексте, скажем, в функции, файле или объекте, в котором они определены. Мы обычно называем эти локальные переменные.
Лексическая часть означает, что вы можете извлечь область из чтения исходного кода.
Лексическая область действия также известна как статическая область действия.
Динамическая область действия определяет глобальные переменные, которые можно вызывать или ссылаться из любого места после их определения. Иногда их называют глобальными переменными, хотя глобальные переменные в большинстве языков программирования имеют лексическую область. Это означает, что из чтения кода можно получить информацию о том, что переменная доступна в этом контексте. Может быть, нужно следовать условию использования или включения, чтобы найти указание или определение, но код / компилятор знает о переменной в этом месте.
Напротив, в динамической области поиска сначала выполняется поиск в локальной функции, затем поиск в функции, которая вызвала локальную функцию, затем поиск в функции, которая вызвала эту функцию, и т. Д. По стеку вызовов. «Динамический» относится к изменению в том смысле, что стек вызовов может отличаться каждый раз, когда вызывается данная функция, и поэтому функция может попадать в разные переменные в зависимости от того, откуда она вызывается. (см. здесь )
Чтобы увидеть интересный пример для динамического объема см. Здесь .
Для более подробной информации смотрите здесь и здесь .
Некоторые примеры в Delphi / Object Pascal
Delphi имеет лексическую область применения.
unit Main;
uses aUnit; // makes available all variables in interface section of aUnit
interface
var aGlobal: string; // global in the scope of all units that use Main;
type
TmyClass = class
strict private aPrivateVar: Integer; // only known by objects of this class type
// lexical: within class definition,
// reserved word private
public aPublicVar: double; // known to everyboday that has access to a
// object of this class type
end;
implementation
var aLocalGlobal: string; // known to all functions following
// the definition in this unit
end.
Ближайший Delphi к динамической области видимости - это пара функций RegisterClass () / GetClass (). Для его использования см. Здесь .
Скажем, время, когда RegisterClass ([TmyClass]) вызывается для регистрации определенного класса, нельзя предсказать, прочитав код (он вызывается в методе нажатия кнопки, вызываемом пользователем), код, вызывающий GetClass ('TmyClass'), получит результат или нет. Вызов RegisterClass () не обязательно должен быть в лексической области модуля, использующего GetClass ();
Другая возможность для динамической области видимости - это анонимные методы (замыкания) в Delphi 2009, поскольку они знают переменные своей вызывающей функции. Он не следует пути вызова оттуда рекурсивно и поэтому не является полностью динамическим.
Мне нравятся полнофункциональные, независимые от языка ответы от таких людей, как @Arak. Так как этот вопрос был помечен как JavaScript , я хотел бы добавить некоторые заметки, специфичные для этого языка.
В JavaScript наш выбор области видимости:
var _this = this; function callback(){ console.log(_this); }
callback.bind(this)
Стоит отметить, я думаю, что JavaScript не имеет динамической области видимости . .bind
корректирует this
ключевое слово, и это близко, но технически не то же самое.
Вот пример, демонстрирующий оба подхода. Вы делаете это каждый раз, когда принимаете решение о том, как настроить обратные вызовы, чтобы это относилось к обещаниям, обработчикам событий и многому другому.
Вот что вы можете Lexical Scoping
назвать обратными вызовами в JavaScript:
var downloadManager = {
initialize: function() {
var _this = this; // Set up `_this` for lexical access
$('.downloadLink').on('click', function () {
_this.startDownload();
});
},
startDownload: function(){
this.thinking = true;
// Request the file from the server and bind more callbacks for when it returns success or failure
}
//...
};
Еще один способ охвата заключается в использовании Function.prototype.bind
:
var downloadManager = {
initialize: function() {
$('.downloadLink').on('click', function () {
this.startDownload();
}.bind(this)); // Create a function object bound to `this`
}
//...
Эти методы, насколько мне известно, поведенчески эквивалентны.
bind
не влияет на объем.
IBM определяет это как:
Часть программы или сегмента, в которой применяется объявление. Идентификатор, объявленный в подпрограмме, известен в этой подпрограмме и во всех вложенных подпрограммах. Если вложенная подпрограмма объявляет элемент с тем же именем, внешний элемент недоступен во вложенной подпрограмме.
Пример 1:
function x() {
/*
Variable 'a' is only available to function 'x' and function 'y'.
In other words the area defined by 'x' is the lexical scope of
variable 'a'
*/
var a = "I am a";
function y() {
console.log( a )
}
y();
}
// outputs 'I am a'
x();
Пример 2:
function x() {
var a = "I am a";
function y() {
/*
If a nested routine declares an item with the same name,
the outer item is not available in the nested routine.
*/
var a = 'I am inner a';
console.log( a )
}
y();
}
// outputs 'I am inner a'
x();
Лексическая область означает, что во вложенной группе функций внутренние функции имеют доступ к переменным и другим ресурсам своей родительской области . Это означает, что дочерние функции лексически связаны с контекстом выполнения их родителей. Лексическую область видимости иногда называют статической областью действия .
function grandfather() {
var name = 'Hammad';
// 'likes' is not accessible here
function parent() {
// 'name' is accessible here
// 'likes' is not accessible here
function child() {
// Innermost level of the scope chain
// 'name' is also accessible here
var likes = 'Coding';
}
}
}
Что вы заметите в лексической области видимости, так это то, что она работает вперед, а это означает, что к имени могут обращаться контексты выполнения своих детей. Но это не работает назад к его родителям, означая, что переменная likes
не может быть доступна его родителям.
Это также говорит нам о том, что переменные с одинаковыми именами в разных контекстах выполнения имеют приоритет сверху вниз в стеке выполнения. Переменная с именем, аналогичным имени другой переменной, в самой внутренней функции (самый верхний контекст стека выполнения) будет иметь более высокий приоритет.
Обратите внимание, что это взято отсюда .
На простом языке лексическая область - это переменная, определенная вне вашей области, или верхняя область автоматически доступна внутри вашей области, что означает, что вам не нужно передавать ее туда.
Пример:
let str="JavaScript";
const myFun = () => {
console.log(str);
}
myFun();
// Вывод: JavaScript
bind
. С ними bind
больше не требуется. Для получения дополнительной информации об этом изменении проверьте stackoverflow.com/a/34361380/11127383
В нем отсутствует важная часть разговора о лексической и динамической области видимости : простое объяснение времени жизни переменной области действия - или когда к переменной можно получить доступ.
Динамическое определение объема лишь очень слабо соответствует «глобальному» определению в том смысле, в котором мы традиционно думаем об этом (причина, по которой я привожу сравнение между ними, заключается в том, что оно уже упоминалось - и мне не особенно нравится связанный объяснение статье ); вероятно, лучше всего не проводить сравнение между глобальным и динамическим - хотя, согласно предположительной статье, «... [это] полезно в качестве замены глобально изменяемых переменных».
Итак, на простом английском языке, в чем заключается важное различие между двумя механизмами определения объема?
Лексическая область видимости была очень хорошо определена в ответах выше: переменные с лексической областью доступны - или доступны - на локальном уровне функции, в которой они были определены.
Однако, поскольку это не является предметом OP, динамическое определение области не получило большого внимания, и внимание, которое он получил, означает, что, вероятно, нужно немного больше (это не критика других ответов, а скорее "о, этот ответ заставил нас пожелать, чтобы было немного больше)). Итак, вот еще немного:
Динамическое определение объема означает, что переменная доступна для более крупной программы в течение времени жизни вызова функции - или во время выполнения функции. На самом деле, Википедия действительно хорошо справляется с объяснением различий между ними. Чтобы не запутывать это, вот текст, который описывает динамическую область видимости:
... [I] n динамическая область видимости (или динамическая область действия), если область действия имени переменной является определенной функцией, то ее область действия - это период времени, в течение которого функция выполняется: во время выполнения функции имя переменной существует , и привязан к своей переменной, но после возврата из функции имя переменной не существует.
Лексическая область действия означает, что функция ищет переменные в контексте, в котором она была определена, а не в области непосредственно вокруг нее.
Посмотрите, как работает лексическая область видимости в Лиспе, если вы хотите больше подробностей. Выбранный ответ Кайл Кронин в динамических и лексических переменных в Common Lisp намного яснее, чем ответы здесь.
По совпадению я узнал об этом только в классе Lisp, и это применимо и в JavaScript.
Я запустил этот код в консоли Chrome.
// JavaScript Equivalent Lisp
var x = 5; //(setf x 5)
console.debug(x); //(print x)
function print_x(){ //(defun print-x ()
console.debug(x); // (print x)
} //)
(function(){ //(let
var x = 10; // ((x 10))
console.debug(x); // (print x)
print_x(); // (print-x)
})(); //)
Вывод:
5
10
5
Лексическая область видимости в JavaScript означает, что переменная, определенная вне функции, может быть доступна внутри другой функции, определенной после объявления переменной. Но обратное неверно; переменные, определенные внутри функции, не будут доступны вне этой функции.
Эта концепция широко используется в замыканиях в JavaScript.
Допустим, у нас есть код ниже.
var x = 2;
var add = function() {
var y = 1;
return x + y;
};
Теперь, когда вы вызываете add () -> это напечатает 3.
Итак, функция add () обращается к глобальной переменной, x
которая определена перед функцией метода add. Это вызвано лексическим ограничением в JavaScript.
add()
функция вызывается сразу после данного фрагмента кода, она также выдает 3. Лексическое определение не просто означает, что функция может обращаться к глобальным переменным вне локального контекста. Так что пример кода действительно не помогает показать, что означает лексическая область видимости. Чтобы показать лексическую область видимости в коде, нужен контрпример или хотя бы объяснение других возможных интерпретаций кода.
Лексическая область действия относится к лексикону идентификаторов (например, переменных, функций и т. Д.), Видимых из текущей позиции в стеке выполнения.
- global execution context
- foo
- bar
- function1 execution context
- foo2
- bar2
- function2 execution context
- foo3
- bar3
foo
а также bar
всегда находятся в пределах лексикона доступных идентификаторов, потому что они являются глобальными.
Когда function1
выполняется, он имеет доступ к лексике foo2
, bar2
, foo
, и bar
.
когда function2
выполняется, он имеет доступ к лексике foo3
, bar3
, foo2
, bar2
, foo
, иbar
.
Причина, по которой глобальные и / или внешние функции не имеют доступа к внутренним идентификаторам функций, заключается в том, что выполнение этой функции еще не произошло, и поэтому ни один из ее идентификаторов не был выделен для памяти. Более того, после выполнения этого внутреннего контекста он удаляется из стека выполнения, а это означает, что все его идентификаторы были собраны сборщиком мусора и более недоступны.
Наконец, именно поэтому вложенный контекст выполнения ВСЕГДА может получить доступ к контексту выполнения своих предков и, следовательно, почему он имеет доступ к большему лексикону идентификаторов.
Видеть:
Отдельное спасибо @ robr3rd за помощь в упрощении приведенного выше определения.
Вот другой угол в этом вопросе, который мы можем получить, сделав шаг назад и посмотрев на роль определения масштаба в более широком контексте интерпретации (запуска программы). Другими словами, представьте, что вы создавали интерпретатор (или компилятор) для языка и отвечали за вычисление вывода, учитывая программу и некоторый вклад в нее.
Интерпретация предполагает отслеживание трех вещей:
Состояние, а именно: переменные и ссылки на ячейки памяти в куче и стеке.
Операции в этом состоянии, а именно, каждая строка кода в вашей программе
Среда , в которой данная операция выполняется , а именно - проекция состояния на операцию.
Интерпретатор начинает с первой строки кода в программе, вычисляет ее среду, запускает строку в этой среде и фиксирует ее влияние на состояние программы. Затем он следует потоку управления программы для выполнения следующей строки кода и повторяет процесс до завершения программы.
Вы вычисляете среду для любой операции с помощью формального набора правил, определенных языком программирования. Термин «привязка» часто используется для описания отображения общего состояния программы на значение в среде. Обратите внимание, что под «общим состоянием» мы подразумеваем не глобальное состояние, а скорее общую сумму каждого достижимого определения в любой точке выполнения).
Это основа, в которой определяется область видимости. Теперь перейдем к следующей части наших возможностей.
Это суть динамической области видимости , при которой среда, в которой выполняется любой код, связана с состоянием программы, определяемым ее контекстом выполнения.
Другими словами, в лексической области видимость среды, которую видит любой код, связана с состоянием, связанным с областью, явно определенной в языке, такой как блок или функция.
Древний вопрос, но вот мой взгляд на него.
Лексическая (статическая) область действия относится к области действия переменной в исходном коде. .
В таком языке, как JavaScript, где функции можно передавать, присоединять и повторно присоединять к разным объектам, вы можете иметь, хотя эта область будет зависеть от того, кто вызывает функцию в данный момент, но это не так. Изменение области таким образом было бы динамической областью, и JavaScript не делает этого, за исключением, возможно, сthis
ссылки объект.
Чтобы проиллюстрировать это:
var a='apple';
function doit() {
var a='aardvark';
return function() {
alert(a);
}
}
var test=doit();
test();
В этом примере переменная a
определена глобально, но затенена в doit()
функции. Эта функция возвращает другую функцию, которая, как вы видите, опирается наa
переменную вне своей области видимости.
Если вы запустите это, вы обнаружите, что используемое значение есть aardvark
, а не apple
какое, хотя оно находится в области действияtest()
функции, не входит в лексическую область исходной функции. То есть используемая область - это область, как она появляется в исходном коде, а не область, где функция фактически используется.
Этот факт может иметь неприятные последствия. Например, вы можете решить, что проще организовать ваши функции по отдельности, а затем использовать их, когда придет время, например, в обработчике событий:
var a='apple',b='banana';
function init() {
var a='aardvark',b='bandicoot';
document.querySelector('button#a').onclick=function(event) {
alert(a);
}
document.querySelector('button#b').onclick=doB;
}
function doB(event) {
alert(b);
}
init();
<button id="a">A</button>
<button id="b">B</button>
Этот пример кода делает один из каждого. Вы можете видеть, что из-за лексической области видимости кнопка A
использует внутреннюю переменную, а кнопкаB
- нет. Вы можете закончить вложенными функциями больше, чем хотели бы.
Кстати, в обоих примерах вы также заметите, что внутренние лексически изменяемые переменные сохраняются, даже несмотря на то, что содержащая функция функция работает. Это называется закрытием и относится к доступу вложенной функции к внешним переменным, даже если внешняя функция завершена. JavaScript должен быть достаточно умным, чтобы определить, не нужны ли эти переменные, и если нет, может ли их собрать мусор.
Я обычно учусь на примере, и вот кое-что:
const lives = 0;
function catCircus () {
this.lives = 1;
const lives = 2;
const cat1 = {
lives: 5,
jumps: () => {
console.log(this.lives);
}
};
cat1.jumps(); // 1
console.log(cat1); // { lives: 5, jumps: [Function: jumps] }
const cat2 = {
lives: 5,
jumps: () => {
console.log(lives);
}
};
cat2.jumps(); // 2
console.log(cat2); // { lives: 5, jumps: [Function: jumps] }
const cat3 = {
lives: 5,
jumps: () => {
const lives = 3;
console.log(lives);
}
};
cat3.jumps(); // 3
console.log(cat3); // { lives: 5, jumps: [Function: jumps] }
const cat4 = {
lives: 5,
jumps: function () {
console.log(lives);
}
};
cat4.jumps(); // 2
console.log(cat4); // { lives: 5, jumps: [Function: jumps] }
const cat5 = {
lives: 5,
jumps: function () {
var lives = 4;
console.log(lives);
}
};
cat5.jumps(); // 4
console.log(cat5); // { lives: 5, jumps: [Function: jumps] }
const cat6 = {
lives: 5,
jumps: function () {
console.log(this.lives);
}
};
cat6.jumps(); // 5
console.log(cat6); // { lives: 5, jumps: [Function: jumps] }
const cat7 = {
lives: 5,
jumps: function thrownOutOfWindow () {
console.log(this.lives);
}
};
cat7.jumps(); // 5
console.log(cat7); // { lives: 5, jumps: [Function: thrownOutOfWindow] }
}
catCircus();
Эта тема тесно связана со встроенной bind
функцией и представлена в ECMAScript 6 Arrow Functions . Это действительно раздражало, потому что для каждого нового метода «класса» (на самом деле, функции), который мы хотели использовать, нам приходилось bind
это делать, чтобы иметь доступ к области действия.
JavaScript по умолчанию не устанавливает его объем this
на функции (она не устанавливает контекст на this
). По умолчанию вы должны явно указать, какой контекст вы хотите иметь.
Функции стрелки автоматически получают так называемую лексическую область видимости (имеют доступ к определению переменной в содержащем ее блоке). При использовании функций со стрелками он автоматически связывается this
с местом, где функция со стрелкой была определена в первую очередь, и контекст этой функции со стрелкой является содержащим его блоком.
Посмотрите, как это работает на практике, на простейших примерах ниже.
Перед стрелками (без лексической области по умолчанию):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const globalScope = programming.getLanguage;
console.log(globalScope()); // Output: undefined
const localScope = programming.getLanguage.bind(programming);
console.log(localScope()); // Output: "JavaScript"
С функциями стрелок (лексическая область по умолчанию):
const programming = {
language: "JavaScript",
getLanguage: function() {
return this.language;
}
}
const arrowFunction = () => {
console.log(programming.getLanguage());
}
arrowFunction(); // Output: "JavaScript"