Какова мотивация приведения символов в ES6?

UPDATE. Недавно появилась блестящая статья из Mozilla. Прочтите, если вам интересно.

Как вы знаете, они планируют включить новый примитивный тип Symbol в ECMAScript 6 (не говоря уже о некоторых других сумасшедших вещах). Я всегда думал, что понятие :symbol в Ruby бесполезно; мы могли бы легко использовать простые строки, как в JavaScript. И теперь они решили усложнить ситуацию в JS.

Я не понимаю мотивацию. Может ли кто-нибудь объяснить мне, нужны ли нам символы в JavaScript?

Ответ 1

Первоначальная мотивация введения символов в Javascript заключалась в том, чтобы включить частные свойства.

К сожалению, они оказались сильно пониженными. Они больше не являются частными, так как вы можете найти их через отражение, например, используя Object.getOwnPropertySymbols или прокси.

Теперь они известны как уникальные символы, и их единственное предназначение - избегать конфликтов имен между свойствами. Например, сам ECMAScript теперь может вводить расширения с помощью определенных методов, которые вы можете наложить на объекты (например, определять их протокол итерации), не рискуя столкнуться с именами пользователей.

ли, что достаточно сильна мотивация для добавления символов языка является дискуссионным.

Ответ 2

Символы не гарантируют настоящую конфиденциальность, но могут использоваться для разделения общедоступных и внутренних свойств объектов. Возьмем пример, где мы можем использовать Symbol для наличия частных свойств.

Возьмем пример, когда свойство объекта не является приватным.

var Pet = (function() {
  function Pet(type) {
    this.type = type;
  }
  Pet.prototype.getType = function() {
    return this.type;
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

Выше, свойство класса Pet type не является приватным. Чтобы сделать это частным, мы должны создать закрытие. В приведенном ниже примере показано, как мы можем сделать type private с закрытием.

var Pet = (function() {
  function Pet(type) {
    this.getType = function(){
      return type;
    };
  }
  return Pet;
}());

var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog

Недостаток вышеприведенного подхода: мы вводим дополнительное закрытие для каждого созданного экземпляра Pet, который может повредить производительность.

Теперь введем Symbol. Это может помочь нам сделать недвижимость частной, не используя лишние ненужные закрытия. Пример кода ниже:

var Pet = (function() {
  var typeSymbol = Symbol('type');
  function Pet(type) {
    this[typeSymbol] = type;
  }
  Pet.prototype.getType = function(){
    return this[typeSymbol];
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog

Ответ 3

Symbols - это новый, особый вид объекта, который можно использовать как уникальное имя свойства в объектах. Использование Symbol вместо string позволяет различным модулям создавать свойства, которые не конфликтуют друг с другом. Symbols также можно сделать приватными, чтобы к их свойствам не мог получить доступ никто, кто уже не имеет прямого доступа к Symbol.

Symbols - это новый примитив. Как и у number, string и boolean примитивов, Symbol имеет функцию, которую можно использовать для их создания. В отличие от других примитивов, Symbols не имеют буквального синтаксиса (например, как string имеет '') - единственный способ создать их с помощью конструктора Symbol следующим образом:

let symbol = Symbol();

В действительности Symbol - это просто немного другой способ присоединения свойств к объекту - вы можете легко предоставить общеизвестные Symbols качестве стандартных методов, например Object.prototype.hasOwnProperty который появляется во всем, что наследуется от Object.

Вот некоторые из преимуществ примитивного типа Symbol.

Symbols имеют встроенную возможность отладки

Symbols можно дать описание, которое на самом деле просто используется для отладки, чтобы немного упростить жизнь при регистрации их на консоли.

Symbols могут использоваться как ключи Object

Вот где Symbol становится действительно интересным. Они сильно переплетены с предметами. Symbol можно назначать в качестве ключей для объектов, то есть вы можете назначать неограниченное количество уникальных Symbol для объекта и гарантировать, что они никогда не будут конфликтовать со string ключами или другими уникальными Symbols.

Symbols могут использоваться как уникальные значения.

Предположим, у вас есть библиотека журналов, которая включает несколько уровней журналов, таких как logger.levels.DEBUG, logger.levels.INFO, logger.levels.WARN и так далее. В коде ES5 вы хотите сделать эти string (например, logger.levels.DEBUG === 'debug') или number (logger.levels.DEBUG === 10). Оба из них не идеальны, так как эти значения не являются уникальными значениями, но Symbol есть! Так что logger.levels просто становится:

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

Узнайте больше в этой замечательной статье.

Ответ 4

Этот пост посвящен Symbol(), представленному с фактическими примерами, которые я мог найти/сделать, и фактами и определениями, которые я мог найти.

TL;DR;

Symbol() - это тип данных, введенный с выпуском ECMAScript 6 (ES6).

Есть два любопытных факта о Символе.

  • первый тип данных и только тип данных в JavaScript, который не имеет литерала

  • любая переменная, определенная с помощью Symbol(), получает уникальный контент, но это не действительно частный.

  • у любых данных есть свой собственный Символ, и для тех же данных символы будут одинаковыми. Больше информации в следующем абзаце, иначе это не TLRD;:)

Как инициализировать символ?

1. Чтобы получить уникальный идентификатор с отлаживаемым значением

Вы можете сделать это следующим образом:

var mySymbol1 = Symbol();

Или так:

var mySymbol2 = Symbol("some text here");

Строка "some text here" не может быть извлечена из символа, это просто описание для целей отладки. Это никак не изменяет поведение символа. Хотя, вы могли бы console.log его (что справедливо, поскольку это значение для отладки, чтобы не ошибиться в этом журнале с помощью какой-либо другой записи в журнале):

console.log(mySymbol2);
// Symbol(some text here)

2. Чтобы получить символ для некоторых строковых данных

В этом случае значение символа фактически принимается во внимание, и таким образом два символа могут быть неединственными.

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

Позвольте называть эти символы символами второго типа. Они не пересекаются с символами "первого типа" (т.е. Теми, которые определены с помощью Symbol(data)).

Следующие два абзаца относятся только к символу первого типа.

Как извлечь выгоду из использования Symbol вместо старых типов данных?

Сначала рассмотрим объект, стандартный тип данных. Мы могли бы определить некоторые пары ключевых значений там и иметь доступ к значениям, указав ключ.

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

Что делать, если у нас есть два человека с именем Петр?

Выполнение этого действия:

var persons = {"peter":"first", "peter":"pan"};

не имеет большого смысла.

Итак, кажется, проблема двух совершенно разных людей, имеющих одно и то же имя. Позвольте затем указать новый Symbol(). Это как человек в реальной жизни - любой человек уникален, но их имена могут быть равными. Пусть определите двух "лиц".

 var a = Symbol("peter");
 var b = Symbol("peter");

Теперь у нас есть два разных человека с тем же именем. Действительно ли наши люди разные? Они есть; вы можете проверить это:

 console.log(a == b);
 // false

Как нам там выгодно?

Мы можем сделать две записи в вашем объекте для разных лиц, и они не могут быть приняты каким-либо образом.

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

Примечание:
Однако стоит отметить, что строгая объект с помощью JSON.stringify приведет к падению всех пар, инициализированных символом в качестве ключа.
Выполнение Object.keys не вернет такие пары Symbol()->value.

Используя эту инициализацию, абсолютно невозможно перенести записи для первого и второго лиц. Вызов console.log для них будет правильно выводить свои имена.

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

При использовании в объекте, как он отличается от определения неперечислимого свойства?

В самом деле, уже существовал способ определения свойства, которое должно быть скрыто от Object.keys и перечисления. Вот он:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

Какая разница приносит Symbol()? Разница в том, что вы все равно можете получить свойство, определенное с помощью Object.defineProperty обычным способом:

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

И если определено с символом, как в предыдущем абзаце:

fruit = Symbol("apple");

У вас будет возможность получить его значение, только зная его переменную, т.е.

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

Кроме того, определение другого свойства под ключом "apple" сделает объект более старым (и если он жестко закодирован, он может вызвать ошибку). Итак, больше яблок! Это жаль. Ссылаясь на предыдущий параграф, символы уникальны и определяют ключ, поскольку Symbol() сделает его уникальным.

Преобразование и проверка типов

  • В отличие от других типов данных невозможно преобразовать Symbol() в любой другой тип данных.

  • Можно "создать" символ на основе примитивного типа данных, вызвав Symbol(data).

  • Что касается проверки типа, ничего не меняется.

    function isSymbol ( variable ) {
        return typeof someSymbol === "symbol";
    }
    
    var a_Symbol = Symbol("hey!");
    var totally_Not_A_Symbol = "hey";
    
    console.log(isSymbol(a_Symbol)); //true
    console.log(isSymbol(totally_Not_A_Symbol)); //false
    

Ответ 5

Вот как я это вижу. Символы обеспечивают "дополнительный уровень конфиденциальности", предотвращая доступ к ключам/свойствам объекта через некоторые популярные методы, такие как Object.keys() и JSON.stringify().

var age = Symbol();  // declared in another module perhaps?
class Person {
   constructor(n,a){
      this.name = n;
      this[age] = a;  
   }
   introduce(){
       console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
   }
}
var j = new Person('Jane',45);
j.introduce();  // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45   (well…only if you know the age in the first place…)

Хотя данный объект сам по себе, такие свойства все еще могут быть обнаружены через отражение, прокси, Object.getOwnPropertySymbols() и т.д., нет естественных способов доступа к ним с помощью нескольких прямых методов, которые могут быть достаточными иногда из ООП.

Ответ 6

Они в значительной степени помогают нам иметь конфликты имен. В любое время, когда вы хотите создать свойство уникальным способом, когда вам нужно найти символ.

Посмотрите на мой пример

const bert = Symbol('Bert');

'Берт'

Примечание: это не значение, это то, что они назвали дескриптором, потому что сам символ является просто уникальным идентификатором. Так что, если бы вы визуализировали, каким будет символ, возможно, вы сможете визуализировать его примерно как этот "sdfasdfa2342134987fgsdfgsdf9808fsfgsd" абсолютный уникальный символ, так что вы можете быть уверены, что он никогда не заменит любой другой фрагмент кода в нем.

Что круто в этом, если я создаю второй символ, например

const person = Symbol('Bert')

Вы можете видеть, что я снова использовал "Берт". Будут ли они такими же, потому что я описал их как одно и то же?

const bert = Symbol('Bert');
const person = Symbol('Bert');


console.log(bert);
console.log(person);
console.log(bert === person);
console.log(bert == person);

Ответ 7

Символ JS - это новый примитивный тип данных. Это токены, которые служат уникальными идентификаторами. Символ можно создать с помощью конструктора Symbol. Взять, к примеру, этот фрагмент из MDN:

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.