Символы — это уникальный тип данных в JavaScript, который при вызове возвращает значение Symbol или Symbol. При каждом вызове Symbol
возвращается новое неизменное значение. Мы можем создать символ, вызвав функцию Symbol().
let symbol1 = Symbol(); let symbol2 = Symbol(); console.log(symbol1 === symbol2) // false
Для отладки мы также можем предоставить описание символа, передав его в качестве аргумента. Несколько символов могут иметь одно и то же описание, но они все равно не будут одинаковыми. Мы можем проверить описание символа, используя свойство description
, присутствующее в символе.
let symbol1 = Symbol('my-description'); let symbol2 = Symbol('my-description'); console.log(symbol1 === symbol2) // false console.log(symbol1.description) // "my-description"
Символы как «скрытые» свойства
Символы обычно используются для создания свойств, которые должны быть скрыты и не могут быть напрямую доступны или перезаписаны. Ключи свойств в объектах могут быть двух типов: либо string
, либо symbol
. Мы можем использовать символы для создания скрытых свойств. Например:
function fn1() { const databaseName = Symbol('databaseName'); const databasePassword = Symbol('databasePassword') const userSecretInfo = { password: 'password', // String type key [databaseName]: 'databaseName', // Symbol type key [databasePassword]: 'databasePassword' // Symbol type key } console.log(userSecretInfo['databaseName']) // undefined console.log(userSecretInfo[databasePassword]) // "databasePassword" fn2(userSecretInfo) // Forgot to remove the critical passwords from the object and directly passed object in another function } function fn2(info) { console.log(info) // Now there is no more way to access the Symbol properties in here } fn1()
В приведенном выше примере мы создали 2 функции, fn1
и fn2
. В fn1
мы создали два символа и назначили их в качестве свойств в userSecretInfo
, который также содержит password
как свойство строкового типа.
Когда userSecretInfo
печатается в fn1
, регистрируются все свойства внутри объекта. Но, как только мы попытаемся зарегистрировать userSecretInfo[‘databaseName’]
, мы получим undefined
в качестве вывода, потому что значение типа символа недоступно напрямую, к ним можно получить доступ только с помощью переменных символа, как показано в операторе console.log(userSecretInfo[databasePassword])
.
Кроме того, как только этот объект будет передан какой-либо другой функции, у них больше не будет возможности получить доступ к скрытым свойствам, поскольку область действия переменных символа была ограничена функцией fn1
.
Символы не повторяются в циклах for…in
Символические свойства не повторяются в for…in
циклах. Они остаются скрытыми и недоступными на протяжении всего процесса итерации. Наряду с циклами for…in символы также не повторяются при использовании Object.keys()
. Мы получим ключи только для свойств строкового типа.
const symbolKey3 = Symbol(); const myTestObj = { stringKey1: 'value1', stringKey2: 'value2', [symbolKey3]: 'value3' } for( let key in myTestObj ) { console.log(myTestObj[key]) // "value1", "value2" } Object.keys(myTestObj) // [ "stringKey1", "stringKey2" ]
Однако при использовании Object.assign()
символы по-прежнему клонируются. Например:
const clone = Object.assign({}, myTestObj); console.log(clone[symbolKey3]) // "value3"
Глобальные символы
Как мы уже видели, символы используются для создания уникальных значений, но могут быть определенные сценарии, в которых мы можем захотеть каким-то образом сохранить символ глобально, чтобы использовать его позже в некоторых других частях кода.
Мы можем добиться этого с помощью Global Symbol Registry
, в котором мы можем создавать символы и обращаться к ним позже. Делается это с помощью метода Symbol.for(key)
.
Когда мы предоставляем какой-либо ключ в методе Symbol.for(key)
, этот ключ проверяется на соответствие данным, представленным в Global Symbol Registry
. Символ с тем же ключом возвращается, если этот ключ уже существует в реестре, в противном случае новый символ регистрируется с данным ключом для последующего использования.
// Key does not exist in symbol registry, a new symbol get registered with 'databaseName' key let databaseName = Symbol.for('databaseName'); // Reading 'databaseName' key from some other part of code, which will return same registered symbol let newDatabaseName = Symbol.for('databaseName'); console.log(databseName === newDatabaseName) // true
Теперь мы знаем, как получить зарегистрированный символ с помощью ключа, но мы также можем сделать обратное, где мы можем запросить имя ключа, с которым регистрируется символ. Это делается с помощью Symbol.keyFor(sym)
, где sym
означает символ.
let databseName = Symbol.for('databaseName') let newDatabaseName = Symbol.for('databaseName'); console.log(Symbol.keyFor(newDatabaseName)) // "databaseName"
Системные или общеизвестные символы
Это те глобальные символы, которые встроены в JavaScript для предоставления различных свойств объектам. Некоторые из них перечислены ниже: -
- Symbol.toPrimitive
- Symbol.hasInstance
- Symbol.iterator
- Symbol.isConcatSpreadable
- … и так далее.
Заключение
В заключение, символы являются важным аспектом JavaScript, который может помочь в создании скрытых и неизменяемых свойств в объектах. Они предлагают уникальный способ хранения и доступа к данным, позволяют создавать безопасный и эффективный код. Используя глобальный реестр символов и встроенные символы, разработчики могут создавать более динамичные и сложные приложения на JavaScript.