Как стать автором
Обновить

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

Хорошая статья, но, мне кажется, называть javascript ФП языком несколько неверно. Скорее всего — язык с элементами ФП. Впрочем тут все расходятся в толковании термина — так что это имхо.
НЛО прилетело и опубликовало эту надпись здесь
Спасибо, весьма интересный подход, прям учитался с головой, с эффектом —«блин дочитал :(».
НЛО прилетело и опубликовало эту надпись здесь
ну а что вы хотели — отличная статья, ни добавить, ни прибавить :) пишите ещё, с удовольствием почитаю
Шедевральная статья. Распечатаю, буду читать каждый день с фонариком под одеялом.
Спасибо!
НЛО прилетело и опубликовало эту надпись здесь
Хорошая статья. Спасибо.
Одно но: так как подавляющее число людей визуалы — я бы добавил побольше иллюстраций, особенно при объяснении как растет стек и растет цепочка замыканий. Я сам когда пытаюсь чтото понять — рисую на листке карандашом объекты и их взаимосвязи
НЛО прилетело и опубликовало эту надпись здесь
Просто стэк рисовать, конечно, не надо, а вот эту фразу: «При возврате вложенной функции bar из внешней функции foo, стековый кадр, созданный при вызове foo, сохраняется в переменной baz в составе контекста исполнения функции bar. Таким образом формируется цепочка областей видимости, совершенно не зависящая от стека вызовов.» наверное можно было пояснить. Засыпаю и так и не разобрался кадр в стэке остаётся или нет. Копируется в цепочку или переносится. По идее переносится, но с JS я ни в чём не уверен :)
НЛО прилетело и опубликовало эту надпись здесь
Да, вот так понятней :) А как на самом деле реализовано — не важно. Хотя я ваш метод использовал для окончательного понимания многих фишек в ООП C++ читая ассемблерные листинги, сгенерированные VC++ (с отключенными оптимизациями).
Шикарная статья! Читал взахлеб :) теперь прототипы перестали быть чем-то странным и не понятным)
Все-таки я всегда говорил что JavaScript — мощный язык (может я просто не знаю о подобных).

Как ни странно — вы меня не удивили (изучал его досконально), но написано шикарно, все-таки немного нового поймал.
Немного не в тему — но есть ли способ JS (в ачстности листинги из поста) запустить в консоли, в браузере не так как-то, а гугл что-то не то, что хочется выдавать, пишет про объект console а не линуксовую
Я иногда балуюсь Rhino Shell, хотя браузерными объектами там и не пахнет.
Скрин листинга
О, кажется то, что нужно. А на DOM я и не рассчитывал, особых проблем нет. Я язык понять хочу лучше.
НЛО прилетело и опубликовало эту надпись здесь
Удобство, вернее его отсутствие, непривычное. Под консолью я имел в виду терминал с bash например, а не объект consol :)
НЛО прилетело и опубликовало эту надпись здесь
Ага, спасибо :)
> Переменные x, foo и baz — глобальные,
Дальше не читал
Хотя конечно придираюсь, все правильно дальше описано. Хотел просто сказать про это:
var myValue;
function setValue()
{
myValue = «test»;
}

function getValue()
{
alert(window.myValue); // yup, it's «test»
}
вам вообще ничего читать не надо.
Приятно когда человек пытается разобраться в вопросе, и делится с нами этим опытом в доступной форме. В случае JS нужно именно уметь думать и понимать происходящее, тут не получится следовать каким-то строгим шаблонным/справочным установкам и при этом считать себя JS экспертом. Многие работают с JS годами, но вникать не пытаются.

Подобные статьи по JS очень полезны, в отличие от других статей/переводов «справочного» характера.

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

В общем просто хотел сказать спасибо :)
Хорошая статья, поздравляю (Хабр снова начинает становится «тортом»! ;))

Ниже пара дополнений.

P.S.:

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


Кстати, у GCC есть расширение — он все-таки позволяет создавать вложенные функции и их даже можно вернуть наружу (т.е. upward funarg). Достигается это за счет техники трамплинов (trampoline).

