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

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

А для методов точно нужно делать .bind(proxy)? Вроде же рантайм сам подставит прокси вместо this, когда этот самый метод будет вызывать.


Это, кстати, еще сильнее усугубляет проблему с прокси: они перестают работать даже если не пытаться делать что-то хитрое, просто по умолчанию.

Нет, не нужно. В моём оригинальном коде для такой прокси этого и нет — просто хотел указать явно на важный момент того, что функция будет вызвана в контексте прокси.

Почему в голосовалке нет поля «Закопать этот гребанный недоязык вместе с его ущербным комитетом»?
Если серьезно, то по моему мнению дело совсем не в отсутствии фидбека — комитеты по развитию других языков как то справляются с этим (java например). Остается только предположить, что в комитете по js участвуют определенного сорта люди… Почему нельзя было сделать просто модификатор private и не заигрывать с дурацкими символами типа #$@ (ну мешает оно type-script так его пусть потом и правят а сейчас получается что и c ним синтаксически не совместимо и синтаксис убог и неоднозначен) и заканчивая этим «brand-check» (вот реально причем тут приватные поля в языке и какая то безопасность? что с этими проверками будет в nodejs, где нет песочницы ?)
Закончится это видимо как с perl — язык станет настолько «законспирирован» символами и неявными связями, что его станут избегать.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Такое ощущение, что комментарий по работе комитета С++ читаю.
Я это все понимаю. Про вариант голосования это конечно шутка. Но расширять язык как бог на душу положит это же не выход. Значит нужно потратить больше времени на проработку ситуации. А здесь решение выглядит так: «Ээ… ну у нас есть неиспользуемый символ #. Давайте его используем для обозначения приватных членов класса. Ну потом когда придется еще и protected добавить как-нибудь выкрутимся».
НЛО прилетело и опубликовало эту надпись здесь

Почему не private там есть отдельное объяснение в FAQ.
А здесь я показал как можно таки использовать ключевое слово private, но у такого подхода есть определённые ограничения, самое главное из которых — это увеличение когнитивной нагрузки на и без того сложный this.

не совсем понимаю это объяснение. Чем запись
class A {
private x

method() {
this.x;
}
}

синтаксически отличается от
class A {
#x

method() {
this.#x;
}
}

Это практически эквивалентная запись за исключением того, что во втором случае атрибут приватности указан еще и при использовании. Почему в других языках без этого все нормально работает (включая и интерпретируемые), а в js ссылаются на то что это будет медленно и небезопасно? Я не тролю если что — на самом деле не понимаю мотивацию. Если такие проблемы с производительностью и безопасностью, так может лучше тогда остановиться и подумать что еще нужно изменить в языке что бы стало возможным дать ему нормальный синтаксис и указывать область видимости один раз при объявлении, а не кругом и всюду, жертвуя читаемостью.
По поводу того что это можно реализовать через 27 строк кода — ну так многое из введенного в стандарт можно было реализовать через определенное количество строк кода. Но это добавили потому, что не хорошо когда есть стопятсот реализаций фичи языка с помощью библиотек. Добавление в стандарт дает единообразие. И с этой фичей будет также — либо реализация своего собственного private станет моветоном и закрепится не читаемый синтаксис, либо опять будут изменения в стандарте. К сожалению скорее всего будет первый вариант.

На этот вопрос уже есть ответ в упомянутом в статье FAQ. В Javascript можно задать любому объекту любое свойство. Как вы будете различать публичный obj.x, заданный снаружи и приватный this.x заданный изнутри?


Почему в других языках без этого все нормально работает (включая и интерпретируемые)

Дело не в интепретируемости, а в динамической типизации. Есть у вас пример языка с динамической типизацией и приватными полями?

НЛО прилетело и опубликовало эту надпись здесь
На этот вопрос уже есть ответ

Having a private field named x must not prevent there from being a public field named x, so accessing a private field can't just be a normal lookup.

Ок. Это понятно. Ну получается вопрос то лежит глубже — механизм получения доступа к полю класса не продуман. Информация о типе здесь как бы не причем. К приватному полю снаружи доступа нет, поэтому ничто не мешает писать obj.x и интерпретатор должен вызвать при этом obj.getX() В то время как внутри будет объявлен private x и при написании this.x геттер не должен вызываться. Ну разумеется если позволить по прежнему делать this = obj то получится шлак. Так может над этой проблемой поработать?
Есть у вас пример языка с динамической типизацией и приватными полями?

Хм. Есть один и очень известный (правда я его не сразу вспомнил т к и он мне не нравился никогда) — это PHP
php.net/manual/ru/language.oop5.visibility.php

Ответил здесь. Постарайтесь больше не промахиватся веткой;)

как вы и предлагаете.

Ну я собственно предлагал запретить некоторые действия с this — в частности присвоение ему другого значения руками (т е не через apply а как this=something) Это бы вполне себе дало возможность сделать нормальные геттеры/сеттеры и приватность. Тем более что в некоторых случаях так уже запрещено делать (https://stackoverflow.com/questions/9713323/why-cant-i-assign-a-new-value-to-this-in-a-prototype-function) А вместо этого предлагают костыль. Ну это конечно мое мнение. Вариантов всегда более одного и некоторые хуже по реализации зато быстрее. Вот только я бы не делал такого в столь распространенном и востребованном языке как js. Возможно что сейчас на его проектирование стоит тратить даже больше времени чем на Си++ т к сложность и запутанность уже сравнимая а популярность все растет.
Так может над этой проблемой поработать?

Так над ней уже и поработали, и мы сейчас видим результат – предложение использовать диез в качестве префикса.


Есть один и очень известный — это PHP

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

Приватные поля и не должно быть можно назначить снаружи. Публичные в php вполне себе назначаются
$foo = new EmptyObject();
$foo->bar = '1234';

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

По моему это очень поспешный результат. За отсутствие приватных полей js критиковали чуть ли не со дня его появления… Уж можно было еще лет 5 потратить чтобы выработать какое то более логичное решение.
Так можно делать если не заявлен strict mode. По моему очень не плохое решение.

В качестве своеобразного strict mode у нас есть Typescript. Там приватные поля есть с самого начала.


По моему это очень поспешный результат

Интересно, откуда следует такой вывод. Если в результате нет "приватности через ключевое слово private" – это значит, что недостаточно поработали?

В качестве своеобразного strict mode у нас есть Typescript.

Я его считаю другим языком. Примерно с тем же успехом можно компилировать C# в js.
Если в результате нет «приватности через ключевое слово private» – это значит, что недостаточно поработали?

Приведу первым «стандартный» аргумент — во многих языках так. Почему js должен настолько отличаться? Ну тут можно возразить, что для того и существуют разные языки. На что я отвечу, что js имеет мягко говоря слегка особое положение среди остальных. И хорошо бы, что бы он не был столь экзотическим (исторически сложилось что уже так, но этот разрыв продолжают увеличивать) И отсюда прямо вытекает второй аргумент — если нотация с приват так распространена, так значит видимо она удобна. Иначе бы языки постепенно от нее избавлялись. Ну и третий аргумент — логика. По логике это не дело программиста постоянно напоминать языку что он объявил приватное поле. Это ухудшает читаемость. И использование для этого одного символа лишь кажется хорошей идеей. Следуя этому правилу через некоторое время весь код начнет кишить символами, которые человеческому разуму придется именно дешифровать в отличие от слов, которые он более менее понимает.
На что я отвечу, что js имеет мягко говоря слегка особое положение среди остальных. И хорошо бы, что бы он не был столь экзотическим
Простите, но это аргумент вида «а *ули он вы*бывается?!»
если нотация с приват так распространена, так значит видимо она удобна
Она удобна в языках с классовым ООП, а в JS оно прототипное. И так вам сахар для имитации классов подвезли, так вы всё равно пишете, что «разрыв продолжают увеличивать».
Простите, но это аргумент вида «а *ули он вы*бывается?!»

Не пойму как вы сюда хотите это притянуть? js особый потому что именно этот язык работает по умолчанию во всез браузерах и никакой другой. Для всех остальных языков нужны дополнительные подпорки (написанные на js).
И так вам сахар для имитации классов подвезли, так вы всё равно пишете, что «разрыв продолжают увеличивать».

Ну так зачем «подвезли» то? Плохо с прототипами жилось? А раз уж «подвезли» так оно должно быть сделано правильно что бы потом не было мучительно больно.
Не пойму как вы сюда хотите это притянуть? js особый потому что именно этот язык работает по умолчанию во всез браузерах и никакой другой.
Да, но это не значит, что он чем-то «обязан» другим языкам и должен быть на них похож. Если бы на месте JS был, скажем, Хаскель, вы бы и от него требовали классов?
да никто никому ничего не должен! И паскаль не был должен и кобол тем более. С хаскель сравнивать не корректно. Во первых у него все же своя область. Во вторых он не является «дефолтным» языком в какой либо области.
Так есть своя область, в которой он дефолтный — это повод или не повод что-то требовать от языка?

Спасибо за интересную статью!


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


Применять приватные поля для скрытия данных не выйдет, потому что они легко достаются вот таким кодом (поправьте, если это не так):


class WithPrivate {
   #secretValue = 123;
}

function steal() {
  return this.#secretValue;
}

const test = new WithPrivate();
steal.call(test); // 123

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


Остается еще сценарий грамотной организации кода и инкапсулирования своих собственных классов. Для этого лучше подходит проверка во время сборки (Typescript), чтобы невалидный код даже не имел шанса попасть на сервер. Рантайм-проверка это слишком поздно – ваш код уже попал в браузеры пользователей и работает там с ошибками.

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


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


Это, правда, тоже можно решить, если вы контролируете окружение: тот же паттерн Мембрана и Realms пропозал для этого и придуманы.


P.S.
Function.protoype.toString — тоже можно обмануть заставив его возвращать строку [native code], но пусть это будет задачкой для любопытных;)

