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

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

А через модули не проще разве? можно экспортнуть только то, что надо, остальное будет закрыто.
как вам модули помогут ограничить доступ к полям классов?
ну вынести эти поля в константу и не эскспортировать её, а в классе работать через геттер и сеттер

Во-первых, интересно представить, как вы себе представляете сеттер для константы. Ну это ладно, допустим, вы имели в виду переменную. Но что делать с тем, что у класса может быть не один экземпляр?

Вероятно, имелось в виду, что можно вместо классов использовать фабрики объектов. И публичные поля и методы пихать в объект, а приватные оставлять в замыкании. Ну или все поля / методы задавать в конструкторе, ничего не оставляя в прототипе. Хотя это все дичь конечно =)
Все приватные выносить в соседний объект и ссылку хранить в замыкании. Основной вопрос — зачем весь этот цирк если дебаггером всё равно можно остановиться внутри функции объекта и изучить\модифицировать всё приватное.
На backend как это сделаете?
Если есть доступ к коду — то так же легко подключается.
Если нету доступа, то какая разница — приват там или паблик
Так нет у Вас доступа, если вы не разработчик, а если есть — то вы ковыряете просто front-end. А для чего на фронте делать приваты???
А для чего на беке делать приваты? Если доступа нет — я и паблики не посмотрю. А если доступ есть — приваты мне не помешают.
Так от чего они защитят? От вашего чинарика в зубах?
Что?
Я к тому, что я не обязан был 10 лет писать приватные поля в JS потому что хватало и замыканий; и теперь не обязан слушать людей, которые утверждают, что это критически нужно для дисциплины и понятности API класса. Всю жизнь все объекты DOM в JS были таки нашпигованы кучей оснастки и расконсоливание об этом прекрасно рассказывает. На сервере, кстати, гораздо проще объекты и сами классы поэтому, я по привычке даже не задумаюсь об этом. Минусуете, как будто 300 лет в лесу жили.
Минусуют, потому что вы речь построить не можете.
НЛО прилетело и опубликовало эту надпись здесь
У вас какие-то претензии к моему профилю?

Приватные поля не для защиты от просмотра их разработчиком… это не права доступа на файлы. Приватные поля нужны для реализации инкапсуляции, которая служит для таких целей:


  • ускорение понимания кода другим разработчиком (ему не надо смотреть на приватные поля и методы или читать весь код, чтобы понять, как использовать класс, а достаточно пробежаться взглядом по заголовкам публичных методов)
  • возможность автору класса обеспечить гарантии. Например, гарантию, что в определенное ситуации класс ведет себя определенным образом. Если поле публичное, в него можно снаружи записать что угодно, и гарантировать корректную работу класса нельзя. А вот если приватное — то доступ к нему возможен только через методы, которые не позволят туда записать что-то некорректное. Условно говоря, если у вас есть объект, представляющий дату, то вы не сможете в нем сделать 32 февраля. А в примере счетчика выше — с приватным полем автор может гарантировать, что счетчик нельзя "открутить" назад.

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

Эмс. Вы веткой ошиблись? Я как раз и старался объяснить человеку, что «приват» модификатор никак не защищает данные. Более того, с разделением «на сервере защищает, а на клиенте — нет». Это вообще не имеет никакой логики. Я задавал наводящие вопросы и надеялся, что человек дойдет до этого.

Жаль, что вы сразу бросились в оскорбления не разобравшись в ситуации.

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

И да, то, что модификатор «приват» НУЖЕН для реализации инкапсуляции — поверхностное мнение от попсового (упрощенного) определения инкапсуляции.
да, переменную имел ввиду, по остальному уже особенности архитектуры приложения,
где то конечно и не прокатит такой метод.

Есть такой старый добрый паттерн "Модуль": сохраняем приватные значения в замыкании, возвращая наружу только публичное API.


Что-то похожее можно сделать и на современных ES-модулях

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

А есть подтверждение этой информации?


Например, я вижу, что Redux использует как раз замыкание для своего createStore. Сокращенно код выглядит как-то так.


function createStore() {
   let currentState = ...;
   function getState() {
     return currentState;
   }
   function dispatch() {
   }
   function subscribe() {
   }

   return {
     dispatch,
     subscribe,
     getState
   };
}