Также, если нет upward funarg'a (иными словами, если вложенная функция используется только локально внутри родительской функции, «не убегая» наружу вверх), то техника лямбда-поднятия (lambda lifting) также решает эту проблему. Именно она используется в Паскеле для вложенных функций.

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

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


Здесь стоит отметить, что «копии метода» не будет, но будет, действительно, копия слота (т.е. создастся родное свойство). Слот же этот будет ссылаться на ту же функцию. Примитивов, конечно же, это не касается.

Некоторые прототипные языки, например Io, используют так же делегирующее наследование, но метод, который осуществляет «сцепление» предка с потомком назван почему-то clone ;)

каждый конструктор «висит» над своим объектом


В общем случае, порожденный объект не зависит от своего конструктора.

Пара конструктор+прототип (в этом примере: B()+B.prototype) играет ту же роль, что класс в классическом ООП


Собственно, это и есть «класс» в JavaScript (только без сахара), поскольку реализует классовый code reuse. Как эта пара превращается в класс при добавлении сахара можно хорошо видеть, например, на классах CoffeeScript. Или Python. Да-да, Питон — это такой же делегирующий «прототипный» язык, как и JavaScript, только в нем добавлен сахар. Именно это я отмечал, говоря, что разницы «прототип vs. класс» недостаточно, всегда нужно задействовать все сопутствующие механизмы (первоклассные сущности, динамика и т.д. — здесь сводная таблицы — "классификация классов").

Этот пример с Python'ом достаточно интересен для анализа. Как только появляется «сахар» — всё, язык сразу же классовый и сразу же понятный. Но стоит убрать из Питона сахар — мы получим тот же JavaScript в этом аспекте. Таким образом, «сахар решает». А «класс» — это не какое-то ключевое слово class в языке, и даже не возможность создавать классы с помощью этого слова. Класс — это возможность программировать в классифицирующем стиле. А уж как это делается — с ключевым ли словом class или ключевым словом function — это по большому счету дело десятое.

наследование сделано по-старинке, через new


Такой же сахар над делегацией для классов (как в Python, Ruby или CoffeeScript), кстати, довольно активно обсуждался в es-discuss (возможно мы даже увидим классы в JS — такие же как в Coffee). Так что, «по-старинке» скорей всего здесь некорректно. Разница ведь в данном случае не в «старинках», а в методах code reuse'a. Если нам нужен классовый реюз (т.е. (1) возможность генерировать объекты по шаблону и (2) наследовать строгую иерархию), то мы используем классовый реюз. Если нет — пожалуйста, прототипный (т.е. (1) не нужно генерировать много однотипных объектов и (2) «наследую от кого хочу»). Это два разных код реюза и оба применимы. Здесь на Хабре был подробный тред в комментах на эту тему.

Ссылка на родительский объект (__parent__ в листингах 6 и 7) во многих реализациях называется __proto__


Не совсем удачно выбранное псевдо-имя, т.к. __parent__ — это реальное (хоть и нестандартное) свойство как раз, чтобы связываться объекты активации в scope chain. Больше подошло бы __delegate__ — оно отражает суть механизма и показывает, что по идее, может быть несколько прототипов (делегатов), если это свойство, например, будет списком. Так, к примеру, Io и Self поддерживают множественное наследование, как раз расширения список делегатов. Если, вдруг, станут интересны эксперименты, для JS тоже такое реализуется на прокси-объектах.

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


Да, там есть сложные заморочки, когда, и вроде кажется, что функция вызвана как метод, но все равно this становится глобальным (или undefined в strict mode):

var o = {m: function() {this;}};

o.m(); // o
(o.m)(); // o

(o.m = o.m)(); // global
(o.m || o.n)(); // global


В целом же, еще раз — статья отличная ;)
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Гениальная статья, спасибо огромное. Именно такого экскурса в ecma мне не хватало. Попробую поковырять в данном ключен классовый и поддерживающий строгую типизацию ActionScript 3. Вот там весёлости должны получиться.
На сколько разный может быть подход к изучению вещей :)
Мне, например, для того чтобы использовать замыкания, не пришлось изучать все до уровня стека и кадров. Тем более реализация тех или иных вещей может зависеть от движка. Ценность статьи однако несомненна, для тех кто любит покопаться в деталях и через это понять, как все работает.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации