
Развлечения с марками. Эпизод 21. Приватные данные в JavaScript. 4 способа использования штампов
Привет. Я разработчик Василий Боровяк и добро пожаловать в двадцать первую серию Василия Боровяка, представляющего Fun with Stamps.
Нажмите здесь, чтобы опубликовать эту статью в LinkedIn »
Очень часто люди задают вопрос - как иметь частные (или защищенные, или просто привилегированные) данные или методы в штампах.
Ответ - так же, как это делается в JavaScript в целом. Вот 4 способа.
TL; DR: Closure, Symbol, WeakMap, прокси-объект (через штамп Приватизировать).
1. Закрытие
Это самый известный и самый частный способ скрыть ваши данные. (Прочтите комментарии!)
Давайте создадим BlockchainApi штамп с личными данными token и частной функцией request.
const rp = require('request-promise')
const {init} = require('@stamp/it') // using the init shorthand here
const BlockchainApi = init(function ({config}) {
// private data
const token = config.token
// public property
this.url = config.url
// private method accesses both the public and the private data
const request = ({path = '', body, method = 'GET'} = {}) => {
return rp({
url: this.url + '/' + path, // url is public
headers: {authorization: token}, // but token is private
json: true,
body,
method
})
}
// public method accesses the private method
this.ping = () => request()
})
В другом файле:
const config = {token: '1', url: 'http://i.ua'}
const blockchainApi = BlockchainApi({config}) // create instance
blockchainApi.ping().then(() => console.log('ok')) // have access
Как видите, штамп BlockchainApi имеет единственный инициализатор. В нем мы создаем общедоступный метод ping, который имеет доступ к закрытому методу request, который, в свою очередь, имеет доступ к закрытым данным token.
Это означает, что каждый новый метод, которому требуется доступ к request, должен быть частью этого закрытия. Иногда это может быть далеко не идеально. Марки лучше быть крошечными.
Плюсы:
- Это самый частный способ скрыть данные. Никто не может получить к нему доступ.
- Частные методы могут иметь доступ к некоторым общедоступным методам (
requestимеют доступ кping).
Минусы:
- Неэффективно с памятью. Он создает две закрытые функции для каждого экземпляра объекта.
- Чтобы добавить другой метод (например,
verifyBlock()), он должен быть частью той же функцииinit. Вздутие одного штампа - вообще плохая идея. Если штампBlockchainApiсостоит из другого штампа, он не сможет получить доступ кrequestметоду.
2. Символ
Прочтите комментарии.
const rp = require('request-promise')
const stampit = require('@stamp/it')
const requestMethod = Symbol('requestMethod')
const tokenData = Symbol('tokenData')
const BlockchainApi = stampit({
init ({config}) {
// protected data
this[tokenData] = config.token
// public property
this.url = config.url
},
methods: {
// protected method accesses both public and protected data
[requestMethod] ({path = '', body, method = 'GET'} = {}) {
return rp({
url: this.url + '/' + path,
headers: {authorization: this[tokenData]},
json: true,
body,
method
})
},
ping () {
// public method accesses the protected method
return this[requestMethod]()
}
}
})
В другом файле:
const config = {token: '1', url: 'http://i.ua'}
const blockchainApi = BlockchainApi({config}) // create instance
blockchainApi.ping().then(() => console.log('ok')) // have access
Мы используем символы в качестве ключей свойств. Это означает, что если у кода есть доступ к самому символу, он может получить «защищенные» данные.
Плюсы:
- Это полностью доказанный подход к сокрытию данных. Применимо в большинстве случаев.
- Удобно для памяти, в отличие от замыканий.
- Защищенные методы имеют доступ к всем общедоступным методам.
Минусы:
- К личным данным и методу все еще можно получить доступ и изменить с помощью еще некоторого кода JavaScript (пример).
- Чтобы добавить другой метод (например,
verifyBlock()), он должен быть частью того же файла. Если этотBlockchainApiштамп состоит из другого штампа из другого файла, он не сможет получить доступ кrequestметоду.
3. WeakMap
const rp = require('request-promise')
const stampit = require('@stamp/it')
const mapOfPrivateObjects = new WeakMap()
// private method accesses both the public and private data
function requestMethod ({path = '', body, method = 'GET'} = {}) {
return rp({
url: this.url + '/' + path,
headers: {authorization: mapOfPrivateObjects.get(this).token},
json: true,
body,
method
})
}
const BlockchainApi = stampit({
init ({config}) {
// private data and method
mapOfPrivateObjects.set(this, {
token: config.token,
request: (...args) => requestMethod.apply(this, ...args)
})
// public property
this.url = config.url
},
methods: {
ping () {
// public method accesses the private method
return mapOfPrivateObjects.get(this).request()
}
}
})
В другом файле:
const config = {token: '1', url: 'http://i.ua'}
const blockchainApi = BlockchainApi({config}) // create instance
blockchainApi.ping().then(() => console.log('ok')) // have access
Мы используем WeakMap в качестве хранилища личных данных для каждого экземпляра объекта. Это лучше, чем символы, теперь данные действительно конфиденциальны. Кроме того, это лучше, чем Closures - более эффективное использование памяти.
Плюсы:
- Это частный способ скрыть данные. Никто не может это изменить.
- Дружественный к памяти.
- Частные методы могут получить доступ к любым общедоступным методам.
Минусы:
- Чтобы добавить другой метод (например,
verifyBlock()), он должен быть частью того же файла. Если этотBlockchainApiштамп состоит из другого штампа из другого файла, он не сможет получить доступ кrequestметоду.
4. Прокси-объект через штамп Приватизировать
Если вы составите Privatize со своим штампом, то объекты, созданные из вашего нового штампа, будут объектами-оболочками. Они обернут каждый метод (кроме методов, которые вы объявили как частные) в методы-оболочки, которые будут вызывать оригинальные методы экземпляра объекта. См. Картинку.

Возвращенный объект-оболочка будет иметь тот же набор методов, что и исходный экземпляр объекта, но не будет иметь никаких свойств.
const rp = require('request-promise')
const stampit = require('@stamp/it')
const {privatizeMethods} = require('@stamp/privatize')
const BlockchainApi = stampit({
init({config}) {
// private data
this.token = config.token
// private data too!!! But was public in previous examples
this.url = config.url
}
},
privatizeMethods('request'), // Hiding a method
{
methods: {
// private method accesses private data, or public methods
request({path = '', body, method = 'GET'} = {}) {
return rp({
url: this.url + '/' + path,
headers: {authorization: this.token},
json: true,
body,
method
})
},
ping() {
// public method accesses the private method
return this.request()
}
}
})
В другом файле:
const config = {token: '1', url: 'http://i.ua'}
const blockchainApi = BlockchainApi({config}) // create instance
blockchainApi.ping().then(() => console.log('ok')) // have access
Не уверен, Pro или Con, но все общедоступные методы могут получить доступ ко всем частным методам (например, ключевому слову protected в классических классах). Итак, если у вас есть доступ к экземпляру объекта, созданному из штампа, вы не можете получить доступ к личным данным и методам. Однако вы можете взять исходный штамп, добавить к нему еще один метод и предоставить себе доступ ко всем личным данным и методам.
Плюсы:
- Только члены штампа могут получить доступ к личным данным и методам.
- Все частные методы могут обращаться ко всем публичным методам.
- Вы можете добавить другие методы (например,
verifyBlock()) из других файлов. (Если этот штампBlockchainApiсоставлен с другим штампом из другого файла, тогда каждый метод будет иметь доступ к методуrequest.)
Минусы:
- Не дружественный к памяти. Создает небольшую функцию-оболочку для каждого метода.
- Вся недвижимость становится частной. Вам нужно будет создать геттер для общедоступных свойств. Например.
getUrl().
Удачи с марками!
- Эпизод 1. Основы штампа
- Эпизод 2. Внедрение зависимостей в FP
- Эпизод 3. Сравнение с классами ES2015
- Эпизод 4. Самостоятельная реализация штампов в 30 LOC
- Эпизод 5. Шаблон оформления композиции
- Эпизод 6. Статика - свойства на марках
- Эпизод 7. Ранняя и поздняя инъекция зависимости
- Эпизод 8. Отслеживание и переопределение композиции
- Эпизод 9. Отключение композиции ()
- Эпизод 10. Моя ментальная модель штампа
- Эпизод 11. Мешающий состав
- Эпизод 12. Новый дом @stamp
- Эпизод 13. Метод контроля коллизий
- Эпизод 14. Новый @ штамп / он на замену Stampit
- Эпизод 15. Экосистема @stamp / modules
- Эпизод 16. Смешанные классы TypeScript против штампов
- Эпизод 17. Простое 100% покрытие модульных тестов на JS
- Эпизод 18. Рай для инъекций зависимости
- Эпизод 19. Абстрактные методы Java / C # в JavaScript
- Эпизод 20. Stampit v4
- Эпизод 21. Личные данные в JavaScript. 4 способа использования штампов (эта статья)
- Эпизод 22. Экземпляр JavaScript как составной штамп
- Эпизод 23. Новый stampit.js.org со всеми документами
- Эпизод 24. Новая« именная функция»