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)

Zeroglif 01.11.2009 01:21

Secrets of the Javascript Ninja
 
Читали последнюю книжку от Ресига? Только открыл и уже:

To start, it's important to realize what that the function context represents: The object within which the function is being executed. For example, defining a function within an object structure will ensure that its context always refers to that object - unless otherwise overwritten... However, what about a function that isn't explicitly declared as a property of an object? Then the function's context refers to the global object.

Ну, то что наш ниндзя прибил точный и важнейший термин "контекст" его народным определением ещё можно понять и даже простить, если бы речь шла о вызове функции "в контексте чего-либо". Но он пишет об определении/объявлении функции в связи с чем "context always refers to that object", не о вызове. Типичная избитая ошибка javascript-ньюбов. Ещё 3 страницы осилил - на каждой по системной ошибке.

Резюме - начинающим читать по диагонали (хуже не будет), хорошо знающим - читать по диагонали (чтобы составить мнение о книге и авторе), уже не начинающим, но пока плавающим - лучше не читать. ;-)

Dmitry A. Soshnikov 01.11.2009 01:58

Ниндзюцу, блин :D

Кстати, 80% пишущих на JS, под термином "контекст" (или даже полно - контекст исполнения) понимают this-value (Ниндзя в данном случае тоже использует слово "context", но подразумевает this-value).

Да, вот так продвигается неграмотность. Zeroglif, надо выступать в том месте и в то время, где эти вопросы решаются. Польза будет, я думаю.

Интересно, Резиг же, вероятно, советовался со многими перед выпуском книги. Он же, вроде, в Мозилле работает (нет?) - ну, посоветовался бы с B.Eich-ом.

Цитата:

Сообщение от Википедия
Джон Резиг (англ. John Resig) — JavaScript евангелист в Mozilla Corporation.

А кто ему этот титул присвоил? Или он самозванно? ;) Или это Википедия так балуется?

Zeroglif 01.11.2009 02:29

Dmitry A. Soshnikov,

Ресиг давно мозильный javascript евангелист, получает деньги за это... так что... Ты почитай, как евангелист пиарит в книжке конструкцию with(), несколько страниц аж. Я уже до прототипов дошёл, там вообще труба, какая-то очередность присвоения свойств (сначала из prototype, потом из конструктора, поэтому затираются)... там же про то, что свойства объекта статичны!!!, а свойства прототипа live!!! и они bind к экземпляру даже!!! после его создания. Типа своя трактовка непонятного чуда.

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

Цитата:

Кстати, 80% пишущих на JS
Угу. "Вызов в контексте" нормальные люди используют, подразумевая 'this'. Вызов! Он говорит не про вызов, а про определение функции, это чушь.

Zeroglif 01.11.2009 02:32

Ему всего-то надо понять reference type, просто разобрать почему:
var obj = { m: function () { return this; } };

alert( obj.m() );           //-> object
alert( (obj.m)() );         //-> object
alert( (obj.m = obj.m)() ); //-> global

Octane 01.11.2009 03:27

Так что то я начал сомневаться. В таком варианте: вызов функции func в контексте объекта context — правильная терминология и именование аргумента функции?
function bind(func, context) {
    return function(arg) {
        func.call(context, arg);
   };
}

Всегда смущало, что вместо context многие пишут scope — это же не правильно? Или вообще может лучше thisObj называть :)

Riim 01.11.2009 03:28

Цитата:

Сообщение от Zeroglif
просто разобрать почему

а почему? :)

Я сталкивался с подобным в немного другом виде:
alert( (obj.m || obj.n)() ); //-> global


объяснил себе так: раз "obj.m || obj.n" в скобках, то они будут вычисляться и на выходе из скобок будет просто "m" или просто "n", т. е. это тоже самое что:
var x = obj.m || obj.n;
x(); //-> global


Правильно я понял?

Riim 01.11.2009 03:37

Цитата:

Сообщение от Octane
что вместо context многие пишут scope

т. е. получается:
Цитата:

вызов функции func в scope объекта context
разве у объектов есть scope? Он же у функций.

Zeroglif 01.11.2009 12:21

Цитата:

