Как можно создать в классе (ecma6 class) приватную переменную?
4 Answers
Для приватных свойств надо использовать замыкания и символы.
const Animal = (function () {
const privatePropSymbol = Symbol("privateProp");
class Animal {
// гетеры и сеттеры для свойства
get PrivateProp() { return this[privatePropSymbol]; }
set PrivateProp(value) { this[privatePropSymbol] = value }
}
Animal.prototype[privatePropSymbol] = "default value";
return Animal;
})();
Фокус в том, что два символа с одинаковым названием остаются двумя разными символами - поэтому не имея переменной privatePropSymbol внешний код не сможет получить к нему доступ.
- 58,537
В es6 такой возможности нет. Многие думают, что классы es6 это какая-то новая конструкция или реализация , нет. Это обычный , новый синтаксис для старого доброго прототипного наследования.
class Animal {
constructor() {
this.name = "dog"
}
say() { alert("gaf") }
}
То же самое, что и
function Animal() {
this.name = "dog"
}
Animal.prototype.say = function () { alert("gaf") }
Поэтому приватных методов в классе не реализовано, вам придется решать эту проблему самому. Как? Например, через замыкание
const Animal = function () {
let privateProp = "i am private";
class Animal {
constructor() {
this.name = "dog";
}
// гетеры и сеттеры для свойства
get PrivateProp() { return privateProp }
set PrivateProp(value) { privateProp = value }
}
return new Animal();
};
let dog = new Animal();
dog.privateProp; // приватное свойство
dog.privateProp = "new private prop"; // меняем приватное свойство
Тут методы get/set просто для примера, в реальности скорее всего их не будет, если уже переменная приватная. Тут get/set единственные способы достучатся до privateProp, больше никак, уберете get/set privateProp станет настоящим приватным способом.
UPD
Чуть ошибся, переменная создавалась бы одна для всех экземпляров, как написали в комментарии, поправил ответ.
Теперь всегда будет создаваться свой экземпляр приватной переменной
- 12,261
-
1Ну нельзя же так делать! У вас теперь
privatePropне только приватная, но и статическая... – Pavel Mayorov Apr 24 '16 at 07:11 -
-
@Юрий, в первом варианте создавалась одна переменная для всех экземпляров. Поэтому если мы где то поменяли бы ее, то она поменялась бы для всех экземпляров класса. – ThisMan Apr 24 '16 at 08:26
-
-
1@Юрий, нет, в первом варианте Animal сам был ф-цие конструктором, сейчас же эта функция, которая вызывает ф-цию конструктор) Поэтому при каждом вызове создается новое замыкание с переменной
privateProp– ThisMan Apr 24 '16 at 08:34 -
-
@ThisMan а как можно в такой реализации проверить, что (dog instanceof Animal)? Просто так почему-то не работает – Юрий Apr 24 '16 at 09:01
-
@Юрий В такой реализации - нельзя. Потому что тут
Animal- не конструктор, а обычная функция. – Pavel Mayorov Apr 24 '16 at 09:58 -
ThisMan, теперь у вас создается по новому классу на каждый вызов "конструктора". Зачем в такой схеме вообще нужны классы? – Pavel Mayorov Apr 24 '16 at 09:59
-
-
-
-
Что за бред? Даже переменные созданные в конструкторе вины лишь в области его видимости, и не видны остальным методам, надо свойства класса хранить в
thisи только вthis, давая лишь уникальные имена, для этого и придумалиSymbolну илиMath.random()как полифилл – Maxmaxmaximus Jul 18 '16 at 06:02 -
3Раскладываем запас костылей на будущее? Даже если забить на производительность, то за что какому-то бедному программисту должно достаться
dog instanceof Animal= false? – Qwertiy Jan 30 '17 at 12:01
Теперь это делается намного проще с помощью синтаксиса #:
class WithPrivateFields {
#privateField = 'Private Field';
static #staticPrivateField = 'Static Private Field';
get privateField() {
return this.#privateField;
}
static get staticPrivateField() {
return this.#staticPrivateField;
}
}
const obj = new WithPrivateFields();
console.log(obj.privateField);
console.log(obj['privateField']);
console.log(obj['#privateField']);
console.log(WithPrivateFields.staticPrivateField);
console.log(WithPrivateFields['staticPrivateField']);
console.log(WithPrivateFields['#staticPrivateField']);
- 6,178
- 2
- 9
- 28
А ВОТ ТАК (при чем так можно делать уже 200 лет отроду):
ES6:
var say = Symbol()
class Cat {
constructor(){
this[say]() // call private
}
[say](){
alert('im private')
}
}
ES5:
var say = Math.random() // pollyfill Symbol()
function Cat(){
this[say]() // call private methods
}
Cat.prototype[say] = function(){ alert('im a private') }
Пример использования ES6:
var handlers = Symbol()
class EventEmitter {
constructor(){
this[handlers] = []
}
on(handler){
this[handlers].push(handler)
}
emit(){
for(let handler of this[handlers]) handler()
}
}
class Cat extends EventEmitter {
}
var q = new Cat()
q.on // function
q.emit // function
q.handlers // undefined cuz PRIVATE ;)
И не обязательно больше учить названия свойств из реализации класса, таких как внутреннее свойство handlers, боясь их случайно перекрыть в классах наследниках. Использую приватные уже 6 лет. И ни каких проблем с утечками. Не понимаю людей которые говорят что в яваскрипте приватных нет.
Наслаждайтесь, и поменьше слушайте всяких неумех ;)
- 943
-
-
-
1В JavaScript приватных свойств нет, так уж устроен язык. Все, что вы привели, это обходные пути, позволяющие эмулировать приватные свойства. Это не одно и тоже. Ну и ваш вариант ES5 будет приводить к неожиданным для новичков эффектам при использовании
for inилиfor of. От меня минус. – Dmitriy Simushev Jul 18 '16 at 12:29 -
-
Однако, справедливости ради, хочу заметить, что символы - хорошая эмуляция приватных свойств. Только вот нежелательно сорить ими в глобальном scope – vp_arth Feb 02 '17 at 13:48
-
И по поводу приватности: даже в таком варианте, можно получить все ключи с помощью Object.getOwnPropertySymbols – vp_arth Feb 02 '17 at 14:01
-
@vp_arth Тогда это уже будет осознанно, а мы хотим защитить от случайного пересечения имен. Ну либо можно создать новый приметив типа privates – Maxmaxmaximus Feb 03 '17 at 01:18
class Cat { [1+2](){ функция с именем '3' } }– Maxmaxmaximus Jul 18 '16 at 05:59[1+2]()с полями как я понял пока такое не работает – Grundy Jul 18 '16 at 12:58