С первым аргументом понятно. Может быть, кому-то такой синтаксис зайдет лучше WeakMap в замыкании, хотя, как я понимаю, особых преимуществ у него нет, только дополнительные проблемы с Proxy.


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

Вы, тут упускаете один момент. Да, для приложения, которое просто выкладывается в прод soft private из TS вполне достаточно, а вот для библиотеки — мало.


Hard private нужен как раз разработчикам библиотек — например Joyee, один из мейнтенеров node.js, приводил примеры, когда отсутствие нормальной инкапсуляции приводило к серьёзным проблемам.


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

НЛО прилетело и опубликовало эту надпись здесь

Мне кажется, суть в основном в том что бы, семантический обозначить какие поля приватные и уберечь от "случайных" использований. В других языках (java) можно вытащить приватные значения через relection. Если есть очень большое желание — всегда можно вытащить

Поскольку "#" является синтаксическим сахаром над тем же WeakMap, очевидно что указанный вами способ не сработает.
Чтобы было нагляднее, запишем аналог приведённого вами кода, заменив новый синтаксис явным использованием WeakMap:
const WithPrivate = ( function (){ 
    const privates = new WeakMap(); 
    return class { 
        constructor(){ 
            privates.set( this, {} ); 
            privates.get( this ).secretValue = 123; 
        } 
    } 
}) ( );

function steal (){ 
    return privates.get(this).secretValue; 
}

const test = new WithPrivate();
steal.call(test); // ReferenceError: privates is not defined

Соответственно, чтобы получить доступ к secretValue, нужны две составляющие: ссылка на this, и доступ к WeakMap privates. Последний недоступен функции steal, т.к. скрыт в замыкании.

Ответ на комментарий пользователя Arlekcangp, который опять промахнулся веткой ¯\_(ツ)_/¯


Как, я уже говорил ранее, вот здесь я предоставил рабочий пример использования private синтаксиса, но и у него есть проблема — ключевое слово this получает ещё одно усложнение в понимании (а оно и так непростое) т.к. в зависимости от лексического контекста обладает разным смыслом, как вы и предлагаете.


Поэтому эта идея развилась в более непротиворечивый синтаксический сахар для всё того же Symbol.private стоящего в основе этой альтернативы.


В любом случае, скорее всего, ключевое слово private будет жить только в TypeScript, а в ES сможет появиться только вместе с аннотациями типов.


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