Сообщение от Octane
Всегда смущало, что вместо context многие пишут scope — это же не правильно? Или вообще может лучше thisObj называть

'thisObj' было бы идеально, тогда не будут перекрещиваться термины, обозначающие разные вещи:

- "вызов функции в контексте объекта";
- "контекст исполнения функции";

Но, в принципе говорить о вызове в контексте (именно о вызове) - это нормально, если вы посмотрите на родные доки JavaScript (начиная с 1.3), то в отношении 'apply' и 'call' эта терминология применяется:
Цитата:

Allows you to apply a method of another object in the context of a different object (the calling object).
что в переводе на формальный язык ES расшифровывается с точки зрения вызова прямо там же:
Цитата:

assign a different this object
Отсюда, говоря о "вызове в контексте объекта" мы однозначно говорим об определении 'this'...

Zeroglif 01.11.2009 12:52

Цитата:

Сообщение от Riim
Правильно я понял?

Не совсем, но близко. Согласно Дж.Ресигу "определение функции в качестве метода объекта гарантирует, что вызов всегда будет в контексте этого объекта". Это само собой глупость, так как 'this' определяется 'caller'-ом, попросту говоря тем, как записана форма вызова, а не тем как и где определена функция. Обычную объявленную функцию уже с ходу можно вызывать по-разному, меняя 'this':

function f() {
    alert( this );
}
f();
f.prototype.constructor();

Не говоря уже о том, что её можно передавать, присваивать и т.д.

При вызове интерпретатор определяет, что стоит слева от "оператора вызова", от скобок. Вычисляется выражение и определяется тип вычисленного промежуточного результата... а это одно из двух - Reference type или не Reference type. Если там (слева) стоит идентификатор (foo) или аксессор (foo.x или foo['x']), то мы имеем дело с Reference type, а значит определённый объект, "владелец" метода в момент вызова, станет значением 'this' (activation object не в счёт). Если там любое другое выражение, например:

(foo.x || foo.y)();
(foo.x, foo.y)();
(function () {})();


то это не Reference type, значением 'this' станет null->global.

Dmitry A. Soshnikov 01.11.2009 13:41

Цитата:

Сообщение от Zeroglif
Ты почитай, как евангелист пиарит

Ага, полистаю.

Цитата:

Сообщение от Zeroglif
alert( (obj.m)() ); //-> object
alert( (obj.m = obj.m)() ); //-> global

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

Цитата:

Сообщение от 11.1.6 The Grouping Operator
NOTE
This algorithm does not apply GetValue to Result(1). The principal motivation for this is so that
operators such as delete and typeof may be applied to parenthesised expressions.

Поэтому, в первом случае после оператора группировки, у нас всё ещё Reference Type, а во втором случае, оператор присваивания (как и другие - "ИЛИ", или "запятая"), вызовет для AssignmentExpression GetValue, который, вызвав в свою очередь [[Get]], вернёт уже значение свойства, т.е. функцию, и будет уже не тип Reference. Для выражения вызова, это означает использовать Global в качестве this-value.

Цитата:

Сообщение от 11.13.1 Simple Assignment ( = )
2. Evaluate AssignmentExpression.
3. Call GetValue(Result(2)).

Цитата:

Сообщение от Octane
Всегда смущало, что вместо context многие пишут scope — это же не правильно?

С точки зрения Scope и [[Scope]] в ES (и основной точки зрения - понимания этих сущностей) - конечно, это неправильно. Этим, кстати, грешит фреймворк ExtJS, который везде для handler-ов событий указывает вторым параметром - scope: this, имея в виду thisValue.

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
(про скобки и функции хоста опускаю)

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

monolithed 26.11.2010 10:37

Отличная тема, грех не апнуть))

PeaceCoder 26.11.2010 14:50

Спасибо. Посмеялся. На сколько бывают извращенные программисты.

Теме ап. Полезно, но забивает голову мусором.

На счет Ресига. после просмотра кода JQ и парочки "тупых" багов и неудобности его UI-библиотек, его книги пропускаю глазами как только вижу John Re..

ahimaskrasavin 21.02.2012 16:48

Цитата:

Сообщение от PeaceCoder (Сообщение 80865)
На счет Ресига. после просмотра кода JQ и парочки "тупых" багов и неудобности его UI-библиотек...

