Частные объекты в JavaScript ES6 классы
Можно ли создавать частные свойства в классах ES6?
Вот пример.
Как я могу предотвратить доступ к instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
Ответы
Ответ 1
Частные поля внедряются в стандарте ECMA. Вы можете начать использовать их сегодня с babel 7 и предустановкой стадии 3. См. пример Babel REPL.
class Something {
#property;
constructor(){
this.#property = "test";
}
}
const instance = new Something();
console.log(instance.property); //=> undefined
Ответ 2
Короткий ответ, нет, нет встроенной поддержки частных свойств с ES6-классами.
Но вы могли бы подражать этому поведению, не добавляя новые свойства к объекту, но сохраняя их внутри конструктора классов и используя геттеры и сеттеры для достижения скрытых свойств. Обратите внимание, что геттеры и сеттеры получают переопределение в каждом новом экземпляре класса.
ES6
class Person {
constructor(name) {
var _name = name
this.setName = function(name) { _name = name; }
this.getName = function() { return _name; }
}
}
ES5
function Person(name) {
var _name = name
this.setName = function(name) { _name = name; }
this.getName = function() { return _name; }
}
Ответ 3
Чтобы расширить ответ на @loganfsmyth:
Единственные действительно частные данные в JavaScript все еще ограничены переменными. Вы не можете иметь приватные свойства в смысле свойств, которые доступны внутренне, так же, как и общедоступные свойства, но вы можете использовать скоординированные переменные для хранения частных данных.
Скользящие переменные
Подходом здесь является использование области функции конструктора, которая является частной, для хранения частных данных. Чтобы методы имели доступ к этим личным данным, они также должны быть созданы внутри конструктора, что означает, что вы воссоздаете их с каждым экземпляром. Это штраф за производительность и память, но некоторые считают, что штраф является приемлемым. Штраф можно избежать для методов, которым не нужен доступ к личным данным, добавив их в прототип, как обычно.
Пример:
function Person(name) {
let age = 20; // this is private
this.name = name; // this is public
this.greet = function () {
// here we can access both name and age
console.log(`name: ${this.name}, age: ${age}`);
};
}
let joe = new Person('Joe');
joe.greet();
// here we can access name but not age
Scope WeakMap
A WeakMap можно использовать, чтобы избежать предыдущей производительности подхода и штрафа за память. WeakMaps связывает данные с объектами (здесь, экземпляры) таким образом, что к ним можно получить доступ только с помощью этой WeakMap. Таким образом, мы используем метод переменных области для создания частной WeakMap, а затем используем эту WeakMap для извлечения частных данных, связанных с this
. Это быстрее, чем метод переменных с областью, потому что все ваши экземпляры могут совместно использовать одну WeakMap, поэтому вам не нужно воссоздавать методы, чтобы они могли получить доступ к своим собственным WeakMaps.
Пример:
let Person = (function () {
let privateProps = new WeakMap();
class Person {
constructor(name) {
this.name = name; // this is public
privateProps.set(this, {age: 20}); // this is private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
}
}
return Person;
})();
let joe = new Person('Joe');
joe.greet();
// here we can access joe name but not age
В этом примере используется Object для использования одного WeakMap для нескольких частных свойств; вы также можете использовать несколько WeakMaps и использовать их как age.set(this, 20)
или написать небольшую обертку и использовать ее по-другому, например privateProps.set(this, 'age', 0)
.
Конфиденциальность этого подхода теоретически может быть нарушена путем вмешательства в глобальный объект WeakMap
. Тем не менее, все JavaScript можно сломать искаженными глобалами. Наш код уже построен на предположении, что этого не происходит.
(Этот метод также можно выполнить с помощью Map
, но WeakMap
лучше, потому что Map
создаст утечки памяти, если вы не будете очень осторожны, и для этих целей они не отличаются друг от друга.)
Полуответ: Обозначенные символы
Символ - это тип примитивного значения, который может служить именем свойства. Вы можете использовать метод переменной scoped для создания частного символа, а затем хранить личные данные в this[mySymbol]
.
Конфиденциальность этого метода может быть нарушена с помощью Object.getOwnPropertySymbols
, но это несколько неудобно делать.
Пример:
let Person = (function () {
let ageKey = Symbol();
class Person {
constructor(name) {
this.name = name; // this is public
this[ageKey] = 20; // this is intended to be private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${this[ageKey]}`);
}
}
return Person;
})();
let joe = new Person('Joe');
joe.greet();
// Here we can access joe name and, with a little effort, age. ageKey is
// not in scope, but we can obtain it by listing all Symbol properties on
// joe with `Object.getOwnPropertySymbols(joe)`.
Полу-ответ: подчеркивание
Старый по умолчанию, просто используйте общедоступное свойство с префиксом подчеркивания. Хотя это и не является частной собственностью, эта конвенция достаточно распространена, что она хорошо говорит о том, что читатели должны относиться к собственности как к частной, которая часто выполняет свою работу. В обмен на этот провал мы получаем подход, который легче читать, проще печатать и быстрее.
Пример:
class Person {
constructor(name) {
this.name = name; // this is public
this._age = 20; // this is intended to be private
}
greet() {
// Here we can access both name and age
console.log(`name: ${this.name}, age: ${this._age}`);
}
}
let joe = new Person('Joe');
joe.greet();
// Here we can access both joe name and age. But we know we aren't
// supposed to access his age, which just might stop us.
Заключение
В ES2017 по-прежнему нет идеального способа делать частные свойства. Различные подходы имеют плюсы и минусы. Скользящие переменные являются действительно частными; Scope WeakMaps являются очень частными и более практичными, чем облачные переменные; Обозначенные символы являются достаточно частными и разумно практичными; подчеркивания часто достаточно приватны и очень практичны.
Ответ 4
Обновление. Предложение с более сильным синтаксисом находится на своем пути. Взносы приветствуются.
Да, есть - для облачного доступа к объектам - ES6 представляет Symbol
s.
Символы уникальны, вы не можете получить доступ к одному извне, кроме как с отражением (например, privates в Java/С#), но любой, кто имеет доступ к символу внутри, может использовать его для доступа к ключам:
var property = Symbol();
class Something {
constructor(){
this[property] = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> undefined, can only access with access to the Symbol
Ответ 5
Ответ: "Нет". Но вы можете создать частный доступ к следующим свойствам:
(Предложение о том, что Символы могут использоваться для обеспечения конфиденциальности, было истинным в более ранней версии спецификации ES6, но это уже не так: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html и fooobar.com/questions/28094/.... Более подробную информацию о символах и конфиденциальности см. в разделе https://curiosity-driven.org/private-properties-in-javascript)
Ответ 6
Единственный способ получить истинную конфиденциальность в JS - это определить область видимости, поэтому нет способа иметь свойство, являющееся членом this
, которое будет доступно только внутри компонента. Лучший способ хранить действительно частные данные в ES6 - с помощью WeakMap.
const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();
class SomeClass {
constructor() {
privateProp1.set(this, "I am Private1");
privateProp2.set(this, "I am Private2");
this.publicVar = "I am public";
this.publicMethod = () => {
console.log(privateProp1.get(this), privateProp2.get(this))
};
}
printPrivate() {
console.log(privateProp1.get(this));
}
}
Очевидно, что это, вероятно, медленный и определенно уродливый, но он обеспечивает конфиденциальность.
Имейте в виду, что ДАЖЕ ЭТО НЕ СОВЕРШЕННО, потому что Javascript настолько динамичен. Кто-то все еще мог
var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
// Store 'this', 'key', and 'value'
return oldSet.call(this, key, value);
};
чтобы поймать значения по мере их хранения, поэтому, если вы хотите быть более осторожными, вам нужно будет привязать локальную ссылку к .set
и .get
, чтобы использовать явно, вместо того, чтобы полагаться на переопределяемый прототип.
const {set: WMSet, get: WMGet} = WeakMap.prototype;
const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();
class SomeClass {
constructor() {
WMSet.call(privateProp1, this, "I am Private1");
WMSet.call(privateProp2, this, "I am Private2");
this.publicVar = "I am public";
this.publicMethod = () => {
console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
};
}
printPrivate() {
console.log(WMGet.call(privateProp1, this));
}
}
Ответ 7
Для будущих ссылок на других пользователей, я слышал, что рекомендация состоит в том, чтобы использовать WeakMaps для хранения личных данных.
Вот более понятный рабочий пример:
function storePrivateProperties(a, b, c, d) {
let privateData = new WeakMap;
// unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value
let keyA = {}, keyB = {}, keyC = {}, keyD = {};
privateData.set(keyA, a);
privateData.set(keyB, b);
privateData.set(keyC, c);
privateData.set(keyD, d);
return {
logPrivateKey(key) {
switch(key) {
case "a":
console.log(privateData.get(keyA));
break;
case "b":
console.log(privateData.get(keyB));
break;
case "c":
console.log(privateData.get(keyC));
break;
case "d":
console.log(privateData.set(keyD));
break;
default:
console.log(`There is no value for ${key}`)
}
}
}
}
Ответ 8
Зависит от о котором вы спрашиваете: -)
Нет private
модификатор свойства включен в Предложение минимально минимальных классов, которое, похоже, превратилось в текущий проект.
Однако может существовать поддержка личные имена, что позволяет использовать частные свойства - и они, вероятно, могут также использоваться в определениях классов.
Ответ 9
Использование модулей ES6 (изначально предложенных @d13) хорошо работает для меня. Он не идеально имитирует частные свойства, но по крайней мере вы можете быть уверены, что свойства, которые должны быть частными, не будут протекать вне вашего класса. Вот пример:
something.js
let _message = null;
const _greet = name => {
console.log('Hello ' + name);
};
export default class Something {
constructor(message) {
_message = message;
}
say() {
console.log(_message);
_greet('Bob');
}
};
Затем потребительский код может выглядеть так:
import Something from './something.js';
const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception
Обновление (важно):
Как отмечают в комментариях @DanyalAytekin, эти частные свойства являются статическими, поэтому глобальны по объему. Они будут хорошо работать при работе с Singletons, но для объектов Transient необходимо соблюдать осторожность. Расширение приведенного выше примера:
import Something from './something.js';
import Something2 from './something.js';
const a = new Something('a');
a.say(); // a
const b = new Something('b');
b.say(); // b
const c = new Something2('c');
c.say(); // c
a.say(); // c
b.say(); // c
c.say(); // c
Ответ 10
Завершение @d13 и комментарии @johnny-oshika и @DanyalAytekin:
Я думаю, что в примере, предоставленном @johnny-oshika, мы могли бы использовать обычные функции вместо функций стрелок, а затем .bind
их с текущим объектом плюс объект _privates
в качестве валютного параметра:
something.js
function _greet(_privates) {
return 'Hello ' + _privates.message;
}
function _updateMessage(_privates, newMessage) {
_privates.message = newMessage;
}
export default class Something {
constructor(message) {
const _privates = {
message
};
this.say = _greet.bind(this, _privates);
this.updateMessage = _updateMessage.bind(this, _privates);
}
}
main.js
import Something from './something.js';
const something = new Something('Sunny day!');
const message1 = something.say();
something.updateMessage('Cloudy day!');
const message2 = something.say();
console.log(message1 === 'Hello Sunny day!'); // true
console.log(message2 === 'Hello Cloudy day!'); // true
// the followings are not public
console.log(something._greet === undefined); // true
console.log(something._privates === undefined); // true
console.log(something._updateMessage === undefined); // true
// another instance which doesn't share the _privates
const something2 = new Something('another Sunny day!');
const message3 = something2.say();
console.log(message3 === 'Hello another Sunny day!'); // true
Преимущества, о которых я могу думать:
- мы можем иметь частные методы (
_greet
и _updateMessage
действовать как частные методы, если мы не export
ссылки)
- хотя они и не находятся в прототипе, вышеупомянутые методы будут сохранять память, потому что экземпляры создаются один раз, вне класса (в отличие от их определения в конструкторе)
- мы не утечка каких-либо глобальных переменных, так как мы находимся внутри модуля
- мы также можем иметь частные свойства, используя связанный объект
_privates
Некоторые недостатки, о которых я могу думать:
Доступный фрагмент можно найти здесь: http://www.webpackbin.com/NJgI5J8lZ
Ответ 11
Да - вы можете создать инкапсулированное свойство, но это не было сделано с помощью модификаторов доступа (public | private), по крайней мере, не с ES6.
Вот простой пример того, как это можно сделать с ES6:
1 Создайте класс, используя class слово
2 Внутри этого конструктора объявить переменную с блочной областью, используя let ИЛИ const зарезервированные слова → поскольку они являются областью блока, к которой они не могут быть доступны извне (инкапсулированные)
3 Чтобы позволить некоторым элементам управления доступом (seters | getters) к этим переменным, вы можете объявить метод экземпляра внутри этого конструктора, используя: this.methodName=function(){}
синтаксис
"use strict";
class Something{
constructor(){
//private property
let property="test";
//private final (immutable) property
const property2="test2";
//public getter
this.getProperty2=function(){
return property2;
}
//public getter
this.getProperty=function(){
return property;
}
//public setter
this.setProperty=function(prop){
property=prop;
}
}
}
Теперь давайте проверим его:
var s=new Something();
console.log(typeof s.property);//undefined
s.setProperty("another");//set to encapsulated `property`
console.log(s.getProperty());//get encapsulated `property` value
console.log(s.getProperty2());//get encapsulated immutable `property2` value
Ответ 12
Другой подход к "private"
Вместо того, чтобы бороться с тем, что частная видимость в настоящее время недоступна в ES6, я решил использовать более практичный подход, который отлично подходит, если ваша среда IDE поддерживает JSDoc (например, Webstorm). Идея заключается в использовании тега @private
. Что касается разработки, среда IDE не позволит вам получить доступ к любому частному члену вне его класса. Работает очень хорошо для меня, и это было действительно полезно для скрытия внутренних методов, поэтому функция автозаполнения показывает мне именно то, что класс действительно хотел разоблачить. Вот пример:
![автозаполнение, показывающее только общедоступные вещи]()
Ответ 13
WeakMap
- поддерживается в IE11 (символов нет)
- hard-private (реквизиты с использованием символов мягко-частные из-за
Object.getOwnPropertySymbols
)
- может выглядеть действительно чистым (в отличие от закрытий, для которых требуются все реквизиты и методы в конструкторе)
Сначала определите функцию для переноса WeakMap:
function Private() {
const map = new WeakMap();
return obj => {
let props = map.get(obj);
if (!props) {
props = {};
map.set(obj, props);
}
return props;
};
}
Затем создайте ссылку вне вашего класса:
const p = new Private();
class Person {
constructor(name, age) {
this.name = name;
p(this).age = age; // it easy to set a private variable
}
getAge() {
return p(this).age; // and get a private variable
}
}
Примечание. класс не поддерживается IE11, но в этом примере он выглядит более чистым.
Ответ 14
Я думаю, ответ Benjamin, вероятно, лучший для большинства случаев, пока язык не поддерживает явно частные переменные.
Однако, если по какой-то причине вам необходимо предотвратить доступ с помощью Object.getOwnPropertySymbols()
, метод, который я рассмотрел, неконфигурируемое, неперечислимое, незаписываемое свойство, которое может использоваться как идентификатор свойства для каждого объекта при построении (например, уникальный Symbol
, если у вас еще нет другого уникального свойства, такого как id
). Затем просто держите карту каждого объекта 'private' переменных, используя этот идентификатор.
const privateVars = {};
class Something {
constructor(){
Object.defineProperty(this, '_sym', {
configurable: false,
enumerable: false,
writable: false,
value: Symbol()
});
var myPrivateVars = {
privateProperty: "I'm hidden"
};
privateVars[this._sym] = myPrivateVars;
this.property = "I'm public";
}
getPrivateProperty() {
return privateVars[this._sym].privateProperty;
}
// A clean up method of some kind is necessary since the
// variables won't be cleaned up from memory automatically
// when the object is garbage collected
destroy() {
delete privateVars[this._sym];
}
}
var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"
Потенциальное преимущество этого подхода в использовании WeakMap
- более быстрое время доступа, если производительность становится проблемой.
Ответ 15
Я считаю, что можно получить "лучшее из обоих миров", используя замыкания внутри конструкторов. Существует два варианта:
Все члены данных являются закрытыми
function myFunc() {
console.log('Value of x: ' + this.x);
this.myPrivateFunc();
}
function myPrivateFunc() {
console.log('Enhanced value of x: ' + (this.x + 1));
}
class Test {
constructor() {
let internal = {
x : 2,
};
internal.myPrivateFunc = myPrivateFunc.bind(internal);
this.myFunc = myFunc.bind(internal);
}
};
Ответ 16
Фактически это возможно с использованием символов и прокси. Вы используете символы в области видимости класса и устанавливаете два ловушки в прокси: один для прототипа класса, так что Reflect.ownKeys(экземпляр) или Object.getOwnPropertySymbols не дает ваши символы, а другой для самого конструктора поэтому, когда вызывается new ClassName(attrs)
, возвращаемый экземпляр будет перехвачен и будет заблокирован собственный символ свойств.
Здесь код:
const Human = (function() {
const pet = Symbol();
const greet = Symbol();
const Human = privatizeSymbolsInFn(function(name) {
this.name = name; // public
this[pet] = 'dog'; // private
});
Human.prototype = privatizeSymbolsInObj({
[greet]() { // private
return 'Hi there!';
},
revealSecrets() {
console.log(this[greet]() + ` The pet is a ${this[pet]}`);
}
});
return Human;
})();
const bob = new Human('Bob');
console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']
// Setting up the traps inside proxies:
function privatizeSymbolsInObj(target) {
return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}
function privatizeSymbolsInFn(Class) {
function construct(TargetClass, argsList) {
const instance = new TargetClass(...argsList);
return privatizeSymbolsInObj(instance);
}
return new Proxy(Class, { construct });
}
Ответ 17
Лично мне нравится предложение оператора привязки ::
и затем объединить его с упомянутым решением @d13, но на данный момент придерживаться с ответом @d13, где вы используете ключевое слово export
для своего класса и помещаете частные функции в модуль.
существует еще одно жесткое решение, которое не упоминалось здесь ниже, более функциональный подход и позволит ему иметь все частные реквизиты/методы внутри класса.
Private.js
export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }
Test.js
import { get, set } from './utils/Private'
export default class Test {
constructor(initialState = {}) {
const _set = this.set = set(initialState);
const _get = this.get = get(initialState);
this.set('privateMethod', () => _get('propValue'));
}
showProp() {
return this.get('privateMethod')();
}
}
let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5
комментарии к этому будут оценены.
Ответ 18
Я столкнулся с этим сообщением при поиске лучшей практики для "частных данных для классов". Было упомянуто, что некоторые из шаблонов будут иметь проблемы с производительностью.
Я собрал несколько тестов jsperf на основе 4 основных шаблонов из онлайн-книги "Изучение ES6":
http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes
Тесты можно найти здесь:
https://jsperf.com/private-data-for-classes
В Chrome 63.0.3239/Mac OS X 10.11.6 наиболее эффективными моделями были "Частные данные через среды конструктора" и "Частные данные через соглашение об именах". Для меня Safari хорошо зарекомендовал себя для WeakMap, но Chrome не очень хорошо.
Я не знаю влияния памяти, но шаблон для "среды конструктора", который некоторые предупреждали, был проблемой производительности, был очень результативным.
4 основных шаблона:
Частные данные через среды конструктора
class Countdown {
constructor(counter, action) {
Object.assign(this, {
dec() {
if (counter < 1) return;
counter--;
if (counter === 0) {
action();
}
}
});
}
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
Частные данные через среды конструктора 2
class Countdown {
constructor(counter, action) {
this.dec = function dec() {
if (counter < 1) return;
counter--;
if (counter === 0) {
action();
}
}
}
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
Частные данные с помощью соглашения об именах
class Countdown {
constructor(counter, action) {
this._counter = counter;
this._action = action;
}
dec() {
if (this._counter < 1) return;
this._counter--;
if (this._counter === 0) {
this._action();
}
}
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
Личные данные через WeakMaps
const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
constructor(counter, action) {
_counter.set(this, counter);
_action.set(this, action);
}
dec() {
let counter = _counter.get(this);
if (counter < 1) return;
counter--;
_counter.set(this, counter);
if (counter === 0) {
_action.get(this)();
}
}
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
Личные данные с помощью символов
const _counter = Symbol('counter');
const _action = Symbol('action');
class Countdown {
constructor(counter, action) {
this[_counter] = counter;
this[_action] = action;
}
dec() {
if (this[_counter] < 1) return;
this[_counter]--;
if (this[_counter] === 0) {
this[_action]();
}
}
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
Ответ 19
class Something {
constructor(){
var _property = "test";
Object.defineProperty(this, "property", {
get: function(){ return _property}
});
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"
Ответ 20
Даже Typescript не может этого сделать. Из документация:
Когда элемент отмечен как личный, доступ к нему из-за пределов его класса невозможен. Например:
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
new Animal("Cat").name; // Error: 'name' is private;
Но на их игровая площадка это дает:
var Animal = (function () {
function Animal(theName) {
this.name = theName;
}
return Animal;
}());
console.log(new Animal("Cat").name);
Таким образом, их ключевое слово "private" неэффективно.
Ответ 21
Придя очень поздно к этой вечеринке, но я попал в вопрос OP в поиске, поэтому...
Да, вы можете иметь частные свойства, обернув объявление класса в закрытии
Есть пример того, как у меня есть частные методы в этот код. В нижеприведенном фрагменте класс Subscribable имеет две функции 'private' process
и processCallbacks
. Любые свойства могут быть добавлены таким образом, и они остаются закрытыми с помощью закрытия. Конфиденциальность IMO - редкая необходимость, если проблемы хорошо разделены, и Javascript не нуждается в том, чтобы стать раздутым, добавив больше синтаксиса, когда закрытие выполняет эту работу аккуратно.
const Subscribable = (function(){
const process = (self, eventName, args) => {
self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};
const processCallbacks = (self, eventName, args) => {
if (self.callingBack.get(eventName).length > 0){
const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
self.callingBack.set(eventName, callingBack);
process(self, eventName, args);
nextCallback(...args)}
else {
delete self.processing.delete(eventName)}};
return class {
constructor(){
this.callingBack = new Map();
this.processing = new Map();
this.toCallbacks = new Map()}
subscribe(eventName, callback){
const callbacks = this.unsubscribe(eventName, callback);
this.toCallbacks.set(eventName, [...callbacks, callback]);
return () => this.unsubscribe(eventName, callback)} // callable to unsubscribe for convenience
unsubscribe(eventName, callback){
let callbacks = this.toCallbacks.get(eventName) || [];
callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
if (callbacks.length > 0) {
this.toCallbacks.set(eventName, callbacks)}
else {
this.toCallbacks.delete(eventName)}
return callbacks}
emit(eventName, ...args){
this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
if (!this.processing.has(eventName)){
process(this, eventName, args)}}}})();
Мне нравится этот подход, потому что он прекрасно разделяет проблемы и сохраняет все по-настоящему частным. Единственным недостатком является необходимость использования "я" (или чего-то подобного) для ссылки на 'this' в частном контенте.
Ответ 22
О, так много экзотических решений! Меня обычно не волнует конфиденциальность, поэтому я использую "псевдо-конфиденциальность", как здесь сказано. Но если все равно (если для этого есть особые требования), я использую что-то вроде этого:
class jobImpl{
// public
constructor(name){
this.name = name;
}
// public
do(time){
console.log('${this.name} started at ${time}');
this.prepare();
this.execute();
}
//public
stop(time){
this.finish();
console.log('${this.name} finished at ${time}');
}
// private
prepare(){ console.log('prepare..'); }
// private
execute(){ console.log('execute..'); }
// private
finish(){ console.log('finish..'); }
}
function Job(name){
var impl = new jobImpl(name);
return {
do: time => impl.do(time),
stop: time => impl.stop(time)
};
}
// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");
// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error
Другая возможная реализация функции (конструктор) Job
:
function Job(name){
var impl = new jobImpl(name);
this.do = time => impl.do(time),
this.stop = time => impl.stop(time)
}
Ответ 23
См. этот ответ для чистого и простого "класса" решения с частным и общедоступным интерфейсом и поддержкой композиции
Ответ 24
Да, вполне может, и довольно легко. Это делается путем просмотра ваших частных переменных и функций путем возврата графа объекта-прототипа в конструкторе. Это ничего нового, но возьмите немного js foo, чтобы понять его элегантность. Этот способ не использует глобальные облачные или слабые карты. Это форма отражения, встроенная в язык. В зависимости от того, как вы это используете; можно либо вызвать исключение, которое прерывает стек вызовов, либо закопать исключение как undefined
. Это показано ниже и может больше узнать об этих функциях здесь
class Clazz {
constructor() {
var _level = 1
function _private(x) {
return _level * x;
}
return {
level: _level,
public: this.private,
public2: function(x) {
return _private(x);
},
public3: function(x) {
return _private(x) * this.public(x);
},
};
}
private(x) {
return x * x;
}
}
var clazz = new Clazz();
console.log(clazz._level); //undefined
console.log(clazz._private); // undefined
console.log(clazz.level); // 1
console.log(clazz.public(1)); //2
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error
Ответ 25
На самом деле это возможно.
1. Сначала создайте класс и в конструкторе верните вызываемую _public
функцию.
2. В вызываемой _public
функции передайте this
ссылку (чтобы получить доступ ко всем частным методам и реквизитам) и все аргументы из constructor
(которые будут переданы в new Names()
)
3. В области _public
function также есть класс Names
с доступом к this
(_this) ссылке класса private Names
class Names {
constructor() {
this.privateProperty = 'John';
return _public(this, arguments);
}
privateMethod() { }
}
const names = new Names(1,2,3);
console.log(names.somePublicMethod); //[Function]
console.log(names.publicProperty); //'Jasmine'
console.log(names.privateMethod); //undefined
console.log(names.privateProperty); //undefind
function _public(_this, _arguments) {
class Names {
constructor() {
this.publicProperty = 'Jasmine';
_this.privateProperty; //"John";
_this.privateMethod; //[Function]
}
somePublicMethod() {
_this.privateProperty; //"John";
_this.privateMethod; //[Function]
}
}
return new Names(..._arguments);
}
Ответ 26
Я нашел очень простое решение, просто используйте Object.freeze()
. Конечно, проблема заключается в том, что вы не можете ничего добавить к объекту позже.
class Cat {
constructor(name ,age) {
this.name = name
this.age = age
Object.freeze(this)
}
}
let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode
Ответ 27
Я использую этот шаблон, и он всегда работал для меня
class Test {
constructor(data) {
class Public {
constructor(prv) {
// public function (must be in constructor on order to access "prv" variable)
connectToDb(ip) {
prv._db(ip, prv._err);
}
}
// public function w/o access to "prv" variable
log() {
console.log("I'm logging");
}
}
// private variables
this._data = data;
this._err = function(ip) {
console.log("could not connect to "+ip);
}
}
// private function
_db(ip, err) {
if(!!ip) {
console.log("connected to "+ip+", sending data '"+this.data+"'");
return true;
}
else err(ip);
}
}
var test = new Test(10),
ip = "185.167.210.49";
test.connectToDb(ip); // true
test.log(); // I'm logging
test._err(ip); // undefined
test._db(ip, function() { console.log("You have got hacked!"); }); // undefined
Ответ 28
Вы можете попробовать этот https://www.npmjs.com/package/private-members
Этот пакет сохранит элементы по экземпляру.
const pvt = require('private-members');
const _ = pvt();
let Exemplo = (function () {
function Exemplo() {
_(this).msg = "Minha Mensagem";
}
_().mensagem = function() {
return _(this).msg;
}
Exemplo.prototype.showMsg = function () {
let msg = _(this).mensagem();
console.log(msg);
};
return Exemplo;
})();
module.exports = Exemplo;
Ответ 29
Большинство ответов либо говорят, что это невозможно, либо требуют использования WeakMap или Symbol, которые являются функциями ES6, которые, вероятно, потребуют многопотоков. Там, тем не менее, другой путь! Проверьте это:
// 1. Create closure
var SomeClass = function() {
// 2. Create `key` inside a closure
var key = {};
// Function to create private storage
var private = function() {
var obj = {};
// return Function to access private storage using `key`
return function(testkey) {
if(key === testkey) return obj;
// If `key` is wrong, then storage cannot be accessed
console.error('Cannot access private properties');
return undefined;
};
};
var SomeClass = function() {
// 3. Create private storage
this._ = private();
// 4. Access private storage using the `key`
this._(key).priv_prop = 200;
};
SomeClass.prototype.test = function() {
console.log(this._(key).priv_prop); // Using property from prototype
};
return SomeClass;
}();
// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged
// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged
Ответ 30
Другой способ, похожий на последние два опубликованных
class Example {
constructor(foo) {
// privates
const self = this;
this.foo = foo;
// public interface
return self.public;
}
public = {
// empty data
nodata: { data: [] },
// noop
noop: () => {},
}
// everything else private
bar = 10
}
const test = new Example('FOO');
console.log(test.foo); // undefined
console.log(test.noop); // { data: [] }
console.log(test.bar); // undefined