Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   хитросплетения Global,контекст,this,Reference (https://javascript.ru/forum/misc/3158-khitrospleteniya-global-kontekst-reference.html)

Zeroglif 12.04.2009 19:35

Цитата:

Сообщение от kefi
контекстом функции

Контекст исполнения (execution context) - некая абстракция, изолирующая одну часть исполняемого кода от других частей в зависимости от того, что это за код. Исполняемый код лексически делится на три части - Global code, Function code, Eval code, отсюда и три вида контекста исполнения, по которым бродит js-интерпретатор. Контексты формируют стек, запустили сам скрипт - вошли в глобальный контекст, запустили функцию - вошли в контекст этой функции, запустили вложенную функцию - вошли в контекст вложенной функции, вернули что-то из вложенной - вышли из контекста вложенной и вернулись в контекст внешней, вернули что-то из внешней - вышли из контекста внешней и вернулись в глобальный контекст, отработал сам скрипт - вышли из глобального контекста... В любой точке сущестует один единственный активный/работающий контекст исполнения.

Цитата:

Сообщение от kefi
объектом переменных

Если говорить об именах, то js - это мир свойств. Переменные, параметры и имена объявленных функций должны стать свойствами некого объекта. Специально для них создаё(ю)тся объект(ы).

Цитата:

Сообщение от kefi
[[scope]] функции

http://javascript.ru/forum/offtopic/....html#post6885

kefi 13.04.2009 00:19

2 Zeroglif >
мда, что-то мутно там.

Мог бы кто понятно объяснить эти [[scope]], VO, контекст и иже с ними (какие-то, оказывается, еще спец объекты бывают ... ) , какие из этих объектов когда создаются, что они содержат и какие из них свойствами каких являются :
- при доRUN-time'оых FD определениях функций ,
- при FE определениях функций ,
- при вызове функций ?

Dmitry A. Soshnikov 13.04.2009 12:11

Вообще, там достаточно информации было, но попробую ещё проще; по частям, чтобы "уварилось". Начнём с VO.

Variable object (Объект переменных, сокращённо VO) - это объект, служащий хранилищем для:

1. переменных (var);
2. деклараций функций (function declaration, FD);
3. формальных параметров функции;

Главной особенностью здесь является то, что VO наполняется при входе в контекст, т.е. до выполнения кода контекста. Причём, в случае var'ов и недостающих формальных параметров под "наполнением" подразумевается резервирование слотов в VO со значением undefined.

Пример:

function test(a, b) {
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}

test(10); // вызов


Этапы вызова:

1. Вход в контекст:

VO = {  // упрощённо; в реале он "наследуется" от Объекта активации
  a: 10,
  b: undefined,
  c: undefined,
  d: <reference to functionBody>
  e: undefined
};


2. Исполнение контекста:

VO['c'] = 10;
VO['e'] = <reference to "_e" functionBody>;


Всё. Как видим, функция "x", являясь FE (function expression), а не FD, не попала в VO (поскольку, ничего не было сказано про воздействие FE на VO в пунктах выше). Если попробовать вызвать функцию "x" за пределами объявления - будет ReferenceError. С другой стороны, "_e" также является FE, но последующее присвоение её переменной "e" оставляет на неё ссылку через VO["e"].

Если здесь всё понятно, и есть желание дальше изучать, можно будет продолжить.

update:

Эм.. Оказывается, я уже в этой теме писал почти тоже самое.

kefi 13.04.2009 12:59

Цитата:

Сообщение от Dmitry A. Soshnikov
Если здесь всё понятно, и есть желание дальше изучать, можно будет продолжить.

Да, VO, вроде понятно, но возник еще объект активации AO...
а еще есть arguments, [[scope]] , etc ... Да, и еще бы понять, чьи они свойства - если функции , то в глобальном контексте если находимся - тогда чьи ?

Dmitry A. Soshnikov 13.04.2009 23:26

Цитата:

Сообщение от kefi
но возник еще объект активации AO...

Activation object (Объект активации, AO) касается только контекста исполнения типа "Функция".

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

AO создаётся при входе в контекст функции и инициализируется свойством arguments, значением которого является Arguments object (Объект аргументов, обозначим ArgO, чтобы не путать с AO):

AO = {
  arguments: <ArgO>
};


Далее, этот AO становится ничем иным как VO. Поэтому, говоря о VO в контексте типа "функция", можно использовать и терминологию AO, т.к. здесь VO === AO. Образно в предыдущем посте (чтобы не перегужать и не приплетать arguments) я назвал это "наследуется", физически же, это тот же самый объект.

Говоря о VO в глобальном контексте, - сам глобальный объект является VO (в браузере - window).

Цитата:

Сообщение от kefi
а еще есть arguments,

Arguments object (ссылка выше) - объект, находящийся в VO/AO функции; содержит свойства:

- callee - ссылка на выполняемую функцию;
- length - количество реально переданных параметров;
- свойства-индексы (числовые, приведённые к строке), значения которых есть формальные параметры функции (слева направо в списке параметров). Количество этих свойств-индексов == arguments.length. Значения свойств-индексов объекта arguments и присутствующие формальные параметры - взаимозаменяемы:

function a(x, y, z) {
  alert(arguments.length); // 2
  alert(arguments.callee === a); // true
  alert(x === arguments[0]); // true
  alert(x); // 10
  arguments[0] = 20;
  alert(x); // 20
  x = 30;
  alert(arguments[0]); // 30
}

a(10, 20);


Цитата:

Сообщение от kefi
Да, и еще бы понять, чьи они свойства - если функции , то в глобальном контексте если находимся - тогда чьи ?

Если код входит в контекст функции, то это свойства функции. Если мы находимся в глобальном коде, то сам глобальный объект - есть VO.

Если всё понятно, можно будет продолжить про [[Scope]].

kefi 14.04.2009 23:46

Я понял так , еще немного допридумал, чтобы было логичнее, проверьте что не так :

Variable object (Объект переменных, сокращённо VO) - это объект, служащий хранилищем для:

1. переменных (var);
2. деклараций функций (function declaration, FD);
3. формальных параметров функции;

Главной особенностью здесь является то, что VO наполняется при входе в контекст, т.е. до выполнения кода контекста. Причём, в случае var'ов и недостающих формальных параметров под "наполнением" подразумевается резервирование слотов в VO со значением undefined.

Для глобального контекста :
VO===Global (где Global - встроенный глобальный объект) нашпиговывается ДО run-time слотами, которые по мере работы программы могут менять свои значения, но не свое количество или имена !

Для функции :
Если не было входа управления в контекст функции, то не существует для нее никакого ни VO, ни AO.
Если управление вошло во внутренний контекст функции F , то ДО ее выполнения для этой функции создается свой VO', при этом выполняются след шаги :
arguments = [<0аргумент>,<1аргумент,...];
 arguments.callee=F;
 arguments.length=<КолвоАргументовФункции_F>;
  AO = {  arguments: arguments };
  F.VO=AO;

При выходе из контекста функции выполняется :
delete F.VO
Цитата:

Если всё понятно, можно будет продолжить про [[Scope]].
"Огласите весь список, пожалуйста."

Dmitry A. Soshnikov 15.04.2009 00:31

Цитата:

Сообщение от kefi
Для глобального контекста :
VO===Global (где Global - встроенный глобальный объект) нашпиговывается ДО run-time слотами, которые по мере работы программы могут менять свои значения, но не свое количество или имена !

До исполнения, если встречаются: var и FD. Всё остальное (например, a = 10 (без var), либо явно - window.a = 10) - создаётся в runtime'e (и слот и его значение). Поэтому, Global свободно может менять свои слоты и значения этих слотов в runtime'e.

Цитата:

Сообщение от kefi
Если не было входа управления в контекст функции, то не существует для нее никакого ни VO, ни AO.

Да, так. Однако, [[Scope]] в функции зашит изначально и навсегда, не важно, будет ли запущена функция или нет.

Цитата:

Сообщение от kefi
Если управление вошло во внутренний контекст функции F , то ДО ее выполнения для этой функции создается свой VO', при этом выполняются след шаги :

Да.

Цитата:

Сообщение от kefi
F.VO=AO;

Можно и так, для удобства (абстрактно) обозначить.

Цитата:

Сообщение от kefi
delete F.VO

Да, только, если не было замыкания, тогда F.VO сохранится в [[Scope]] внутренней функции (т.е. не будет удалён, пока на него есть ссылка).

Цитата:

Сообщение от kefi
"Огласите весь список, пожалуйста."

[[Scope]] - это иерархическая цепь Объектов переменных (VO), стоящих выше контекста функции; цепь записывается свойством в функцию при её создании.

Scope (Scope chain, Иерархия областей видимости (в данном переводе)) - это цепь Объектов переменных, в которой происходит поиск значений при разрешении имен идентификаторов. Данная цепь создаётся при выполнении функии, и состоит из VO(текущей функции) и [[Scope]] функции:

Scope = VO + [[Scope]].

Можно ещё раз здесь почитать.

Пример:

var a = 10;
function b() {
  var c = 20;
  function d() {
    var e = 30;
    alert(a + c + e);
  }
  d();
  return d;
}
b();


Имеем:

VO(Global):

VO(Global) === Global = {
  a: 10
  b: <reference to functionBody>
};


При создании "b", [[Scope]] "b":

[[Scope]](b) = [
  VO(Global)
];


VO(b):

VO(b) = {
  c: 20,
  d: <reference to functionBody>
};


При запуске "b", Scope(b) = VO(b) + [[Scope]](b):

Scope(b) = [
  VO(b),
  VO(Global)
];


При создании внутрененней функции "d", [[Scope]] "d":

[[Scope]](d) = [
  VO(b),
  VO(Global)
];


VO(d):

VO(d) = {
  e: 30
};


При запуске "d", Scope(d) = VO(d) + [[Scope]](d):

Scope(b) = [
  VO(d),
  VO(b),
  VO(Global)
];


Разрешение имён идентификаторов "a", "c" и "e" при alert'e:

- "a"
-- VO(d) - не найдено;
-- VO(b) - не найдено;
-- VO(Global) - найдено - 10.


- "с"
-- VO(d) - не найдено;
-- VO(b) - найдено - 20.


- "е"
-- VO(d) - найдено - 30;


Т.е. Scope опрашивается по цепи вверх, если идентификатор не найден в родном VO.

При return d; остаётся ссылка на [[Scope]](d), поэтому все VO в цепи, стоящие иерархически выше - остаются в памяти.

kefi 15.04.2009 01:33

2 Dmitry A. Soshnikov >

Все яснее и яснее, Я сам-то мучился с этими терминами, понимал для себя только , что есть внешний контекст для каждой функции и внутренний. И как это все Вы поняли из ECMA спецификации , ума не приложу ...

Кстати контекст, как я сейчас понимаю, не является термином обозначающим некий объект, это чисто лексический контекст для текста функции или какого-ни слова из кода программы ?

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

Цитата:

При return d; остаётся ссылка на [[Scope]](d), поэтому все VO в цепи, стоящие иерархически выше - остаются в памяти
Хотел уточнить :
- именно на [[Scope]] , т.е. БЕЗ VO функции d ?
- и сохраняются не просто , если возвращается return, как в Вашем примере b() , а если возвращаемое где-то сохранено var q=b() ?

Dmitry A. Soshnikov 15.04.2009 21:43

Цитата:

Сообщение от kefi
И как это все Вы поняли из ECMA спецификации , ума не приложу ...

Несомненно, стоит отметить этого человека - изначально, он помог мне поднять некоторые глубокие аспекты ("Этот человек", только ни в коем случае не считай это за лесть =) я просто благодарен и отдаю должное ;)). Углубляясь, изучая, дальше сам уже стандарт проштудировал (что и всем советую для профессионального понимания JavaScript).