А зачем вообще убиваться по поводу доступности приватных полей извне? В большинстве языков это сделать можно и никто не жалуется. Безопасность на такой сомнительной вещи строить не получится. Так зачем усложнять язык? По мне текущего де-факто стандарта «this._x” более чем достаточно.


Возможность залезть в кишки объекта это большое благо на самом деле. Например чтобы починить баг в библиотеке, бывает в тестах сильно проще проверить приватную переменную чем городить что-то вокруг и т.п. Я не призываю лезть в внутрь всех объектов, но эта фича имеет своё применение.


А инкапсуляция она от private не сильно зависит. У вас либо понятный поток данных и управления в системе или каша. А в каше существенная часть приватного состояния все равно будет косвенно видно в деталях контракта, большом количестве методов, сложном состоянии, о котором надо знать клиентам и т.п.

Полностью поддерживаю. Фронтенд полнится всевозможными библиотеками самого разного уровня качества. И часто действительно приходится самому их патчить – потому что либо исправление их автором либо займёт слишком много времени, а вам надо вотпрямщас; либо вы даже сами пуллреквест сделали, но их там уже 50 штук висит и автору принять их некогда; либо репозиторий вообще заброшен, а форк сделать не вариант, потому что эта библиотека находится в зависимостях ещё нескольких используемых вами пакетов… и так далее.

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

#js_is_public

Собственно, была уже тьма дисскусий на тему hard private vs soft private.


Авторы популярных фреймворков/библиотек в большинстве своё выступают за hard private потому что это позволяет им четко отделить публичный контракт от деталей имплементации и потом спокойно проводить рефакторинги, делать новые фичи и, в целом, развивать свои продукты, не боясь сломать проекты, которые зависят от деталей имплементации, потому что таких просто не будет при хорошо инкапсулированом коде.

Это все попытка выдать желаемое за действительное. Если все хорошо сделано, то пользователю и не нужно лезть в кишки, а если все плохо, то любой значимый рефакторинг сломает контракт. И затем, найдите мне программиста на JS, который не в курсе, что если меняешь/читаешь что-то, что начинается с «_», то можно огрести? Все уже и так все знают, в от идиотов защититься невозможно.


Самые частые проблемы с контрактом выглядят не «переименовало приватную переменную», а как «был метод O(1), стал O(n^2)”, не было исключения, теперь есть, раньше можно было метод дергать до «onload”, а теперь нельзя и т.п. Если сообщество обеспокоено контрактом, то нужно говорить не о private, а о полноценной ala Eifell системе. Наличие hard private автору библиотеки даст ложное спокойствие, а юзеру даст вполне конкретный геморрой. Я не вижу тут реальной выгоды ни для авторов ни для юзеров.

В кишки могут полезть не из-за того, что сделано плохо, а потому что разработчик плохо понимает ограничения библиотеки. Для примера:


class A {
    _x;
    get() { return this._x; }
    set(val) {
        const result = (Array.isArray(val))
            ? val
            : [val];
        result.forEach(doSomeSideEffect);

        return this._x = result;
    }

    method() {
        const transformedX = this._x.map(doImportantTransformation);
        doSomething(transformedX);
    }
}

doSomeSideEffect — делает что-то важное, но на первый взгляд незаметное (например проставляет каких-нибудь слушателей), и влияет на скорость выполнения.


Конечный пользователь решает проставить _x напрямую, не замечает что сайд-эффекта нет (в этот момент он ему не нужен), а скорость выполнения возрасла — "УРА, какой я молодец" думает он и переходит к другим задачам.


Автор библиотеки понимаеть, что он никогда не использует _x без применения к нему doImportantTransformation, а все другие методы (например, doSomething) расчитывают на вход получить уже трансформаированный массив, и решает сделать трансформацию только один раз, что ускорит работу его либы.


class A {
    _x;
    get() { return this._x; }
    set(val) {
        const result = (Array.isArray(val))
            ? val
            : [val];
        result.forEach(doSomeSideEffect);

        return this._x = result.map(doImportantTransformation);;
    }

    method() {
        doSomething(this._x);
    }
}

На первый взгляд автору кажется, что это даже не минорный апдейт либы, а просто патч. Но для пользователя, который использовал _x напрямую это breaking change, который ещё и не так просто найти т.к. ошибка возникает не в месте использования _x, а там где вызываеться method.


Не все авторы библиотек, готовы говорить своим пользователям, что раз вы юзали свойства с префиксом, то идите лесом.

Почему же не готовы? В Java, .NET, С и C++ и в других языках готовы, и прямо говорят, «если вы меняете приватные переменные, то идите лесом». В JS сейчас чуть проще, чем в перечисленных языках поменять приватные переменные, но что с того? Авторы говорят обычно даже более сильную вещь — «используете недокументированные методы и функции — идите лесом». В вашем примере "_x" де факто приватная переменная, «де-юре» недокументированная функция. В любом случае юзер идет лесом и это ожидаемо.

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


Кстати, приводился пример какого-то тестового фреймворка из Java, для которого существуют популярные плагины, которые используют приватное состояние через рефлексию, что в итоге существенно замедлило развитие самого фреймворка. (в деталях мог ошибиться, т.к. описывал по памяти, но суть думаю ясна)

На первый взгляд складывается впечатление, что комитет поставил своей задачей не решить конкретную проблему, а насолить, например, typescript (ну, или babel), и показать «кто тут главный», мол, сделаем как угодно, но только не так как у вас там принято. По-моему приватные переменные как таковые в js особо не нужны (поправьте меня, если не прав), а защиту на этапе разработки можно обеспечить любым статическим анализатором (typescript, coffeescript, dart — кому что нравится)
Со вторым аргументом согласен, от приватных переменных в рантайме пользы немного.

А вот первый – это какая-то теория заговора. Там выше уже разбирались причины почему сделано именно так, а вы все туда же.
Я это понимаю. Поэтому и говорю, что «на первый взгляд».

Define для полей вообще зачем? В этом нет смысла!

Ответ довольно прост: [[Define]] семантика сделает декораторы для полей класса более предсказуемыми.

В целом, до тех пор пока brand-check нужно делать явно, это не проблема — разработчик просто осознанно должен решить какой trade-off он совершает и нужен ли он ему, более того в случае явного brand-check можно его реализовать таким образом, что бы ошибка не выбрасывалась на довереных прокси.

К сожалению, текущий пропозал лишает нас этой гибкости:

class A {
    #priv;

    method() {
        this.#priv; // в этой точке brand-check происходит ВСЕГДА
    }
}


Такой method всегда будет выбрасывать исключение, если вызван не в контексте объекта построенного с помощью конструктора A

Что мешает сделать так:

class A { 
    #secret = 42;
    __self = this;
    getSecret(){return this.__self.#secret;} 
    setSecret(val){this.__self.#secret = val} 
}

const a = new A();
const proxy = new Proxy(a, {
    get(target, p, receiver) {
        const property = Reflect.get(target, p, receiver);
        return (typeof property === 'function')
            ? property.bind(proxy)
            : property;
    }
});

a.getSecret(); //42
proxy.getSecret(); //42
proxy.setSecret(43);

a.getSecret(); //43
proxy.getSecret(); //43

Проверил в Chrome Canary. Вроде всё работает.
Либо так:

const A = ( function (){
const __self = Symbol("self");
return class { 
    #secret = 42;
    constructor(){
        this[__self] = this;
    }
    getSecret(){return this[__self].#secret;} 
    setSecret(val){this[__self].#secret = val} 
}
})();

Если хотим скрыть __self, и защититься от его модификации извне.

Мешает то, что прокси, которую вы взяли из примера в статье, была всего лишь демонстрацией проблемы. Ваш пример с реальной прозрачной прокси работать не будет, потому что:


  1. Доступ к __self тоже дернёт get trap, который обернёт результат, а по скольку ссылка на this уже имеет соответствующую ей прокси (в примере из статьи этого нет, но можно глянуть в моём комментарии здесь), то она и будет возвращена, а значит методы будут вызываться в контексте прокси и бренд-чек будет выкидывать ошибку.
  2. Допустим наша реализация не обарачивает __self проперти, тогда исключение не будет выброшенно, но методы/геттеры/сеттеры вызванные после __self будут вызваны в контексте __self, а нам нужно, что бы они вызывались в контексте прокси.
  3. const __self = Symbol("self"); не защищает от модификации — Object.getOwnPropertySymbols позволяет извлечь любой символ из объекта.
Допустим наша реализация не обарачивает __self проперти, тогда исключение не будет выброшенно, но методы/геттеры/сеттеры вызванные после __self будут вызваны в контексте __self, а нам нужно, что бы они вызывались в контексте прокси.

Вообще это интересный момент. Как выполнять методы, обращающиеся к полям, которых в текущем контексте просто не существует? Дело ведь даже не в brand-checking. Ну допустим не будет явной проверки. Но ведь приватных полей, имеющихся в оригинальном объекте, у прокси просто нет. И инициализировать он их не сможет, т.к. не имеет к ним доступа, и не знает, какие у них в данный момент значения, чтобы скопировать их себе. А после не сможет записать полученное состояние в оригинальный объект, т.к. опять же, доступа не имеет.
Получается, проблема не столько в конкретной реализации приватных полей, сколько в невозможности обойти инкапсуляцию. Такая возможность конечно должны быть. И как правило, в других языках она есть. Если рассмотреть тот же C#, то там можно без проблем, как прочитать, так и записать приватное поле. Через рефлексию. Возможно и в JS стоит добавить новый метод в Reflect, позволяющий получить доступ к скрытому WeakMap, в котором хранятся приватные поля. Что-то вроде Reflect.GetPrivate(obj);

Symbol.private предлагает решение и оно довольно простое.
У прокси действительно нет приватных полей оригинального объекта, но:


  1. Для каждой прокси всегда известен его таргет (и рантайм его знает)
  2. Доступ к полю используя что Symbol.private, что #priv отличается от доступа к публичному полю.

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


Что-то вроде Reflect.GetPrivate(obj);

Это нарушает концепцию hard private, коммитет подобные escape hatch не будет даже рассматривать.

НЛО прилетело и опубликовало эту надпись здесь
Я сначала тоже думал возразить, начал писать, что в других языках, рефлексия именно так и работает. Потом нашёл обсуждение на GitHub, где в принципе все эти аргументы, были сказаны и разжёваны.
Лично я, почитав аргументы, так и не понял, в чём смысл такой жёсткости. Аргументы противников мне показались сильнее. Единственный весомый аргумент, что я увидел, это то что, было бы хорошо, если бы такая возможность была. Именно как возможность, с учётом того, что есть и другие возможности, реализовать иные типы приватности, я могу это принять. Правда не совсем понятно, зачем тут отдельный синтаксис. Для редких случаев, когда такая приватность действительно понадобилась, можно и явно WeakMap использовать.
Но тут возникает проблема, исключения вылетают вовсе не при попытке обратиться к приватным полям напрямую, а при вызове публичных методов. И внешнему коду не понятно, какие методы можно вызывать из другого контекста, а какие нет. Вот в этом плане, однозначно, надо что-то улучшать. Хотя бы добавить возможность проверки, можно ли с данным методом делать call, apply и bind.
Это приводит к простому решению, если код пытается осуществить доступ к приватному полю на прокси то вместо обращения к прокси, он сразу обращаеться к таргету этого прокси.

В принципе, в своём примере, я именно к этому и стремился. Вот только с «решёточным» синтаксисом, класс должен быть намеренно написан с расчётом на прокси: объявлять __self, и обращаться к приватным полям через него. И Proxy должен сделать исключение для __self.
Это нарушает концепцию hard private, коммитет подобные escape hatch не будет даже рассматривать.

Ок, если основной целью добавления «решёточного» синтаксиса была именно жёсткая приватность, и это было сделано осознанно, то я могу сказать только одно. Пусть будет. Но использовать её следует лишь тогда, когда это действительно необходимо. Во всех остальных случаях, можно просто использовать символы. Либо, как промежуточный вариант, явное объявление WeakMap — тут и жёсткая приватность, и возможность объявить некоторые объекты «друзьями», на которых эта приватность не распространяется. Главное, чего не надо делать, так это позиционировать приватные поля с решёткой, как главный и единственный способ инкапсуляции. Хорошо бы ещё был способ заранее различать методы, которые можно вызывать с другим this, и которые нельзя.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации