it-roy-ru.com

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

UPDATE: недавно вышла блестящая статья от Mozilla . Прочитайте это, если вам интересно.

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

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

321
Yanis

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

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

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

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

196
Andreas Rossberg

Символы не гарантируют истинную конфиденциальность, но могут использоваться для разделения общих и внутренних свойств объектов. Давайте рассмотрим пример, в котором мы можем использовать 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

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

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
82
Samar Panda

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 вы хотели бы сделать эти strings (так logger.levels.DEBUG === 'debug') или numbers (logger.levels.DEBUG === 10). Оба из них не идеальны, так как эти значения не являются уникальными значениями, но Symbols есть! Так что 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');

Подробнее читайте в этом отличная статья.

34
Mihai Alexandru-Ionut

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

TLDR;

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

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

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

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

  • любые данные имеют свой собственный символ, и для тех же данных символы будут одинаковые. Больше информации в следующем параграфе, иначе это не 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)).

Следующие два абзаца относятся только к символу first-type.

Как извлечь выгоду из использования 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
    

28
nicael

Вот как я это вижу. Символы обеспечивают "дополнительный уровень конфиденциальности", предотвращая раскрытие ключей/свойств объекта с помощью некоторых популярных методов, таких как 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 () и т.д., Но нет естественных средств для доступа к ним через несколько прямых методов, которых иногда может быть достаточно из OOP перспектива.

16
Chong Lip Phang