Цитата:

Сообщение от kefi
Кстати контекст, как я сейчас понимаю, не является термином обозначающим некий объект, это чисто лексический контекст для текста функции или какого-ни слова из кода программы ?

Да, это абстракное определение, зависящее от типа кода: Global code, Eval code, Function code.

Цитата:

Сообщение от kefi
Вот правда, еще остается непонятной роль AO - вроде как и нни к чему он

AO - это и есть VO. А VO, в свою очередь, можно тоже считать абстракцией. Физически же, эта абстракция представлена AO (для функции и eval'a) и Global - для Global'a. У Global'a нет arguments, поэтому для функции это назвали AO. Если говорить отстраннённо от JS, можно представить VO - как интерфейс, а AO и Global - как конкретные классы, реализующие этот интерфейс.

Цитата:

Сообщение от kefi
да еще какой-то непонятныйц остается спец объект

Спец.объект - "анонимный объект", который содержит одно единственное свойство - опциональное имя FE, чтобы FE могла обратиться к себе рекрсивно по имени (помимо arguments.callee):

(function fn() {alert(fn);})();


Откуда alert(fn) выдаёт правильное значение, если FE не воздействуют на VO? Идентификатор fn не должен был попасть в VO(Global). Где же FE его находит? Специально для этого перед созданием FE создаётся этот спец. объект и кладётся в scope chain (в данном случае Global):

Scope(Global).{}.fn = <reference to FE "fn">


Дальше, когда создалась FE "fn", этот спец. объект удаляется из Scope(Global) - теперь он есть в [[Scope]](fn) и, соотвественно, может быть найден.

Здесь ещё можно почитать.

Цитата:

Сообщение от kefi
- именно на [[Scope]] , т.е. БЕЗ VO функции d ?

update: VO и Scope создаются каждый раз при вызове функции; запоминается только [[Scope]](d).

Цитата:

Сообщение от kefi
- и сохраняются не просто , если возвращается return, как в Вашем примере b() , а если возвращаемое где-то сохранено var q=b()

Да, конечно, это тоже подразумевалось.

kefi 15.04.2009 23:52

Цитата:

Конечно же с VO.
Чьим VO? я-то имел ввиду, что VO(d) не сохраняется поскольку НЕ НУЖЕН , т.к. при очередном вызове через замыкание он создается заново .

Dmitry A. Soshnikov 15.04.2009 23:59

Цитата:

Сообщение от kefi
я-то имел ввиду, что VO(d) не сохраняется поскольку НЕ НУЖЕН , т.к. при очередном вызове через замыкание он создается заново .

Да, верно. Я сбился (белиберду написал, устал, наверное; сам же объяснял, что VO создаётся каждый раз, и тут же "опечатался"; "зачеркнул"). Естественно, VO и Scope создаются каждый раз при вызове.

P.S.: надо бы сделать тег зачёркивания.

kefi 16.04.2009 11:56

Тут еще интересно c чисто хозяйственной точки зрения :
а сохраняется ли [[Scope]] (d), если функция d не содержит переменных из своего [[Scope]], в этом случае вроде как бы оно и не нужно тратить память ?
Правда практически на тестах это не проверить никак .

var a = 10;
function b() {
  var c = 20;
  function d() {
    var e = 30;   alert( e);
  }
  d();
  return d;
}
var sav=b();

Dmitry A. Soshnikov 16.04.2009 11:57

Цитата:

Сообщение от Dmitry A. Soshnikov
Физически же, эта абстракция представлена AO (для функции и eval'a) и Global - для Global'a.

Ещё поправка - eval берёт VO того контекста, в котором он запущен: если из функции - AO, если из Global - Global.

Цитата:

Сообщение от kefi
а сохраняется ли [[Scope]] (d), если функция d не содержит переменных из своего [[Scope]],

Да, сохраняется.

function a() {
  var b = 10;
  function c() {
    function d() {
      alert(b);
    }
  }
}


Функция "с" не использует переменные из внешнего VO (ещё говорят: не имеет свободных переменных). Однако, функция "d" - использует. И черпает из Scope, частью которого является [[Scope]] (т.е. Scope вышестоящего контекста). Для "d", [[Scope]] - это Scope(c), и если в "c" не будет ссылки на вышестоящий контекст (где находится "b"), "d" не увидит "b".

kefi 16.04.2009 12:25

Цитата:

Сообщение от Dmitry A. Soshnikov
Да, сохраняется

Уточню - а сохраняется ли [[Scope]] (d), если функция d и все вложенные в нее функции не содержит переменных из [[Scope]](d) и не содержат операции eval , в этом случае вроде как бы оно и не нужно тратить память ?

И еще аналогичный вопрос :
А сохраняется ли Scope Chain контекста в котором мы сделали замыкание? Тоже, вроде как, ни к чему .
Т.е.
var a = 10;
function b() {
  var c = 20;
  function d() {
    var e = 30;   alert( e);
  }
  d();
  var savB=d; // Scope Chain (b) === [[Scope]](d)    Здесь сохранится ?
  return d;
}
var sav=b();

Dmitry A. Soshnikov 16.04.2009 15:19

Да, сохраняется. В спецификации ничего по этому поводу не сказано, сказано лишь, что [[Scope]] однозначно записывается в функцию при её создании. Однако, может, какие-то реализации могут делать оптимизацию (только в однозначно определённых случаях, когда точно известно, что функция не использует ничего извне), но это лишь предположение. С другой стороны, в функции может стоять if, внутри которого будет eval(...), использующий свойства из [[Scope]]. Или же функция может вызывать себя рекурсивно по имени (имя также находится в [[Scope]]).

kefi 16.04.2009 16:47

2 Dmitry A. Soshnikov> Это ответ на первый или второй вопрос, во втором случае вроде как незачем сохранять Scope Chain ?
var a = 10;
( function b() {
  var c = 20;
  function d() {
    var e = 30;   alert( e+c+a);
  }
  d();
  var savB=d; // Scope Chain (b) === [[Scope]](d)    Зачем Здесь хранить Scope Chain (b)  ?
  return d;
} ) ()

Dmitry A. Soshnikov 16.04.2009 17:02

Цитата:

Сообщение от kefi
Это ответ на первый или второй вопрос

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

Возможно, какая-нибудь из реализаций не сохраняет [[Scope]] для оптимизации в функции вида function a() {}. Но, ещё раз, в спецификации ничего не сказано об этом.

kefi 16.04.2009 17:33

Цитата:

Сообщение от Dmitry A. Soshnikov
в спецификации сказано, что [[Scope]] записывается в функцию однозначно при её создании

Спецификация она, того..., не очень однозначно толкуется :
Может он-то и записывается , но записывается ссылка на него, но его содержимое ... , короче, вот :
var a = 10;
function b() {
  var c = 20;
  function d() {
    var e = 30;   al( e+c+a);
  };
  // Замыкаем на savB
  var savB=d; // Scope Chain (b) === [[Scope]](d)    Зачем Здесь хранить Scope Chain (b)  ?
  savB(); // 60
  c=0;    // Если бы VO(b) законсервировался при замыкании, то это изменение 
          // не оказало бы воздействия на последующий вызов через Замыкание: 
  savB(); // 40 <- Scope Chain (b), т.е. VO(b) - не законсервировался
  return savB;
};
var s=b(); 
s();      //40 <- Scope Chain (b), т.е. VO(b) - Законсервировался, но Global - нет.

Zeroglif 16.04.2009 19:17

Цитата:

Сообщение от Dmitry A. Soshnikov
Возможно, какая-нибудь из реализаций не сохраняет

Запросто. Оптимизация может быть какой угодно, лишь бы внешне всё было в рамках стандарта, функцию можно предварительно проанализировать и не создавать то, что в ней не используется, например, 'arguments'... etc.

Dmitry A. Soshnikov 16.04.2009 21:53

Цитата:

Сообщение от kefi
Может он-то и записывается , но записывается ссылка на него

Да, это динамическая цепь (есть ещё и статическая; подробней, можно в вики почитать). Найденное в Scope chain значение, всегда будет последним присвоенным (связанным) значением этой переменной. Чтобы это предотвратить (а такие случаи бывают нужны - например, при замыкании счетчика цикла) - создают новую функцию с новым VO - т.е. передают нужное значение (счетчик цикла) параметром в создаваемую функцию.

Цитата:

Сообщение от kefi
// Замыкаем на savB var savB=d;

Просто ссылка. d - уже замыкание.

Цитата:

Сообщение от kefi
Зачем Здесь хранить Scope Chain (b)

Ну а как? - в "d" есть свободные переменные.

Цитата:

Сообщение от kefi
Если бы VO(b) законсервировался при замыкании,

Здесь динамический scope chain, представляющий из себя стек (т.е. найдётся последнее присвоенное значение первого совпавшего имени переменной, начиная с вершины этого стека, т.е. с VO). Поэтому, ничего здесь не "консервируется", как в статическом лексическом контексте.

Цитата:

Сообщение от kefi
//40 <- Scope Chain (b), т.е. VO(b) - Законсервировался, но Global - нет.

Ничего не "законсервировалось" (снова - динамический Scope): "c" имеет правильное, последнее присвоенное, значение - 0, "а" - правильное, последнее, единожды присвоенное, значение - 10.

Цитата:

Сообщение от Zeroglif
Запросто.

Да, теоретически, и по логике вещей, конечно должны. Но, спецификация этого не отмечает. А точно узнать - можно лишь, проанализировав исходники реализаций.

kefi 17.04.2009 00:25

Цитата:

Сообщение от Dmitry A. Soshnikov
это динамическая цепь (есть ещё и статическая

Чем дальше в сказку, тем страшнее ;) ...

Цитата:

Найденное в Scope chain значение, всегда будет последним присвоенным (связанным) значением этой переменной. Чтобы это предотвратить (а такие случаи бывают нужны - например, при замыкании счетчика цикла) - создают новую функцию с новым VO - т.е. передают нужное значение (счетчик цикла) параметром в создаваемую функцию.
Не понял, к чему это здесь ?..

Цитата:

Зачем Здесь хранить Scope Chain (b)
Ну а как? - в "d" есть свободные переменные.
Ну и пусть есть , ведь далее при повторном вызове savB(); // 40
из того же контекста где эти переменные известны мы же однозначно их определяем ?..

Впрочем, вот сам опровергну свои слова, хотя не понимаю, зачем так сделано :
var a = 10;
function b(x) {
  var c = 20; 
  // Здесь пытаемся вызвать из контекста b, в котором однозначно какзалось бы определяются все значения VO(b)
  if (x) { a=5; c=7; x(); return  } ; // 30,0,5 <- НИКАКОГО влияния нового c==7 ! осталось старое законсервированное c==0
  function d() {
    var e = 30;   al( [e ,c , a ]);
  };
  // Замыкаем на savB
  var savB=d; // Scope Chain (b) === [[Scope]](d)    Зачем Здесь хранить Scope Chain (b)  ?
  savB(); // 30,20,10
  c=0;    // Если бы VO(b) законсервировался при замыкании, то это изменение 
          // НЕ оказало бы воздействия на последующий вызов через Замыкание: 
  savB(); // 30,0,10  <- Scope Chain (b), т.е. VO(b) - не законсервировался, т.к.
          // здесь вызов почувствовал (?) изменение с с=20 на --> c=0 
          // НО ПОЧЕМУ ????????????????? ЗДЕСЬ [[Scope]](d) НЕ СОХРАНИЛСЯ 
  b(savB); // 30,0,5 <- А вот так НЕ почувствовал с=7 , хотя контекст b, казалось бы мог сам определить свои значения, Караул !!!
  return savB;
};
var s=b(); 
//s();      // 30,0,10 <- Scope Chain (b), т.е. VO(b) - Законсервировался, но Global - нет.
// b(s);     // 30,0,5
s();      // 30,0,5


Но уж Global'то VO - никогда не консервируется при замыканиях ?!

PS. Можно еще пояснить вопрос НО ПОЧЕМУ?????????? в примере ?
Просто я считал, что
Замыкание необходимо для разрешения неоднозначности значений локальных переменных из [[Scope]] замыкаемой функции, которая возникает , если ссылка на функцию сохраняется вне Контекста охватывающей функции и мы захотим вызвать функцию Извне ее Охватывающего Контекста по этой ссылке.

Zeroglif 17.04.2009 08:16

Цитата:

Сообщение от kefi
НО ПОЧЕМУ ?????????????????

Funarg, етить его... Функция "запоминает" [[Scope]] (все вышестоящие VO) в момент создания, один раз и навсегда. Дальше уже не имеет значения, куда эту функцию засовывают. Отсюда, если при создании функции 'd', её статичный [[Scope]] - это VO(b)->VO(global), то это уже не изменится, единственное, что динамично - это создание собственного VO(d), что происходит в любой функции с каждым вызовом.

Когда вы отправляете функцию 'd' в качестве аргумента, то у функции 'b' создаётся собственный новый VO(b), но 'd' это не волнует, т.к. неё другой VO(b), созданный ранее при первом вызове. Соответственно, свойство 'c' одного объекта не имеет никакого отношения к свойству 'c' другого объекта...

Dmitry A. Soshnikov 17.04.2009 11:41

Цитата:

Сообщение от kefi
Не понял, к чему это здесь ?..

К тому, что на момент, пока существует воздействие на живой вышестоящий VO, найденное значение свойства в этом VO будет последним присвоенным значением. А вот, когда уже функция передаётся параметром, у "вышестоящей" функции уже другой VO (вот в этом моменте можно говорить о "консервировании" [[Scope]] в функции, но только при условии, что вышестоящий VO уже новый/не существует старый).

kefi 17.04.2009 20:03

Все правильно сказано, но вот что-то не достает для практического руководства, т.е. нужны какие-то
краткие , чтобы легко запомнить и одновременно предельно ясные, чтобы не ошибиться слова ,
поясняющие явление Замыкания.
У меня вот тоже не очень получается сложить такие слова.
попытался вот так, исправьте,плз, что не так :

/* Когда Программа может менять слоты [[Scope]]'a' Замыкания :

[[Scope]] Замыкания - [[Scope]] замыкаемой функции. Он Живой пока есть ссылка на Замыкаемую функцию (т.е. Замыкание).
Контекст Замыкания - Весь Внешний контекст Замыкаемой функции, т.е. Весь Контекст в который она вложена.
Все вызовы через Замыкание происходят с использованием [[Scope]]-а Замыкания для определения в вызываемой функции
значений свободных переменных, точнее слотов [[Scope]]'а Замыкания .

В случае выхода управления на Верхний уровень Программе оказываются доступными только слоты
[[Scope]]'а' Замыкания для [[Scope Chain]]'а' этого уровня, т.е. только [[VO]]Global.

Если управление выходит из Контекста Замыкания не полностью, а на какой-то его Уровень, то Программе на этом Уровне
становятся доступными ТОЛЬКО слоты [[Scope]]'а' Замыкания для [[Scope Chain]]'а' этого уровня ,
При дальнейшем входе с этого Уровня на Вложенные уровни, вновь создаваемые [[VO]] вложенных уровней
ЗАКРЫВАЮТ(консервирует) сответствующие [[VO]] для этих уровней из [[Scope]]'а' Замыкания.

Программма всегда может получить доступ ко всему [[Scope]]'у' Замыкания передав управление в Замкнутую функцию ЧЕРЕЗ Замыкание.
*/
function al(o){document.write(o,'<br>')} ;
al('g,a,b,c ---------------');
var myScope , g = 0 ;
A();
function A(x) { // Вход сюда создает новый [[VO]]A ,но не затрагивает [[VO]]Global === ( [[Scope]]A == [ myScope, g==0 ] )
    var a = 1; 
    if (x==1) { a=2; b=2; c=2; al( [g, a, b, c, '<---A( )'] );  myScope(2) ; return  } ; 
    if (x==2) { al( [g, a, b, c, '-- имеем в A( )']); return  } ; 
    B();
    function B(y) { // Вход сюда создает новый [[VO]]B ,но не затрагивает [[VO]]А,[[VO]]Global 
      var b = 1;
        if (y==1) { a=3; b=3; c=3; al( [g, a, b, c, '<---B( )']); myScope(2); return  } ; 
        if (y==2) { al( [g, a, b, c,  '-- имеем в B( )']); return  } ; 
        C();
        function C(z) { // Вход сюда создает новый [[VO]]С ,но не затрагивает [[VO]]B, [[VO]]А ,[[VO]]Global 
            var c=1;
            if (z==1) { a=4; b=4; c=4; al( [g, a, b, c, '<---C( )']); myScope(2); return  } ; 
            if (z==2) { al( [g, a, b, c, '-- имеем в C( )']); return  } ; 
            
            // myScope = B ; // [[Scope]]B == [ myScope, g==0,a==1,x, B ]
            // Замкнуть, Контекстом Замыкания будет весь Внешний Контекст функции C :
            myScope = C ; 
            // [[Scope]] Замыкания  [[Scope]]C == [ myScope, g==0,a==1,b==1, x, y, B, C ]
            // g,a,b,c ---------------
            // 0,1,1,1,-- имеем в C( ) // <-исходное состояние [[Scope]]'а' Замыкания
            myScope(2);
            
            // Выход на уровень Контекста Global - Полный выход из Контекста Замыкания, В вызываемой функции A
            // никакие слоты [[Scope]]'a' Замыкания не доступны, кроме [[Scope]]A === [[VO]]Global слотов  (ни а ни b не изменить)
            // 0,2,2,2,<---A( )
            // 0,1,1,1,-- имеем в C( )
            A(1); 
            
            // Выход на Уровень Контекста А - Частичный выход из Контекста Замыкания,
            // в вызываемой B() будут доступны только слоты [[Scope]]'a' Замыкания для [[Scope]]B ( a можем изменить, b -нет )
            // 0,3,3,3,<---B( )
            // 0,3,1,1,-- имеем в C( )  // 0,3,3,1,-- имеем в C( ) для вызова C(2) вместо myScope(2),т.е. b меняем !
            B(1);   
            
            // Выход на уровень Контекста B, Нет выхода из Конекста Замыкания, 
            // в вызываемой  С() будут доступны все слоты [[Scope]]'a' Замыкания для [[Scope]]C (a и b можно изменить)
            C(1);   
            return; // Выход на уровень Контекста B
            // 0,4,4,4,<---C( )
            // 0,4,4,1,-- имеем в C( )
            
        };
        return ; // Выход на уровень Контекста A
     };
    return ; // Выход на уровень Контекста Global - Полный выход из Контекста Замыкания  
};

Zeroglif 17.04.2009 20:45

Цитата:

Сообщение от kefi
исправьте,плз, что не так

Всё не так. Аццкий текст. ;)

kefi 17.04.2009 21:32

Цитата:

Сообщение от Zeroglif
Всё не так.

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

Dmitry A. Soshnikov 17.04.2009 21:59

Цитата:

Сообщение от kefi
поясняющие явление Замыкания.

http://javascript.ru/forum/misc/2975...html#post13713

Zeroglif 17.04.2009 23:04

Цитата:

Сообщение от kefi
Объясните то, что иллюстрировано примером

Давайте вы спросите, что именно не понятно, а я попробую объяснить. Или не я. Чтобы не писать одно и то же и не расшифровывать примеры.

kefi 17.04.2009 23:29

Цитата:

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

Цитата:

Давайте вы спросите, что именно не понятно, а я попробую объяснить Или не я. Чтобы не писать одно и то же и не расшифровывать примеры.
Так вроде я сам предложил объяснение.
Мне кажется Дело бы при Замыканиях было бы довольно просто , если бы [[Scope]]Замыкания не менялся. Тогда бы - как только замкнули (myScope = C), - проанализировали кусок кода, где замкнули, поняли для себя , какие там должны быть значения слотов [[Scope]]Замыкания и вуа-ля - спокойно используем, надежно зная и понимая , какие значения будут у переменных [[Scope]]Замыкания при вызове замкнутой функции через Замыкание ( myScope(2) ) - НЕТ никаких проблем !

НО ! Вот после того, как замкнули, этот пресловутый [[Scope]] Замыкания может еще меняться программой ДО того, как стали вызывать Замкнутую функцию через Замыкание и проанализировать это довольно не просто. И поэтому я и привел пример (причем ,довольно интересный, с частичным выходом из Контекста Замыкания), иллюстрирующий наиболее ясно эту проблему и попытался максималоьно кратко дать объяснение - Когда Программа может менять слоты [[Scope]]'a' Замыкания .
Поэтому непонятно ТОЛЬКО одно (нет , ну, конечно, непонятным при обнаружении новых фактов может стать все , что угодно ), но пока это :
- КАК максимально кратко и ясно описать основную проблему при работе с Замыканиями - Когда Программа может менять слоты [[Scope]]'a' Замыкания ? Я попытался так , как попытался, Вы считаете, что плохо, или не хотите "расшифровывать примеры", не знаю ...

Zeroglif 17.04.2009 23:46

Цитата:

Сообщение от kefi
Вы считаете, что плохо, или не хотите "расшифровывать примеры", не знаю ...

Я не считаю, что плохо, только примеры повторяются, сначала было про 'c', теперь 'a,b,c', идея та же, какие там проблемы-то? Всё работает, как должно работать, функция "помнит" объекты в scope chain, а не значения (их вообще в момент создания может не быть), если значения позднее меняются, то они меняются...

function A() {
    // в момент создания [[Scope]] функции 'В'
    // переменной 'x' вообще не существует
    function B() {
        alert(typeof x);
    }
    // в момент вызова тоже
    B();
    eval('var x = 1');
    // теперь есть
    B();
}

A();

Zeroglif 18.04.2009 00:12

Цитата:

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

Так и есть, вы в своём примере даже не выходите из контекстов (рекурсия не в счёт), а раз вы там внутри находитесь, то имеете полное право менять значения 'a' и 'b', и тем более 'g' из любого места, откуда доступны эти свойства. Вот если вы из функции 'A' выйдете и начнёте работать с замыканием снаружи, то кроме него (замыкания) никто ничего не поменяет...

kefi 18.04.2009 01:16

Цитата:

Так и есть, вы в своём примере даже не выходите из контекстов (рекурсия не в счёт), а раз вы там внутри находитесь, то имеете полное право менять значения 'a' и 'b', и тем более 'g' из любого места, откуда доступны эти свойства. Вот если вы из функции 'A' выйдете и начнёте работать с замыканием снаружи,
то-то и оно , что не так и есть
Т.е. для b - это не так , не из любого места ( см пример для B(1); ) и выхожу я в примере ( частично ) из контекста Замыкания.

Dmitry A. Soshnikov 18.04.2009 01:46

kefi, Вы что-то больше себя путаете, толком сами не определились, что хотите спросить (и что конкретно не понятно). Не надо подобных примеров с тучей комментарием, тучей переменных и т.д. Если хотите что-то понять, и осознать (и чтобы легко запоминалось) - надо делать это на примерах с "a" и "b" - не более.

Цитата:

Сообщение от kefi
Так вроде я сам предложил объяснение.

Замечательно, но старайтесь не выдумывать.

Цитата:

Сообщение от kefi
как только замкнули (myScope = C)

Я ж выше писал, что это просто ссылка, а не замыкание (замыкание уже произошло - и это "C"). Зачем Вы продолжаете себя запутывать?

Цитата:

Сообщение от kefi
Я просил для практического руководства

Ну так и нужно стараться понимать ;)

Цитата:

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

Давайте так, на очень кратком примере Вы описываете, что не ясно, а мы постараемся очень по-простому объяснить.

P.S.:

Цитата:

Сообщение от kefi
[[VO]]

Тоже лучше не миксовать обозначения. Когда писалось [[Scope]] - это реальное обозначение из стандарта. Обозначая таким же образом VO, Вы можете сбить с толку тех, кто будет это читать и не знать пока про VO.

Zeroglif 18.04.2009 09:06

Цитата:

Сообщение от kefi
для b - это не так , не из любого места

Так, так. Функция 'B' видит тот же самый объект, что и функция 'C' - VO(A), т.к. обе функции имеют этот объект в своей scope chain. Поэтому изменение значения переменной 'a' будет отражено. И, наоборот, изменение значения переменной 'b' не видно 'C', т.к. у неё совсем другой объект в scope chain - VO(B) от первого вызова 'B', а 'b' меняется в VO(B) от второго вызова...

Артем Шалхаков 18.04.2009 11:12

Все-таки жесть этот JavaScript.

Нужна лексическая область видимости -- юзайте лексическую, нужна динамическая -- используйте ее, опять же.

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

Пусть дан такой кодец:

var x = 0;
var f = function() { return x; }
var g = function() { var x = 1; return f(); }


