Comments 78
Во-первых, интересно представить, как вы себе представляете сеттер для константы. Ну это ладно, допустим, вы имели в виду переменную. Но что делать с тем, что у класса может быть не один экземпляр?
Если нету доступа, то какая разница — приват там или паблик
Приватные поля не для защиты от просмотра их разработчиком… это не права доступа на файлы. Приватные поля нужны для реализации инкапсуляции, которая служит для таких целей:
- ускорение понимания кода другим разработчиком (ему не надо смотреть на приватные поля и методы или читать весь код, чтобы понять, как использовать класс, а достаточно пробежаться взглядом по заголовкам публичных методов)
- возможность автору класса обеспечить гарантии. Например, гарантию, что в определенное ситуации класс ведет себя определенным образом. Если поле публичное, в него можно снаружи записать что угодно, и гарантировать корректную работу класса нельзя. А вот если приватное — то доступ к нему возможен только через методы, которые не позволят туда записать что-то некорректное. Условно говоря, если у вас есть объект, представляющий дату, то вы не сможете в нем сделать 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-модулей. Имеет смысл применять только, если требуется обеспечить поддержку большого числа браузеров.
Больше всего, конечно, смущает синтаксис с решетками. Потому что ломается совместимость с TypeScript, в котором используются ключевые слова private
/ public
/ protected
.
Да и в самом JavaScript эти слова зарезервированы. Правда теперь уже получается, что зря зарезервированы.
Возможно еще в будущем добавят их в качестве синонимов.
Это просто синтаксис меньшего зла ) комментарий по этому поводу
"Так не доставайся ж ты никому!" — весьма распространенное явление, когда в деле замешаны всяческие комитеты.
Часто бывает нужно не private, а internal (видимость на уровне модуля или NPM-пакета). И теперь приходится для private использовать #, для internal — Symbol и 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? :-)
Вот декораторы — это существенное увеличение выразительности языка. Но они зависли в недрах комитета на долгие годы. И неизвестно, вылезут ли из stage-2.
А есть еще такая крутая штука как pipeline operator (тоже несколько лет в stage-1). С ней работа в функциональном стиле упростилась бы в разы. Но без статической типизации она превращается в тыкву. А команда TypeScript, обжегшись на взаимодействии с комитетом по поводу декораторов, отказалась реализовывать этот оператор =(
Вот декораторы — это существенное увеличение выразительности языка. Но они зависли в недрах комитета на долгие годы.Поддерживаю.
Лично я так и не смог понять, почему приватные поля были так резво приняты (за полтора года до stage 3 плюс реализация в V8; не удивлюсь, если они войдут в ES10), в то время как декораторы третий год не могут довести хотя бы такого же состояния (а за многие другие вещи и не брался никто).
А команда TypeScript, обжегшись на взаимодействии с комитетом по поводу декораторовВот тут я, видимо, что-то пропустил. Можно поподробнее что за драматическая история случилась, пока я отходил? :)
— 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
— и тд
Главное что не так как у всех
Сейчас мы используем префикс подчеркивания, чтобы обозначить, что _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-приватными полями уже значится в планах на будущее
Приватные поля придуманы не для защиты коммерческих секретов, а для инкапсуляции, которая упрощает понимание кода и повышает его качество и надежность.
Заменяете приватное свойство соглашением. Так уже давно используют нижнее подчеркивание. Мало того, что оно решает проблему (используешь приватные свойства — ССЗБ, разработчик кода не гарантирует совместимость). Так ещё и помогают, когда нужно поменять что-то в сторонней библиотеке, а нормальной возможности это сделать нет. Тогда на свой страх и риск. Но зато возможность есть, а это лучше ее отсутствия.
Вы явно объявляете свойство при объявлении класса. Не совсем понимаю, какие тут проблемы могут быть, если вы явно свойство объявили. Я так же не понимаю, чем объявление приватного свойства с префиксом с точки зрения реализации концептуально отличается от объявления с ключевым словом.
А зачем к примеру в C++ есть ключевое слово volatile. Чтобы уменьшить вероятность что что-то пойдет не так.
К слову, сейчас это предложение находится на 3 стадии принятия в стандарт.
Опробовать в других браузерах можно с помощью бабеля, используя либо плагин @babel/plugin-proposal-private-methods
, либо пресет stage-3
.
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
Хотя это всего лишь issue, доберётся ли предложение когда-нибудь до формального proposal, непонятно.
JavaScript: Публичные и приватные поля классов