Javascript-форум (https://javascript.ru/forum/)
-   Учебные материалы (https://javascript.ru/forum/study/)
-   -   Secrets of the Javascript Ninja (https://javascript.ru/forum/study/5724-secrets-javascript-ninja.html)

Dmitry A. Soshnikov 01.11.2009 15:13

Цитата:

Сообщение от Riim
разве у объектов есть scope? Он же у функций.

Scope - у контекстов, [[Scope]] - у функций.

Riim 06.11.2009 05:49

Zeroglif, Dmitry A. Soshnikov, спасибо за объяснение.
Получается, что аксессор - это, в понимании интерпретатора, свойство с привязкой к своему контексту и если есть что-то, что заставляет прочитать это свойство (вызвать GetValue), то привязка к контексту теряется и это уже не аксессор?

Dmitry A. Soshnikov 06.11.2009 11:38

Цитата:

Сообщение от Riim
свойство с привязкой к своему контексту

Не совсем. Псевдокодом, значение типа Reference Type можно представить в виде обычного объекта с двумя свойствами - база (base) (т.е. объект, которому принадлежит свойство) и имя свойства (property name) внутри базы:

var valueOfReferenceType = {
  base: <базовый объект>,
  propertyName: <имя свойства>
};


Далее, если мы имеем в коде обращение к переменной, в силу вступает алгоритм (и механизм) разрешения имени идентификатора (этой переменной):

var foo = 10;
alert(foo);


На выходе алгоритма разрешения имени - всегда значение типа Reference Type. Т.е. для нашей переменной foo, результатом разрешения имени будет:

var fooReference = {
  base: Global, // т.к. foo - в глобальном объекте
  propertyName: "foo"
};


Главным моментом разрешения имени является именно тот факт, что возвращается объект типа Reference Type, т.е. без получения значения этой переменной. Иными словами, если мы пишем код:

foo;


Мы получаем "на выходе" - fooRef.

И только затем (в примере выше), когда вызывается alert, уже вызывается метод GetValue для fooRef.

GetValue вернёт уже значение другого типа (в данном случае Number - 10), но не значение ReferenceType.

Псевдокод алгоритма GetValue:

function GetValue(fooReference) {
  var base = GetBase(fooReference); // Global
  return base.[[Get]](GetPropertyName(fooReference)); // 10
}


Так вот, главный момент, касающийся вызова функции - выражение вызова ожидает слева (от скобок вызова) значение типа Reference Type. Если это так, то в качестве this-value будет база этого значения:

// в первых скобках - значение типа
// Reference Type, т.к. сработал алгоритм
// разрешения имён идентификаторов,
// базой является obj, именно он будет передан
// в качестве this-value
(obj.m)();


Если же слева стоит значение не Reference Type (т.е. любого другого типа), то в качестве this-value будет null. Исключение составляет лишь объект активации: в том случае, когда он является базой, также подставляется null.

Повторю, что оператор присваивания вызывает GetValue, и, соответственно, на выходе мы получаем значение типа Object, а не Reference Type:

// this-value будет null
(obj.m = obj.m)();


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

Riim 06.11.2009 12:48

Цитата:

Сообщение от Dmitry A. Soshnikov
в силу вступает алгоритм (и механизм) разрешения имени идентификатора

Как это для аксессора формулируется? "Алгоритм разрешения имени идентификатора который в аксессоре", или "Алгоритм разрешения аксессора", или еще как-то?

Цитата:

Сообщение от Dmitry A. Soshnikov
объект активации

А кто это?

Dmitry A. Soshnikov 06.11.2009 13:13

Цитата:

Сообщение от Riim
Как это для аксессора формулируется? "Алгоритм разрешения имени идентификатора который в аксессоре", или "Алгоритм разрешения аксессора", или еще как-то?

Если стоит просто obj, то - это MemberExpression -> PrimaryExpression -> Identifier. Соответственно, разрешение имени получается для obj.

Если obj.m, то это MemberExpression.Identifier. Соответственно, для m.

Ещё, акксессор (чтобы не путать терминологию) - это выражение доступа к свойству - точка или квадратные скобки, но не сами свойства.

Цитата:

Сообщение от Riim
А кто это?

Объект активации.

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

function foo() {
  function bar() { return this; }
  alert(bar()); // равносильно AO.bar() => null.bar(); => Global.bar();
}


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

Zeroglif 06.11.2009 16:28

Цитата:

Сообщение от Dmitry A. Soshnikov
Мы получаем "на выходе" - fooRef.

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

Цитата:

Сообщение от Dmitry A. Soshnikov
выражение вызова ожидает слева (от скобок вызова) значение типа Reference Type

Точнее ожидает объект, ещё точнее ожидает callable объект, иначе ошибка. Какое именно выражение стоит слева и будет ли оно вычислено в значение Reference Type это как бы важно только с точки зрения 'this'.

Цитата:

Сообщение от Dmitry A. Soshnikov
Если obj.m, то это MemberExpression.Identifier. Соответственно, для m

Неа, резолвится левая часть и то, если там идентификатор только.

Dmitry A. Soshnikov 06.11.2009 17:18

Цитата:

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

Ай, не доглядел, действительно:

Цитата:

Сообщение от ECMA-262-3 12.4. Expression Statement
1. Evaluate Expression.
2. Call GetValue(Result(1)).

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

В alert-e всё ещё Reference Type, и только потом будет получено значение.

Цитата:

Сообщение от Zeroglif
Точнее ожидает объект, ещё точнее ожидает callable объект, иначе ошибка. Какое именно выражение стоит слева и будет ли оно вычислено в значение Reference Type это как бы важно только с точки зрения 'this'.

Ну да, если более точно, то так, конечно.

Цитата:

Сообщение от Zeroglif
Неа, резолвится левая часть и то, если там идентификатор только.

Да, и вправду, тоже ошибся. Только PrimaryExpression : Identifier резолвится.

А MemberExpression . Identifier, вычисляется по своему алгоритму, где будет отрезолвен MemberExpression (на этапе PrimaryExpression : Identifier), получено его значение, получено значение Identifier, которое эквивалентно <identifier-string>, и возвращено новое значение типа Reference c базой ToObject(значение MemberExpression) и именем свойства ToString(значение Identifier).

Спасибо, что поправил ;) Это важные замечания.

Riim 07.11.2009 03:27

Цитата:

Сообщение от Dmitry A. Soshnikov
А MemberExpression . Identifier, вычисляется по своему алгоритму, где будет отрезолвен MemberExpression (на этапе PrimaryExpression : Identifier), получено его значение, получено значение Identifier, которое эквивалентно <identifier-string>, и возвращено новое значение типа Reference c базой ToObject(значение MemberExpression) и именем свойства ToString(значение Identifier).

т. е. сначала создается ReferenceType для obj, а потом второй ReferenceType для m из obj.m , при этом для создания второго ReferenceType нужно вызвать GetValue у первого?

Zeroglif 07.11.2009 11:46

Riim,

1) Аксессор (доступ к свойству):

выражение.выражение
выражение[выражение]

2) Идентификатор:

foo

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

Dmitry A. Soshnikov 07.11.2009 12:42

Цитата:

Сообщение от Riim
т. е. сначала создается ReferenceType для obj, а потом второй ReferenceType для m из obj.m , при этом для создания второго ReferenceType нужно вызвать GetValue у первого?

Давай посмотрим алгоритм:

MemberExpression . Identifier

// получаем значение Reference Type для MemberExpression
1. Evaluate MemberExpression.

// получаем значение Result(1)
2. Call GetValue(Result(1)).

// вычисляем выражение для Expression,
// в нашем случае - для Identifier
// это может быть просто строка,
// идентификатор, который преобразуется
// в строку или выражение
3. Evaluate Expression.

// получаем значение, если у нас просто
// строка - она и вернётся из GetValue
4. Call GetValue(Result(3)).

// преобразование к объекту значения MemberExpression
5. Call ToObject(Result(2)).

// преобразование к строке значения Identifier
// в нашем случае ничего не меняется
6. Call ToString(Result(4)).

// возвращаем новое значение типа Reference
// c базой MemberExpression и именем свойства Identifier
// с учётом предыдущих преобразований
7. Return a value of type Reference whose base object is Result(5) and whose property name is
Result(6).


Цитата:

Сообщение от Zeroglif
(про скобки и функции хоста опускаю)

А что там за случаи?


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