Если область видимости динамическая, то g вернет 1, а если лексическая -- то 0. И не нужные никакие activation objects, [[SCOPE]] и прочая муть. :)

Читать: http://en.wikipedia.org/wiki/Scope_(programming) или SICP, там отличные примеры.

Ну и конкретно по JS: http://www.digital-web.com/articles/...in_javascript/

Dmitry A. Soshnikov 18.04.2009 12:30

Цитата:

Сообщение от Артем Шалхаков
Если область видимости динамическая, то g вернет 1, а если лексическая -- то 0.

Тогда, относительно этих определений, я смиксовал/напутал, и [[Scope]] JS можно отнести к lexical (static) scope, поскольку [[Scope]] однозначно зашился в функцию при её создании, и именно в нём будет искаться "x".

kefi 18.04.2009 13:47

Цитата:

Сообщение от Zeroglif
Так, так.

Не так не так, так так. Уже запутались. Вы, видимо , правильно понимаете, но то, что говорил я не поняли. Спасибо за пример с EVAL, который я сразу не заметил. Он кстати показал мне , что СКОП замыкания не может получить новые слоты после Замыкания, т.е. вообще говоря Замыкания могут общаться через свои Скопы Замыкания, если они пересекаются, НО вот нельзя с помощью EVAL взять и добавить новые переменные в СкопЗамыкания.


