Интервьюер: Каково использование примитивного типа данных JavaScript Symbol?

JavaScript

Когда мы уже упоминали примитивные типы данных JavaScript, мы знаем, что существуютNumber,String,Null,Boolean,UndefinedЭти виды. ES6 представляет новые примитивные типы данныхSymbolа такжеBigInt. Сегодня мы узнаемSymbolТипы.SymbolТип предназначен для решения проблемы конфликта имен атрибутов, а также имеет функцию имитации частных атрибутов.

Введение

СоздайтеsymbolСамый простой способ использовать переменную — использоватьSymbol()функция.sysmbolПеременные являются особыми двумя способами:

  1. Его можно использовать как имя свойства объекта. только струны иsymbolтипы могут использоваться как имена свойств объекта.
  2. нет двухsymbolзначения равны.
const symbol1 = Symbol();
const symbol2 = Symbol();

symbol1 === symbol2; // false

const obj = {};
obj[symbol1] = 'Hello';
obj[symbol2] = 'World';

obj[symbol1]; // 'Hello'
obj[symbol2]; // 'World'

Хотя звонитSymbol()сделать его похожим на объект, на самом делеsymbolявляется примитивным типом данных JavaScript. ПучокSymbolиспользовать как конструкторnewсообщит об ошибке.

const symbol1 = Symbol();

typeof symbol1; // 'symbol'
symbol1 instanceof Object; // false

// Throws "TypeError: Symbol is not a constructor"
new Symbol();

Описание

Symbol()Функция только один параметр, строкаdescription. Единственная роль этого строкового параметра — помощь в отладке, т. е.toString()стоимость. Однако обратите внимание, что оба имеют одинаковыеdescriptionизsymbolСлишкомне равныйиз.

const symbol1 = Symbol('my symbol');
const symbol2 = Symbol('my symbol');

symbol1 === symbol2; // false
console.log(symbol1); // 'Symbol(my symbol)'

иметь глобальныйsymbolреестр, сSymbol.for()созданныйsymbolбудут добавлены в этот реестр и будут использовать егоdescriptionкак индексный ключ. То есть, если вы используетеSymbol.for()Создать с тем жеdescriptionдваsymbol, они равны.

const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');

symbol1 === symbol2; // true
console.log(symbol1); // 'Symbol(test)'

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

конфликт имен

JavaScript имеет встроенныйsymbol, то есть в ES6Symbol.iterator . имеютSymbol.iteratorОбъект функции называетсяповторяемый объект, то есть можно использовать на объектеfor/ofцикл.

const fibonacci = {
  [Symbol.iterator]: function*() {
    let a = 1;
    let b = 1;
    let temp;

    yield b;

    while (true) {
      temp = a;
      a = a + b;
      b = temp;
      yield b;
    }
  }
};

// Prints every Fibonacci number less than 100
for (const x of fibonacci) {
  if (x >= 100) {
    break;
  }
  console.log(x);
}

Зачем использовать это здесьSymbol.iteratorвместо струн? Предположим, что нетSymbol.iterator, итерируемый объект должен иметь строковое имя свойства'iterator', как следующий класс итерируемых объектов:

class MyClass {
  constructor(obj) {
    Object.assign(this, obj);
  }

  iterator() {
    const keys = Object.keys(this);
    let i = 0;
    return (function*() {
      if (i >= keys.length) {
        return;
      }
      yield keys[i++];
    })();
  }
}

MyClassЭкземпляр — это итерируемый объект, который может перемещаться по свойствам объекта. Но у приведенного выше класса есть потенциальный изъян.Предположим, что злоумышленник даетMyClassКонструктору передаетсяiteratorСвойства объекта:

const obj = new MyClass({ iterator: 'not a function' });

Как и тыobjиспользовать наfor/of, JavaScript выдастTypeError: obj is not iterableаномальный. Видно, что входящий объектiteratorФункция переопределяет классiteratorАтрибуты. Это какЗагрязнение прототипаПроблемы с безопасностью, бездумное копирование пользовательских данных повлияет на некоторые специальные свойства, такие как__proto__а такжеconstructorвызвать проблемы.

Суть здесь в том, чтоsymbolПусть хорошо поливают внутренние данные объекта и пользовательские данные. из-заsysmbolНе может быть представлен в формате JSON, поэтому не беспокойтесь о передаче неуместныхSymbol.iteratorатрибутивные данные. Кроме того, для объектов, которые смешивают встроенные функции и пользовательские данные, напримерMongoose model, вы можете использовать егоsymbolчтобы пользовательские данные не конфликтовали со встроенными свойствами.

частная собственность

из-за любых двухsymbolне равны, и их можно легко использовать для моделирования частных свойств в JavaScript.symbolне появится вObject.keys(), поэтому, если вы явноexport Одинsymbolили сObject.getOwnPropertySymbols() функцию, иначе другой код не сможет получить доступ к этому свойству.

function getObj() {
  const symbol = Symbol('test');
  const obj = {};
  obj[symbol] = 'test';
  return obj;
}

const obj = getObj();

Object.keys(obj); // []

// 除非有这个 symbol 的引用,否则无法访问该属性
obj[Symbol('test')]; // undefined

// 用 getOwnPropertySymbols() 依然可以拿到 symbol 的引用
const [symbol] = Object.getOwnPropertySymbols(obj);
obj[symbol]; // 'test'

Другая причинаsymbolне появится вJSON.stringify()в итоге точноJSON.stringify()будет игнорироватьsymbolИмя свойства и значение свойства:

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

Суммировать

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

Увидев этот довольно темпераментный логотип, почему вы не обращаете внимания?