Я пока что не являюсь гуру синтаксиса JS, но постоянно имею с ним дело. JQ меня во всем устраивает. Судя по всему, вы пользуетесь более эффективными библиотеками. Расскажите, пожалуйста, что вы используете.

xcislav 05.05.2014 22:03

Рэсиг эвангелирует поддержку, а те с поддержки своих доказательств, видимо съехали. Главное тему зарисовать красочно. А я вот нихрена не знаю и всё читаю. Узнал с пары страниц всё новое, интересно пишут программисты. Жалко любой может обвинить и ведущих разработчиков и идеологов. Я знаю что JS разрабатывался 10 дней из "смерти JS видео" - так там на такой основе все будут виноваты. Специально для троллей собирать сделали, для обвинителей ради их правоты пъедестала...

BETEPAH 05.05.2014 23:38

xcislav,
лолшто?

KostaShah 13.02.2016 17:38

​Здравствуйте.

Сразу попрошу оставить "за кадром" тему целесообразности чтения этой книги. Я уже прочёл пару глав, и хочу уже дочитать её. С джавоскриптом я знаком на любительском уровне, и из книги узнаю сейчас много нового.

Был бы очень признателен, если кому-то из гуру окажется несложно объяснить мне один момент. Автор несколько раз пишет, и подчёркивает, что функции являются объектами высшего порядка. Как следует понимать слова "высшего порядка"? В смысле, что они в высшей степени являются объектами? Или в смысле, что существуют разные порядки объектов - высшие, низшие, средние, ещё какие-нибудь?

Заранее благодарю.

KostaShah 21.02.2016 03:17

Поскольку всем гуру оказалось сложно :) я покопался в интернете, и вроде понял смысл слов "высшего порядка". На случай, если кому-то ещё тоже это оказалось интересным, напишу, что я выяснил. А гуры, если надо, поправят.

Итак, нету каких-то разных порядков объектов, типа высший, низший, или ещё какой-то. И первое моё предположение, что смысл этих слов просто "в высшей степени объекты" тоже неверен. А дело вот в чём. Некоторые языки программирования позволяют передавать объекты в функции (методы) в качестве аргументов, и возвращать объекты из функций (методов) в качестве возвращаемых значений. Некоторые другие языки программирования (видимо) этого не позволяют. Один какой-то англо-язычный дядя, писав про какой-то язык программирования, желая подчеркнуть, что этот язык позволяет делать с объектами то, что я написал, выразился образно, сравнив объекты в этом языке с гражданами высшего класса, типа аристократами (first class citizen). Другие дяди это подхватили, и так родился термин "объекты первого класса" или "объекты высшего класса". Это означает, что эти объекты можно передавать в качестве параметров, и получать в качестве возвращаемых значений. Позднее, этот же термин, распространился и на функции: "функции первого класса", "функции высшего порядка", и т.п. Это, опять же, означает, что функции в обсуждаемом языке могут быть переданы в другие функции в качестве аргументов, и возвращены из них в качестве возвращаемых значений.


Добавлю, заодно, мои впечатления о книге вообще. Прежде всего, хочу отметить, что книга написана очень хорошим языком (я читаю на русском). Читается легко. Не в том смысле, что всё разжёвывается, а в том, что написана не сухим языком учебника, а живым повествовательным языком, который читается как роман. Мне лично это очень нравится. От учебников меня сразу клонит в сон.

По поводу того, кому она будет полезна. Полезна она будет таким как я. Я в программировании самоучка, но программирую уже очень давно. Программировал на разных других языках. Яваскрипт специально никогда не изучал. Когда понадобилось написать что-то простенькое, ознакомился с синтаксисом, и стал писать, подглядывая в сети, что как делается. Теперь решил углубиться в него посерьёзней. И вот эта книга для этой цели - самое то. Я часто смотрел на чужие скрипты как баран на новые ворота, не понимая, что тут делают, зачем, почему, как? Теперь мне многое из этого становится понятно.
Тем, кто вообще не знаком с яваскриптом, или вообще не знаком с программированием, эта книга будет, конечно, совершенно бесполезна, ибо она будет совсем непонятна.


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