Библиотека используется много где, и в React и Angular проектах, так что если бы здесь были заметные проблемы с производительностью, то это бы давно заметили.

стор обычно создаётся один раз в приложении, а есть объекты, которые создаются тысячами

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

А современные ES-модули внутри не устроены так же, как этот костыль? С классами, помнится, именно так.

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

Больше всего, конечно, смущает синтаксис с решетками. Потому что ломается совместимость с TypeScript, в котором используются ключевые слова private / public / protected.


Да и в самом JavaScript эти слова зарезервированы. Правда теперь уже получается, что зря зарезервированы.

Возможно еще в будущем добавят их в качестве синонимов.

"Так не доставайся ж ты никому!" — весьма распространенное явление, когда в деле замешаны всяческие комитеты.


Часто бывает нужно не private, а internal (видимость на уровне модуля или NPM-пакета). И теперь приходится для private использовать #, для internalSymbol и computed properties.


А можно просто настроить минификатор, чтобы он манглил все свойтсва объектов, начинающиеся с нижнего подчеркивания, как делали и раньше, и не насиловать себе мозг.


Имхо, они приняли поспешное решение. И теперь нам всем придется с этим жить.

Да, ситуация несколько досадная. Учитывая, что часто JavaScript — не единственный язык, на котором приходится писать… и в нем все больше и больше специфики.


Возможно, многие читали этот issue о кейворде: там довольно много обсуждения и недовольств.


Например, мол сложно внедрять

Think of what you might need to do to make that work.


  • You'd have to somehow encode a "private key" into each lexical environment.
  • You'd have to change the semantics of each and every property access (because any property access might result in a private field). Engines are highly optimized around the current property lookup semantics.
  • Would for-in enumerate over these properties?
  • Can someone shadow a private property with a normal property lower on the prototype chain? What about the reverse?
  • How do you prevent leaking the names of private fields to clients that shouldn't know that information? This is probably a fatal information leak.

Кстати, а что будет, если я добавлю новый метод в прототип класса (да, monkey patching)? И попытаюсь прочитать приватное поле:


class Test {
   #foo = 123;
}
Test.prototype.getFoo = function() {
  return this.#foo;
}

Не сработает. Можно самому в этом убедиться, если включить в хроме флаг "Experimental JavaScript" и попытаться запустить этот код. При попытке вызова getFoo выскочит ошибка


Uncaught SyntaxError: Undefined private field #foo: must be declared in an enclosing class
Тоже об этом подумал ) Но синтаксис приватных полей класса работает только внутри тела определения класса.

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

What do you mean by "encapsulation" / "hard private"?
It means that private fields are purely internal: no JS code outside of a class can detect or affect the existence, name, or value of any private field of instances of said class without directly inspecting the class's source, unless the class chooses to reveal them. (This includes subclasses and superclasses.)
This means that reflection methods like getOwnPropertySymbols must not reveal private fields.

Ох уж эти комитетчики. Одни теоретики. Как они представляют себе работу persistance/hydration с таким подходом? Даже пусть не ORM, а банальную сериализацию? А, кстати, что будет видеть JSON.stringify? :-)

Мне кажется для TypeScript это не смертельно. Ведь они не дождались нормальной реализации стандарта и начали нашпиговывать язык своими, как правило не очень то и критичными фичами.
А private — критическая фича? Вон в питоне как-то живут без нее. И хорошо живут. Все это нужно только для самодисциплины разработчиков.

Вот декораторы — это существенное увеличение выразительности языка. Но они зависли в недрах комитета на долгие годы. И неизвестно, вылезут ли из stage-2.