Цитата:

Сообщение от Dmitry A. Soshnikov
как только замкнули (myScope = C)
Я ж выше писал, что это просто ссылка, а не замыкание (замыкание уже произошло - и это "C"). Зачем Вы продолжаете себя запутывать?

До этих Ваших слов (что я выделил ) мне было все ясно. Но вот они действительно меня запутывают. Мне так хорошо все понималось, когда я считал , что замыкание это процесс присвоения ссылки на FE (myScope = C) переменной или ,точнее , просто использование имени Замыкаемой функции в выражении, и саму такую переменную-ссылку я тоже называл Замыканием.
Цитата:

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

Цитата:

[[VO]]
Тоже лучше не миксовать обозначения. Когда писалось [[Scope]] - это реальное обозначение из стандарта.
VO - это тоже понятие стандарта, обозначающее, кстати внутренний объект такой же как и остальные обозначенные там через [[...]], а почему оно там не обозначено в [[...]], как остальные внутренние объекты, - не знаю.

Цитата:

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

Так , а что собственно Вам в данном вопросе напомнило бензин с кошачьим дерьмом вместо взрывной смеси ?
И , Кстати, причем здесь взрывная смесь? Без Замыканий при использовании функций в качестве переменных логически невозможно обойтись , даже если Вы не нуждаетесь ни в какой взрывной смеси.

Zeroglif 18.04.2009 14:35

Цитата:

Сообщение от kefi
но то, что говорил я не поняли

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

"Замыкания могут общаться через свои Скопы Замыкания"


и это:

"...с частичным выходом из Контекста Замыкания"

и уж тем более:

"Программма всегда может получить доступ ко всему [[Scope]]'у' Замыкания передав управление в Замкнутую функцию ЧЕРЕЗ Замыкание."


p.s. и первый, и второй пример - совершенно обычные по своему поведению, всё абсолютно в рамках логики языка, немного усложнено рекурсией, но это ничего не меняет в схеме создания scope chain и в схеме разрешения идентификаторов.

kefi 18.04.2009 15:22

Цитата:

Сообщение от Zeroglif
что сломалось (что не нравится),

А где я сказал, что что-то сломалось или мне не нравится ?
1)
Цитата:

"Программма всегда может получить доступ ко всему [[Scope]]'у' Замыкания передав управление в Замкнутую функцию ЧЕРЕЗ Замыкание."
Выделенное означает вызов Замкнутой функции через Замыкание :
т.е. myScope(), а не через C().

Цитата:

"Замыкания могут общаться через свои Скопы Замыкания"
Цитата:

"...с частичным выходом из Контекста Замыкания"
А это вообще становится понятным , если прочитать определения , которые я дал в том посте.
Повторю их немного яснее.
/* Более корректное Уточнение Замыкания .
Замыкание - установка Защитника(Телохранителя) для Скопа Замыкания(см ниже). Пока он живой или пока живы те, кому его
скопировали, СкопЗамыкания не убить. Защитником является переменная Замыкания (или просто 'Замыкание'), т.е. ссылка
на замыкаемую функцию (переменная или фактический параметр).
Установить Замыкание (Замкнуть) - можно ТОЛЬКО на Уровне(см ниже Уровни) Контекста Замыкания и вложенных. Установить -
значит присвоить с помощью FE ссылку на функцию переменной или, точнее, просто использовать Имя Замыкаемой функции в выражении .

Когда Программа может менять слоты [[Scope]]'a' Замыкания (основная проблема анализа Замыканий):

[[Scope]] Замыкания - [[Scope]] замыкаемой функции. Он Живой пока живой его Защитник ссылка на Замыкаемую функцию.
Контекст Замыкания - Весь Внешний контекст Замыкаемой функции, т.е. Весь Контекст в который она вложена.
Все вызовы через Замыкание происходят с использованием [[Scope]]-а Замыкания для определения в вызываемой функции
значений свободных переменных, точнее слотов [[Scope]]'а Замыкания .

Уровни Контекста - Верхний(0 или Global),Первый(1 или функций Верхнего уровня(А функций)), Второй(2 или B) , etc ...
( Уровнем Контекста Замыкания считается не Global,а Уровень контекста Замыкаемой функции. )

В случае выхода управления на Верхний уровень Программе оказываются доступными только слоты
[[Scope]]'а' Замыкания для [[Scope Chain]]'а' этого уровня, т.е. только [[VO]]Global.

Если управление выходит из Контекста Замыкания не полностью на Верхний Уровень, а на какой-то его Уровень ниже,
то Программе на этом Уровне становятся доступными ТОЛЬКО слоты [[Scope]]'а' Замыкания для [[Scope Chain]]'а'
этого Уровня , При дальнейшем входе с этого Уровня на Вложенные уровни, вновь создаваемые [[VO]] вложенных уровней
ЗАКРЫВАЮТ(консервируют) сответствующие [[VO]] для этих уровней из [[Scope]]'а' Замыкания.

Программа всегда может получить доступ ко всему [[Scope]]'у' Замыкания, передав управление в Замкнутую функцию
ЧЕРЕЗ Замыкание.

Разные Замыкания могут общаться через пересекающиеся слоты своих СкоповЗамыкания.
*/
function al(o){document.write(o,'<br>')} ;
al('g,a,b,c ---------------');
var myScope , g = 0 , myScope2 ;
A();
function A(x) { // Вход сюда создает новый [[VO]]A ,но не затрагивает [[VO]]Global === ( [[Scope]]A == [ myScope, g==0 ] )
    var a;
    if (x==1) { a=2; b=2; c=2; al( [g, a, b, c, '<---A( )'] );  myScope(2) ; return  } ; 
    if (x==2) { al( [g, a, b, c, '-- имеем в A( )']); return  } ; 
    myScope2=B;
    B(); 
    function B(y,e,e1) { // Вход сюда создает новый [[VO]]B ,но не затрагивает [[VO]]А,[[VO]]Global 
        var b;
        if (y==1) { a=3; b=3; c=3; eval(e); al( [g, a, b, c,eval(e1) , '<---B( )']); myScope(2); return  } ; 
        if (y==2) { al( [g, a, b, c,eval(e1),  '-- имеем в B( )']); return  } ; 
        C();
        function C(z,e,e1) { // Вход сюда создает новый [[VO]]С ,но не затрагивает [[VO]]B, [[VO]]А ,[[VO]]Global 
            var c; 
            if (z==1) { a=4; b=4; c=4; al( [g, a, b, c,eval(e1), '<---C( )']); myScope(2); return  } ; 
            if (z==2) { al( [g, a, b, c, eval(e1), '-- имеем в C( )']); return  } ; 
            
            // myScope = B ; // [[Scope]]B == [ myScope, g==0,a==1,x, B ]
            // Замкнуть, Контекстом Замыкания будет весь Внешний Контекст функции C :
            myScope = C ; 
            // [[Scope]] Замыкания  [[Scope]]C == [ myScope, g==0,a==1,b==1, x, y, B, C ]
            // g,a,b,c ---------------
            // 0,,,,-- имеем в C( ) // <-исходное состояние [[Scope]]'а' Замыкания
            myScope(2);
            
            // Выход на уровень Контекста Global - Полный выход из Контекста Замыкания, В вызываемой функции A
            // никакие слоты [[Scope]]'a' Замыкания не доступны, кроме [[Scope]]A === [[VO]]Global слотов  (ни а ни b не изменить)
            // 0,2,2,2,<---A( )
            // 0,,,,-- имеем в C( )
            A(1); 
            
            // Выход на Уровень Контекста А - Частичный выход из Контекста Замыкания,
            // в вызываемой B() будут доступны только слоты [[Scope]]'a' Замыкания для [[Scope]]B ( a можем изменить, b -нет )
            // 0,3,3,3,<---B( )
            // 0,3,,,-- имеем в C( )  // 0,3,3,1,-- имеем в C( ) для вызова C(2) вместо myScope(2),т.е. b меняем !
            B(1);   
            
            // Выход на уровень Контекста B, Нет выхода из Конекста Замыкания, 
            // в вызываемой  С() будут доступны все слоты [[Scope]]'a' Замыкания для [[Scope]]C (a и b можно изменить)
            C(1);   
            // 0,4,4,4,<---C( )
            // 0,4,4,,-- имеем в C( )
            return; // Выход на уровень Контекста B            
        };
        return ; // Выход на уровень Контекста A
	};
    return ; // Выход на уровень Контекста Global - Полный выход из Контекста Замыкания  
};
al('-----------Как Разные Замыкания могут общаться через пересекающиеся слоты своих СкоповЗамыкания. ---------------------');
al([g,a,b,c,'--имеем в Global как нагрузку от некоторых первичных присваиваний во внутренних функциях ']); // 0,4,2,3,--имеем в Global
myScope2(2); // 0,4,,3,-- имеем в B( ) - в СкопеЗамыкания B имеем тот же a , что и 
myScope(2);  // 0,4,4,,-- имеем в C( )   в СкопеЗамыкания C (но переменной b из СкопаЗамыкания С в СкопеЗамыкания B нет)
myScope2(1,'var ee=11'); // 0,3,3,3,<---B( )
             // 0,3,4,,-- имеем в C( )
//myScope(2,1,'ee'); // ee НЕ ОПРЕДЕЛЕН в C !!! EVAL не меняет слоты Контекста Замыкания !

al([g,a,b,c,'--имеем в Global как нагрузку от некоторых первичных присваиваний во внутренних функциях ']); // 0,4,2,3,--имеем в Global

Zeroglif 18.04.2009 15:51

Цитата:

Сообщение от kefi
Повторю их немного яснее.

:) :) :)

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


Часовой пояс GMT +3, время: 03:07.