Комментарии 39
Интересная вещь. Спасибо. Особенно порадовал профайлер.
0
Молодец, что переводишь статьи / создаешь русскоязычные версии. Чтобы русское JS-сообщество не отставало — это полезное и хорошее дело.
По поводу прокси, от себя могу добавить несколько примеров, когда я экспериментировал с ними в самых первых имплементациях SpiderMonkey:
Из интересного:
1. Массивы с отрицательными индексами — как в Python:
2. Мета-программирование на уровне объекта — фишки с __count__, __noSuchProperty__, __delete__, __get__, __set__ и т.д. Это только ради академического интереса (вообще, прокси и создавались, чтобы разделить мета- и прикладной уровни).
Можно менять трэп обычным присваиванием функции:
3. Сахар для разделенного мета-уровня
4. Оптимизированный __noSuchMethod__ — функция-активатор реюзается.
5. Делегирующие примеси (как в Ruby) или множественное наследование. Используется именно реюз и делегация, а не просто копирование в собственные свойства.
И т.д. — больше можно найти здесь.
P.S.: был длинный тред с дебатами, где мы обсуждали, нужно ли добавить флаг isCall в get трэп — чтобы различать выражение вызова: foo.bar от foo.bar(), но пока решили не делать (что затрудняет имплементацию __noSuchMethod__ — нужна функция-активатор, которая является значением любого свойства).
По поводу прокси, от себя могу добавить несколько примеров, когда я экспериментировал с ними в самых первых имплементациях SpiderMonkey:
Из интересного:
1. Массивы с отрицательными индексами — как в Python:
var a = Array.new(1, 2, 3); console.log(a[-1]); // 3 console.log(a[-2]); // 2 a[-1] = 10; console.log(a); // 1,2,10 console.log(a[-1]); // 10
2. Мета-программирование на уровне объекта — фишки с __count__, __noSuchProperty__, __delete__, __get__, __set__ и т.д. Это только ради академического интереса (вообще, прокси и создавались, чтобы разделить мета- и прикладной уровни).
Можно менять трэп обычным присваиванием функции:
var foo = Object.new({ __get__: function (name) { console.log('__get__:' + name); } }); foo.bar; // __get__: bar foo.baz; // __get__: baz foo.__get__ = function (name) { console.log("New get: " + name); }; foo.bar; // New get: bar
3. Сахар для разделенного мета-уровня
var foo = Object.new({ // normal object level data: { x: { value: 10, writable: true } }, // meta-level meta: { noSuchProperty: function fooNoSuchProperty(name) { console.log("noSuchProperty: " + name); }, noSuchMethod: function fooNoSuchMethod(name, args) { console.log("noSuchMethod: " + name + ", args: " + args); } }, // a prototype of an object proto: Object.prototype }); foo.bar; // noSuchProperty "bar" foo.bar(1, 2, 3); // noSuchProperty: "bar" -> noSuchMethod: "bar", args: 1, 2, 3 // normal-level doesn't disturb meta-level foo.noSuchProperty = 10; // assign to normal-level, but not to meta-level // still noSuchProperty of the meta-level is called foo.baz; // noSuchProperty: "baz" // the same with noSuchMethod foo.noSuchMethod = 20; // assign to normal-level, but not to meta-level // still the meta-level noSuchMethod is activated foo.baz(10, 20, 30); // noSuchMethod: "baz", args: 10, 20, 30
4. Оптимизированный __noSuchMethod__ — функция-активатор реюзается.
5. Делегирующие примеси (как в Ruby) или множественное наследование. Используется именно реюз и делегация, а не просто копирование в собственные свойства.
И т.д. — больше можно найти здесь.
P.S.: был длинный тред с дебатами, где мы обсуждали, нужно ли добавить флаг isCall в get трэп — чтобы различать выражение вызова: foo.bar от foo.bar(), но пока решили не делать (что затрудняет имплементацию __noSuchMethod__ — нужна функция-активатор, которая является значением любого свойства).
+8
Особо радует пункт 3 =)
0
Спасибо за интересные дополнения, которые можно реально применять. Часть из них я видел в твоем комментарии в блоге Brendan Eich, но поленился дописать. Стараюсь продвигать, но как видно, в силу тех или иных причин, нововведения пока не особо интересны. Твой es-laboratory я просмотрю на днях.
0
На самом деле, скажу субъективно, оно достаточно тяжело. Если не разбираться с этим на высоком уровне — не осилишь. И количество плюсов за такую сильную статью это только доказывают. Высококлассных Javascript-прогеров, в т.ч. на Хабре — единицы.
Мне кажется, намного лучше было бы выкладывать подобные вещи малыми порциями. Даже тему, освещенную в статье — разложить штук на 5 мелких (хоть и объекмных) статтей, с примерами и разжовыванием.
Мне кажется, намного лучше было бы выкладывать подобные вещи малыми порциями. Даже тему, освещенную в статье — разложить штук на 5 мелких (хоть и объекмных) статтей, с примерами и разжовыванием.
0
ну и зря не добавили.
хотел сделать автопривязку методов к объектам при получении на них ссылки, но обломался.
хочется простую логику: если метод «вызывается», то и пусть себе вызывается. а вот если «получается» и берётся из прототипа, то в объекте создаётся привязанный к нему прокси и возвращается он.
var c= function(){
this.a= function(){}
}
c.prototype.b= function(){ this.a() }
var o= new c
addEventListener( 'load', o.a, false ) // a вызовется контексте window
addEventListener( 'load', o.b, false ) // b вызовется в контексте o
removeEventListener( 'load', o.b, false ) // o.b не меняется при каждом обращении
сейчас можно делать такие финты лишь для всех методов без разбора, а не только для колбэков. isCall сильно бы в этом помог.
хотел сделать автопривязку методов к объектам при получении на них ссылки, но обломался.
хочется простую логику: если метод «вызывается», то и пусть себе вызывается. а вот если «получается» и берётся из прототипа, то в объекте создаётся привязанный к нему прокси и возвращается он.
var c= function(){
this.a= function(){}
}
c.prototype.b= function(){ this.a() }
var o= new c
addEventListener( 'load', o.a, false ) // a вызовется контексте window
addEventListener( 'load', o.b, false ) // b вызовется в контексте o
removeEventListener( 'load', o.b, false ) // o.b не меняется при каждом обращении
сейчас можно делать такие финты лишь для всех методов без разбора, а не только для колбэков. isCall сильно бы в этом помог.
0
Основной протест был в поддержке не только «функций-вызовов», но и функций, как объектов. I.e.: не только так: foo.bar(), но и foo.bar.apply(...). Записи должны (?) быть семантически эквивалентны. Достичь этого можно лишь всегда возвращая функцию-активатор. Но я тоже ратовал за isCall флаг.
0
ну, они тут вовсе не должны. другое дело, что по другому передать параметры функции Одной переменной никак не получится. лучше бы сделали синтаксис для вызова метода с недетерминированным числом параметров без отрыва от объекта.
foo.bar( arguments: [1,2,3] ) // foo.bar.apply( foo, [1,2,3] )
и вот этот вызов уже должен быть полностью эквивалентен foo.bar( 1, 2, 3 )
foo.bar( arguments: [1,2,3] ) // foo.bar.apply( foo, [1,2,3] )
и вот этот вызов уже должен быть полностью эквивалентен foo.bar( 1, 2, 3 )
0
выглядит как-то слишком костыльно. передать можно только переменную, нельзя заинплейсить какое-нибудь выражение. но таки да, с такой фичей использовать obj.func.apply будет бессмысленно кроме случаев где надо именно переопределить контекст. мой вариант как-то удобней, наглядней и расширяемей
напомнило e4x — расширили синтаксис для работы xml, но работает он исключительно с xml. в результате чего никакой сторонний объект не может реализовать тот же интерфейс.
[a][b]1[/b][/a]..b // 1
({a: {b: 1 } })..b // неизлечимая рантайм ошибка
так что к нему даже прокси не прикрутить.
а всего-то надо было, чтобы оператор ".." вызывал метод __descendants__
напомнило e4x — расширили синтаксис для работы xml, но работает он исключительно с xml. в результате чего никакой сторонний объект не может реализовать тот же интерфейс.
[a][b]1[/b][/a]..b // 1
({a: {b: 1 } })..b // неизлечимая рантайм ошибка
так что к нему даже прокси не прикрутить.
а всего-то надо было, чтобы оператор ".." вызывал метод __descendants__
0
Помоему, rest и spread — элегантный сахар для foo.bar.apply( foo, [1,2,3] )
В «каноничном» JavaScript нет оператора
А вот перегрузки операторов, на сколько я знаю, в JavaScript пока не планируется.
В «каноничном» JavaScript нет оператора
..
— это расширение Mozilla, как вы уже написали, поэтому ждать чего-то ( __descendants__
) стоит только от разработчика расширения. Прочие браузеры реагируют на ..
как на ошибку вроде «expected identifier, got '.'» т.к. парсят код «по стандарту» и врятли будут что-то переделывать.А вот перегрузки операторов, на сколько я знаю, в JavaScript пока не планируется.
0
вот именно что каждый разработчик расширения копошится в своём болоте ибо нет единого механизма расширения синтаксиса. все эти расширения выглядят как костыли, прилепленные сбоку и постоянно спотыкающиеся о костыли соседних расширений.
ну вот почему не планируется-то? сделали же перегрузку «точки» с помощью гетеров. сделали перегрузку «присваивания» с помощью сеттеров. вроде даже с помощью прокси-функции можно перегрузить «вызов» и «инстанцирование». почему не дать перегружать и остальные операторы?
пока что мы видим робкие попытки решить частные случаи, вместо того чтобы решать проблему комплексно. язык всё усложняется и усложняется, вместо того, чтобы упрощаться и унифицироваться.
ну вот почему не планируется-то? сделали же перегрузку «точки» с помощью гетеров. сделали перегрузку «присваивания» с помощью сеттеров. вроде даже с помощью прокси-функции можно перегрузить «вызов» и «инстанцирование». почему не дать перегружать и остальные операторы?
пока что мы видим робкие попытки решить частные случаи, вместо того чтобы решать проблему комплексно. язык всё усложняется и усложняется, вместо того, чтобы упрощаться и унифицироваться.
0
Второе, функции-прокси можно использовать для создания псевдо-классов, сущности которых функции(instances are callable).
function Thing() { /* initialize state, etc */ return makeCallable(this, function() { /* actions to perform when instance is called like a function */ }); }
Между прочим, это не хватало. На примере jquery синтаксический сахар:
var $table = $('table');
$table('td');
// вместо
$table.find('td');
0
Уху… все жду проксей чтобы создать класс Nothing, который всегда будет возвращать сам себя вместо вываливания object property/method doesn't exist и заменить-таки null.
0
var Nothing = function () { return Nothing; }
var NewNothing = new new new new new Nothing();
console.log(NewNothing == Nothing); // true
0
Не, смысл был в том, чтобы, к примеру
В мозилке можно использовать _noSuchMethod_, но только в мозилке.
a = something(); // если метод вываливается с ошибкой, то возвращается Nothing
console.log(a.split(1, 15).length ) // a любые последующие манипуляции c медотами/полями возвращают Nothing
В мозилке можно использовать _noSuchMethod_, но только в мозилке.
0
использую уже около месяца в node.js c node-proxy… ощущения как будто развязали руки
0
ну и хрень. зачем разделять проксиметоды и проксиобъекты? почему не разрешить любому объекту быть прокси? почему не сделают наконец, чтобы не только функции были объектами, но и объекты могли быть функциями? зачем на каждый паттерн придумывают свою кривую нативную реализацию вместо того, чтобы расширить возможности метапрограммирования?
достаточно сделать поддержку __call__, __proto__, __get__, __set__, __cast__ и этого хватит для всего.
достаточно сделать поддержку __call__, __proto__, __get__, __set__, __cast__ и этого хватит для всего.
+1
Прокси создавались, чтобы разделить мета- и прикладной уровни
+2
Я с вами полностью согласен. Несостоятельность новых (порой очень странных) паттернов создает предпосылки к созданию сложных и таких же странных механизмов. Особенно дико выглядит пример с поддержкой нерабочего __noSuchMethod__ через нерабочую и запутанную конструкцию с прокси. Чтобы починить нашу машину, нам нужно починить нашу машину через выхлопную трубу.
0
Вам дают инструмент для создания инструментов, а вы против. Proxy открывают широчайшие возможности для JavaScript. Теперь JavaScript может, как никогда раньше, поглотить любые языковые парадигмы.
0
Я прекрасно пользуюсь хорошими продуманными инструментами. Увы, история развития JS такова, что он пока всё ещё то там, то сям, но «поглощать любые языковые парадигмы» — это далеко не то, что я лично хочу видеть в коде, созданный кем-то другим и пытаясь понять, чем автора не устроил достаточный набор существующих правил. Я не против развития Ecma, даже в парадигму OO, геттеры-сеттеры и т.п., но имхо уже через-чур глядеть в код и видеть отрицательный индекс массива (которого никогда не могло быть) и задавать себе вопрос «wtf?!». К тому же, «теперь js может» — сильно сказано )
0
У меня когда-то была такая же позиция. Но судьба JavaScript такова, что он является дополнительным языком, далеко не все знают его в совершенстве, не все компании могут позволить себе держать выделенного JavaScript программиста.
Довольно часто пишут на JavaScript как на Ruby, Python, PHP — Prototype.js, переход RoR на CoffeeScript отличный тому пример. И как бы мы не хотели заставить всех писать на JavaScript как на JavaScript (как учили деды) у нас ничего не получится.
Для упрощения внедрения семантик и были созданы прокси. К тому же все эти нововведения не так уж и плохи, помоему, отрицательный индекс это хорошо. Сейчас моя позиция более толерантна: «Я пишу на JavaScript как на JavaScript и все проекты над которыми я работаю пишут аналогично, а что делают остальные мне не важно потому, что я на них не могу повлиять».
Довольно часто пишут на JavaScript как на Ruby, Python, PHP — Prototype.js, переход RoR на CoffeeScript отличный тому пример. И как бы мы не хотели заставить всех писать на JavaScript как на JavaScript (как учили деды) у нас ничего не получится.
Для упрощения внедрения семантик и были созданы прокси. К тому же все эти нововведения не так уж и плохи, помоему, отрицательный индекс это хорошо. Сейчас моя позиция более толерантна: «Я пишу на JavaScript как на JavaScript и все проекты над которыми я работаю пишут аналогично, а что делают остальные мне не важно потому, что я на них не могу повлиять».
0
Спорить о развитии JS смысла нет, это просто моё личное мнение. Будет так будет.
Только писать фиговый код можно на любом языке, а тут выглядит как дополнительный стимул к изобретению ещё более мутантных вещей, написанного программистом из вселенной другого языка, пытающегося адаптировать под свою парадигму JS. Когда есть ограниченный набор правил, это ещё хоть как-то сдерживает, а когда правила устанавливаются динамически кем попало и как попало, это где-то на пути к хаосу.
Только писать фиговый код можно на любом языке, а тут выглядит как дополнительный стимул к изобретению ещё более мутантных вещей, написанного программистом из вселенной другого языка, пытающегося адаптировать под свою парадигму JS. Когда есть ограниченный набор правил, это ещё хоть как-то сдерживает, а когда правила устанавливаются динамически кем попало и как попало, это где-то на пути к хаосу.
0
> но имхо уже через-чур глядеть в код и видеть отрицательный индекс массива (которого никогда не могло быть) и задавать себе вопрос «wtf?!»
Да ну, всего лишь сахар для более удобного обращения к индексам с конца:
И такая парадигма используется в разных языках — те же Ruby, Python. Хотите используйте (с сахаром), хотите не используйте (без сахара, с шумом).
Да ну, всего лишь сахар для более удобного обращения к индексам с конца:
a[1]
вместо a[a.length - 1]
.И такая парадигма используется в разных языках — те же Ruby, Python. Хотите используйте (с сахаром), хотите не используйте (без сахара, с шумом).
0
> a[1]
a[-1]
a[-1]
0
Скажем так, отрицательный индекс массива сам себе — сладкий сахар, я за. Я также за то, чтобы были стандартизированы и другие полезные вещи, уменьшающие «шум». Но только как общее ненарушаемое правило, а не кастомное решение от 3ей стороны, когда индекс -1 может значит length-1, а может значить и совершенно иное, если кому-то захотелось.
+1
Предлагалось, обсуждалось — не будет принято, т.к. backward compatibilities рулят развитием JS и основной «лейтмотив» — «не ломать WEB» (т.е. то, что уже написано). Некоторые вещи все равно будут необратимо сломаны, но это уже в зависимости от «migration tax», т.е. что достойно, чтобы поломать, а что не так и важно (как например отрицательные индексы, без которых, в принципе, можно прожить; но с ними слаще, да ;)).
0
Единственное, почему мне видится полезным технология проксей и о чем было сказано, это песочницы. Но складывается ощущение, что эта функциональность становится параллельной концепции языка (т.е. сам язык тут ни при чём, что-то просто изолировано), поэтому, может быть, стоит её (песочницу) рассматривать отдельно как дополняющую технологию, не затрагивающую сам js и не нарушающую back-compatibility.
0
Именно поэтому я и создал свою удобную обертку для работы с объектами, где все эти хуки можно навешивать простыми присваиванием в рантайме. См. выше п. 2 «Метапрограммирование на уровне объекта». Такая модель отражает, например, подход Python (откуда ES много чего позаимствовал).
Основная задача (по мнению TC-39), чтобы foo.get = 10 не означало, что ты «испортил» мета-get-хук. С другой стороны, почему «испортил», если ты сам за свои объектов и следишь — так и появляются обертки типа приведенных выше.
P.S.: кстати, уже ES5 подход с их статическими методами, типа Object.getPrototypeOf, Object.keys и прочее тоже именно из-за разделения мета- и прикладного уровня. Мне в свое время (год назад) этот подход тоже не понравился, но что сделано — то сделано, и с точки зрения разделения мета-уровня и «чтобы не испортить» это осмысленное решение и в целом правильное. Хотя, повторю, синтаксически получается много «шума», отступов и т.д.
P.S.[2]: ты еще не видел как будут value-proxy себя вести (это те, что для перегрузки операторов). Опять же — в Python это все на __magic__ методах, в ES куча синтаксического шума и телодвижений для этого. Причины — вновь разделение мета-уровня и оптимизация по памяти.
Основная задача (по мнению TC-39), чтобы foo.get = 10 не означало, что ты «испортил» мета-get-хук. С другой стороны, почему «испортил», если ты сам за свои объектов и следишь — так и появляются обертки типа приведенных выше.
P.S.: кстати, уже ES5 подход с их статическими методами, типа Object.getPrototypeOf, Object.keys и прочее тоже именно из-за разделения мета- и прикладного уровня. Мне в свое время (год назад) этот подход тоже не понравился, но что сделано — то сделано, и с точки зрения разделения мета-уровня и «чтобы не испортить» это осмысленное решение и в целом правильное. Хотя, повторю, синтаксически получается много «шума», отступов и т.д.
P.S.[2]: ты еще не видел как будут value-proxy себя вести (это те, что для перегрузки операторов). Опять же — в Python это все на __magic__ методах, в ES куча синтаксического шума и телодвижений для этого. Причины — вновь разделение мета-уровня и оптимизация по памяти.
0
Хе… JS становится все больше и больше похожим на Python… Хорошо!
-1
На самом деле он впитывает из всех по чуть-чуть, например Прокси пришли, по большому счету, из никому неизвестного AmbientTalk
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
ES5 Harmony Proxy — меняем семантику JavaScript внутри самого JavaScript