А есть еще такая крутая штука как pipeline operator (тоже несколько лет в stage-1). С ней работа в функциональном стиле упростилась бы в разы. Но без статической типизации она превращается в тыкву. А команда TypeScript, обжегшись на взаимодействии с комитетом по поводу декораторов, отказалась реализовывать этот оператор =(
Я с вами согласен. Я на PHP тоже не использую private ибо толку от него ноль. Только видимость защищенности данных.

Да я тут не на private нападаю, а на тормозной комитет.

Языку со времён ES5 не хватало только возможности заводить типизированные переменные в целях увеличения производительности, эдакое развитие в сторону ActionScript — сейчас скорость бы уже сравнялась с нативными языками, но нет, теперь у нас есть особые классы, куча синтаксиса написания функций, замыкания на уровне блоков чтоб GC подавился. Всё кроме переменных можно было решить транспайлерами, даже перегрузку операторов которую давно жду.
Вот декораторы — это существенное увеличение выразительности языка. Но они зависли в недрах комитета на долгие годы.
Поддерживаю.
Лично я так и не смог понять, почему приватные поля были так резво приняты (за полтора года до stage 3 плюс реализация в V8; не удивлюсь, если они войдут в ES10), в то время как декораторы третий год не могут довести хотя бы такого же состояния (а за многие другие вещи и не брался никто).

А команда TypeScript, обжегшись на взаимодействии с комитетом по поводу декораторов
Вот тут я, видимо, что-то пропустил. Можно поподробнее что за драматическая история случилась, пока я отходил? :)
TypeScript является суперсетом ES/JS, то есть они поддержат изменение github.com/Microsoft/TypeScript/issues/9950, но когда придет время. Обычно TS вводит поддержку новых штуковин начиная со stage 3. Но видимо в этот раз они не спешат ввиду притиворечивости текущего состояния стандарта:
github.com/tc39/proposal-class-fields/issues/100
github.com/tc39/proposal-class-fields/issues/144
github.com/tc39/proposal-class-fields/issues/177
github.com/tc39/proposal-class-fields/issues/203
— и тд
Тоже грустно без private / public / protected. С решеткой какой-то маня-монёвр уровня IE

Главное что не так как у всех

Сейчас мы используем префикс подчеркивания, чтобы обозначить, что _count не должен использоваться напрямую вне класса, но это просто соглашение;
Ну и что? В Джавах и Шарпах тоже можно поменять и прочитать приватное свойство через рефлексию и его не читают «потому-что соглашение». И читают, когда в этом действительно есть необходимость
И читают, когда в этом действительно есть необходимость

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

Обращение Object.getOwnPropertyDescriptor(o, 'prop').value (собственно рефлексия) действительно медленное.
Обращение o[prop] также не очень быстрое (хотя уже значительно быстрее будет).

А вот обращение o.prop успешно подхватится inline cache'ами и будет выполняться как прямое обращение по адресу (смещению) в памяти уже со второго-третьего раза — и по крайней мере до тех пор, пока переменная o не станет постоянно менять тип в рантайме.

Обращение же o.#prop создаёт фиксированный биндинг ещё на этапе компиляции кода и не предполагает выполнения никакого property lookup'а, так что резонно предложоить, что такое обращение всегда будет выполняться как простое обращение по фиксированному адресу в памяти — т. е. плюс-минус как классический struct по скорости.
Тут вопрос в количестве усилий, которые нужно приложить для обращения к приватному свойству. Обращение через механизмы рефлексии это всё же отдельная история.

Если бы для обращения к приватному свойству в JS нужно было сделать что-то подобное, то проблемы бы, пожалуй, и не было:
let x = Object.getPrivatePropertyDescriptor(instance, 'x').value;


Даже решение в Python, при всей своей простоте, очень неплохое. Придётся сделать что-то вроде подобное (да поправят меня питонисты, если можно проще — хотя не думаю, что ощутимо):
 x = instance.__dict__.get('_{0}__{1}'.format(Class.__name__, 'x'))


Понятно, что подобные конструкции:
1) сложно написать непреднамеренно;
2) требуют определённого навыка (с которым обычно приходить понимание, когда так писать разумно, а когда нет);
3) бросаются в глаза и вызывают вопросы к автору в production-коде.

В JS однако, для обращения к приватному свойству, не нужно прилагать вообще никаких усилий.
Вывели объект в консоль — там все свойства как на ладони, в том числе и приватные. Дописали в код строчку и пошли дальше.
let value = o._x;


В сочетании же с возможностями динамически строить имена свойств и итерирования по всем свойствам объекта (через for – in) становится очень просто обратиться к приватному свойству даже ненамеренно, и очень сложно заметить подобное обращение в существующем коде (например, при review).

Мне одному кажется странным использование префикса вместо зарезервированного слова, как это сделано во всех остальных нормальных языках?

достаточно почитать комментарии выше, чтобы убедиться, что нет, не одному.

Где-то и когда-то развитие языка свернуло явно не туда (

Я себе это представляю как-то так:


TC39: дорогие разработчики, какие новые фичи языка вы хотите?
Сообщество: приватные поля! Больше строгости в наш Javascript!
TC39: тут такое дело… из-за обратной совместимости и других загвоздок приватные поля должны обладать специальными именами, модификатор private сделать не получится.
Сообщество: ну и не очень-то нам нужно такое решение


Интересно, как в этой ситуации выкрутится Typescript, у которого интеграция с ЕS-приватными полями уже значится в планах на будущее

TS может сказать, что у нас будет отдельный модификатор для приватного поля, но мы поддерживаем и синтаксис js, однако рекомендуем использовать нормальный подход, который предоставляем мы. Я бы использовал их вариант.
В динамическом языке сложновато придумать, как добавить модификатор приват, т.к. информации про свойство как в статических языках, по сути, нету.
Модификатор добавить легко, сложнее придумать ту напасть от которой нам нужно будет изолироваться с его помощью. Единственный кейс который я вижу — коммерческие библиотеки собранные в бинари, но и тут для «защиты» сейчас имеется ровно столько же инструментов.

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

Заменяете приватное свойство соглашением. Так уже давно используют нижнее подчеркивание. Мало того, что оно решает проблему (используешь приватные свойства — ССЗБ, разработчик кода не гарантирует совместимость). Так ещё и помогают, когда нужно поменять что-то в сторонней библиотеке, а нормальной возможности это сделать нет. Тогда на свой страх и риск. Но зато возможность есть, а это лучше ее отсутствия.

Вы явно объявляете свойство при объявлении класса. Не совсем понимаю, какие тут проблемы могут быть, если вы явно свойство объявили. Я так же не понимаю, чем объявление приватного свойства с префиксом с точки зрения реализации концептуально отличается от объявления с ключевым словом.

Нет, настолько не одному, что разработчики proposal даже сделали FAQ на предмет того, почему они остановились именно на таком синтаксисе.
Так и не смог понять зачем вообще нужно делать приватные поля кроме как разве для работы ide, если ты работаешь с кодом как с либой, то кроме как, то что есть в документации ты нечего как бы и не сможешь использовать. А если приходится лезть внутрь кода то какая разница, тебе и так и так придется разбираться в нем, да мб легче будет разобраться в коде, но если разраб не пишет коменты или доки, то он так же может и приватными поля не делать.
Еще бывает нужно для ABI при динамической линковке. В смысле, чтобы при компиляции *.dll / *.so / whatever не экспортировать метаданные о приватных полях. Но для этого нужна компиляция хотя бы в байт-код. Таким образом вы правы — для JavaScript это не нужно =)

А зачем к примеру в C++ есть ключевое слово volatile. Чтобы уменьшить вероятность что что-то пойдет не так.

Не у всего есть документация, как минимум.
А если так?
class MyClass {
  constructor() {
    let _count = 42
    Object.defineProperty(this, 'count', {
      get: function() {
        console.log('Getting private!')
        return _count
      }
    })

    Object.defineProperty(this, 'increment', {
      value: function() {
        _count++
      },
      writable: false
    })
  }

}

const test = new MyClass()
test.increment()
console.log('Result: ', test.count) // 43
console.log('Give me _count!', test._count) // undefined
Та же вариация приватности через замыкание, только с дескрипторами зачем-то.
При таком подходе все методы, которые работают с приватными свойствами, придётся описывать внутри конструктора, что (1) приведёт к его разрастанию и снижению читаемости и (2) скажется на производительности, т. к. (2a) каждый инстанс будет создавать в памяти объекты методов под себя и (2b) из-за того, что у инстансов фактически будут разные методы, по ним будет некорректная статистика профилирования, и они не будут эффективно оптимизированы движком (перекомпилированы оптимизирующим компилятором как hotspot).
А будут ли доступны приватные поля в классах-наследниках?
Нет. На то они и приватные, а не protected.
Ага, я так и подозревал. А protected у нас не намечается, не в курсе?
Уже кто-то шутил про собачку для protected полей :)
Пожалуйста: Consider `protected`, `abstract`, and `friend`. :)
Хотя это всего лишь issue, доберётся ли предложение когда-нибудь до формального proposal, непонятно.
Некорректно вставил ссылку, вот, если кому интересно.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории