Maison > interface Web > js tutoriel > le corps du texte

A quoi sert le type Symbol en JavaScript ?

不言
Libérer: 2019-03-19 11:21:17
avant
7446 Les gens l'ont consulté

Le contenu de cet article porte sur quelle est l'utilisation du type Symbol en JavaScript ? Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.

Les symboles sont un nouveau type de données introduit dans ES6, qui apporte certains avantages à JS, notamment en ce qui concerne les propriétés des objets. Mais que peuvent-ils faire pour nous que les chaînes ne peuvent pas faire ?

Avant de nous plonger dans les symboles, examinons certaines fonctionnalités JavaScript dont de nombreux développeurs ne sont peut-être pas conscients.

Contexte

Les types de données en js sont généralement divisés en deux types, à savoir : le type de valeur et le type de référence

Type de valeur (type de base) : type numérique (Nombre) , type de caractère (String), type booléen (Boolean), types de référence nuls et sous-définis (classes) : fonctions, objets, tableaux, etc.

Compréhension du type valeur : affectation mutuelle entre variables, fait référence à l'ouverture un nouvel espace mémoire, attribuant des valeurs de variable aux nouvelles variables et les enregistrant dans la mémoire nouvellement ouverte ; les modifications ultérieures des valeurs​​des deux variables ne s'affecteront pas, par exemple :

var a=10; //开辟一块内存空间保存变量a的值“10”;
var b=a; //给变量 b 开辟一块新的内存空间,将 a 的值 “10” 赋值一份保存到新的内存里;
//a 和 b 的值以后无论如何变化,都不会影响到对方的值;
Copier après la connexion

Certains langages, comme C , il existe des concepts de passage de références et de passage de valeurs. JavaScript a un concept similaire, où il est déduit en fonction du type de données transmises. Si une valeur est transmise à une fonction, la réaffectation de la valeur ne modifie pas la valeur à l'emplacement appelant. Cependant, si vous modifiez un type référence, la valeur modifiée sera également modifiée là où elle est appelée.

Compréhension du type de référence : L'affectation mutuelle entre variables est simplement un échange de pointeurs, plutôt que de copier un objet (objet ordinaire, objet fonction, objet tableau) vers une nouvelle variable. un seul objet, juste un guide de plus~~; par exemple :

var a={x:1,y:2} //需要开辟内存空间保存对象,变量 a 的值是一个地址,这个地址指向保存对象的空间;
var b=a; // 将a 的指引地址赋值给 b,而并非复制一给对象且新开一块内存空间来保存;
// 这个时候通过 a 来修改对象的属性,则通过 b 来查看属性时对象属性已经发生改变;
Copier après la connexion

le type de valeur (sauf la mystérieuse valeur NaN) sera toujours exactement égal à un autre type de valeur avec la même valeur, comme suit :

const first = "abc" + "def";
const second = "ab" + "cd" + "ef";
console.log(first === second); // true
Copier après la connexion

Mais les types référence avec la même structure ne sont pas égaux :

const obj1 = { name: "Intrinsic" };
const obj2 = { name: "Intrinsic" };
console.log(obj1 === obj2); // false
// 但是,它们的 .name 属性是基本类型:
console.log(obj1.name === obj2.name); // true
Copier après la connexion

Les objets jouent un rôle important dans le langage JavaScript, et ils sont utilisés partout. Les objets sont souvent utilisés comme collections de paires clé/valeur, cependant, les utiliser de cette manière présente une grande limitation : avant l'apparition du symbole, les clés d'objet ne pouvaient être que des chaînes, et si vous essayiez d'utiliser des caractères autres que des caractères Si la valeur de chaîne est utilisée comme clé de l'objet, alors la valeur sera transformée en chaîne, comme suit :

const obj = {};
obj.foo = 'foo';
obj['bar'] = 'bar';
obj[2] = 2;
obj[{}] = 'someobj';
console.log(obj);
// { '2': 2, foo: 'foo', bar: 'bar',
     '[object Object]': 'someobj' }
Copier après la connexion

Symbol Qu'est-ce que

Symbol() La fonction renverra une valeur de type symbole qui a des propriétés statiques et des méthodes statiques. Ses propriétés statiques exposeront plusieurs objets membres intégrés ; ses méthodes statiques exposeront l'enregistrement global des symboles et sont similaires aux classes d'objets intégrées, mais en tant que constructeur, il n'est pas complet car il ne prend pas en charge la syntaxe : "new Symbol()". Ainsi, les valeurs générées à l'aide de Symbol ne sont pas égales :

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false
Copier après la connexion

Lors de l'instanciation de symbol, il existe un premier argument facultatif auquel vous pouvez éventuellement fournir une chaîne. Cette valeur est destinée à être utilisée pour le débogage du code, sinon elle n'affectera pas vraiment le symbole lui-même.

const s1 = Symbol('debug');
const str = 'debug';
const s2 = Symbol('xxyy');
console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)
Copier après la connexion

le symbole en tant qu'attribut d'objet

le symbole a une autre utilisation importante, ils peuvent être utilisés comme clés dans les objets, comme suit :

const obj = {};
const sym = Symbol();
obj[sym] = 'foo';
obj.bar = 'bar';
console.log(obj); // { bar: 'bar' }
console.log(sym in obj); // true
console.log(obj[sym]); // foo
console.log(Object.keys(obj)); // ['bar']
Copier après la connexion

À première vue Vous voyez, il semble que vous puissiez utiliser symbol pour créer des propriétés privées sur un objet. De nombreux autres langages de programmation ont leurs propres propriétés privées dans leurs classes. L'omission des propriétés privées a toujours été considérée comme une lacune. de JavaScript.

Malheureusement, le code qui interagit avec cet objet peut toujours accéder à ses propriétés dont la clé est symbol . Ceci est même possible dans les cas où le code appelant n'a pas encore accès au symbole lui-même. Par exemple, la méthode Reflect.ownKeys() est capable d'obtenir une liste de toutes les clés de l'objet, y compris les chaînes et les symboles :

function tryToAddPrivate(o) {
  o[Symbol('Pseudo Private')] = 42;
}
const obj = { prop: 'hello' };
tryToAddPrivate(obj);
console.log(Reflect.ownKeys(obj));
// [ 'prop', Symbol(Pseudo Private) ]
console.log(obj[Reflect.ownKeys(obj)[1]]); // 42
Copier après la connexion
Remarque : des travaux sont actuellement en cours pour résoudre l'ajout de propriétés privées à cours en JavaScript. Le nom de cette fonctionnalité s'appelle Private Fields, et même si cela ne profitera pas à tous les objets, cela profitera aux objets qui sont des instances de la classe. Les champs privés sont disponibles à partir de Chrome 74.

Empêche les conflits de noms de propriété La notation

peut ne pas bénéficier directement du fait que JavaScript fournit des propriétés privées pour les objets. Cependant, ils sont bénéfiques pour une autre raison. Ils sont utiles lorsque différentes bibliothèques souhaitent ajouter des propriétés à un objet sans risque de conflits de noms.

Symbol Il est un peu difficile de fournir des propriétés privées pour les objets JavaScript, mais Symbol a un autre avantage, qui est d'éviter le risque de conflits de noms lorsque différentes bibliothèques ajoutent des propriétés aux objets.

Considérons une situation dans laquelle deux bibliothèques différentes souhaitent ajouter des données de base à un objet, peut-être qu'elles souhaitent toutes les deux définir une sorte d'identifiant sur l'objet. En utilisant simplement id comme clé, il existe un risque énorme que plusieurs bibliothèques utilisent la même clé.

function lib1tag(obj) {
  obj.id = 42;
}
function lib2tag(obj) {
  obj.id = 369;
}
Copier après la connexion

En utilisant des symboles, chaque bibliothèque peut générer les symboles requis lors de l'instanciation. Utilisez ensuite la valeur du Symbol généré comme propriété de l'objet :

const library1property = Symbol('lib1');
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = Symbol('lib2');
function lib2tag(obj) {
  obj[library2property] = 369;
}
Copier après la connexion

Pour cette raison, Symbol semble être bénéfique pour JavaScript.

Mais, pourriez-vous vous demander, pourquoi chaque bibliothèque ne peut-elle pas simplement générer une chaîne aléatoire ou utiliser un espace de noms lorsqu'elle est instanciée ?

const library1property = uuid(); // random approach
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = 'LIB2-NAMESPACE-id'; // namespaced approach
function lib2tag(obj) {
  obj[library2property] = 369;
}
Copier après la connexion

这种方法是没错的,这种方法实际上与 Symbol 的方法非常相似,除非两个库选择使用相同的属性名,否则不会有冲突的风险。

在这一点上,聪明的读者会指出,这两种方法并不完全相同。我们使用唯一名称的属性名仍然有一个缺点:它们的键非常容易找到,特别是当运行代码来迭代键或序列化对象时。考虑下面的例子:

const library2property = 'LIB2-NAMESPACE-id'; // namespaced
function lib2tag(obj) {
  obj[library2property] = 369;
}
const user = {
  name: 'Thomas Hunter II',
  age: 32
};
lib2tag(user);
JSON.stringify(user);
// '{"name":"Thomas Hunter II","age":32,"LIB2-NAMESPACE-id":369}'
Copier après la connexion

如果我们为对象的属性名使用了 Symbol,那么 JSON 输出将不包含它的值。这是为什么呢? 虽然 JavaScript 获得了对 Symbol 的支持,但这并不意味着 JSON 规范已经改变! JSON 只允许字符串作为键,JavaScript 不会尝试在最终 JSON 有效负载中表示 Symbol 属性。

const library2property = 'f468c902-26ed-4b2e-81d6-5775ae7eec5d'; // namespaced approach
function lib2tag(obj) {
  Object.defineProperty(obj, library2property, {
    enumerable: false,
    value: 369
  });
}
const user = {
  name: 'Thomas Hunter II',
  age: 32
};
lib2tag(user);
console.log(user); // {name: "Thomas Hunter II", age: 32, f468c902-26ed-4b2e-81d6-5775ae7eec5d: 369}
console.log(JSON.stringify(user)); // {"name":"Thomas Hunter II","age":32}
console.log(user[library2property]); // 369
Copier après la connexion

通过将 enumerable 属性设置为 false 而“隐藏”的字符串键的行为非常类似于 Symbol 键。它们通过 Object.keys() 遍历也看不到,但可以通过 Reflect.ownKeys() 显示,如下的示例所示:

const obj = {};
obj[Symbol()] = 1;
Object.defineProperty(obj, 'foo', {
  enumberable: false,
  value: 2
});
console.log(Object.keys(obj)); // []
console.log(Reflect.ownKeys(obj)); // [ 'foo', Symbol() ]
console.log(JSON.stringify(obj)); // {}
Copier après la connexion

在这点上,我们几乎重新创建了 Symbol。隐藏的字符串属性和 Symbol 都对序列化器隐藏。这两个属性都可以使用Reflect.ownKeys()方法读取,因此它们实际上不是私有的。假设我们为属性名的字符串版本使用某种名称空间/随机值,那么我们就消除了多个库意外发生名称冲突的风险。

但是,仍然有一个微小的区别。由于字符串是不可变的,而且 Symbol 总是保证惟一的,所以仍然有可能生成字符串组合会产生冲突。从数学上讲,这意味着 Symbol 确实提供了我们无法从字符串中得到的好处。

在 Node.js 中,检查对象时(例如使用 console.log() ),如果遇到名为 inspect 的对象上的方法,将调用该函数,并将打印内容。可以想象,这种行为并不是每个人都期望的,通常命名为 inspect 的方法经常与用户创建的对象发生冲突。

现在 Symbol 可用来实现这个功能,并且可以在 equire('util').inspect.custom 中使用。inspect 方法在Node.js v10 中被废弃,在 v1 1中完全被忽略, 现在没有人会偶然改变检查的行为。

模拟私有属性

这里有一个有趣的方法,我们可以用来模拟对象上的私有属性。这种方法将利用另一个 JavaScript 特性: proxy(代理)。代理本质上封装了一个对象,并允许我们对与该对象的各种操作进行干预。

代理提供了许多方法来拦截在对象上执行的操作。我们可以使用代理来说明我们的对象上可用的属性,在这种情况下,我们将制作一个隐藏我们两个已知隐藏属性的代理,一个是字符串 _favColor,另一个是分配给 favBook 的 S ymbol :

let proxy;

{
  const favBook = Symbol('fav book');

  const obj = {
    name: 'Thomas Hunter II',
    age: 32,
    _favColor: 'blue',
    [favBook]: 'Metro 2033',
    [Symbol('visible')]: 'foo'
  };

  const handler = {
    ownKeys: (target) => {
      const reportedKeys = [];
      const actualKeys = Reflect.ownKeys(target);

      for (const key of actualKeys) {
        if (key === favBook || key === '_favColor') {
          continue;
        }
        reportedKeys.push(key);
      }

      return reportedKeys;
    }
  };

  proxy = new Proxy(obj, handler);
}

console.log(Object.keys(proxy)); // [ 'name', 'age' ]
console.log(Reflect.ownKeys(proxy)); // [ 'name', 'age', Symbol(visible) ]
console.log(Object.getOwnPropertyNames(proxy)); // [ 'name', 'age' ]
console.log(Object.getOwnPropertySymbols(proxy)); // [Symbol(visible)]
console.log(proxy._favColor); // 'blue'
Copier après la connexion

使用 _favColor 字符串很简单:只需阅读库的源代码即可。 另外,通过蛮力找到动态键(例如前面的 uuid 示例)。但是,如果没有对 Symbol 的直接引用,任何人都不能 从proxy  对象访问'Metro 2033'值。

Node.js警告:Node.js中有一个功能会破坏代理的隐私。 JavaScript语 言本身不存在此功能,并且不适用于其他情况,例 如Web 浏览器。 它允许在给定代理时获得对底层对象的访问权。 以下是使用此功能打破上述私有属性示例的示例:

const [originalObject] = process
  .binding('util')
  .getProxyDetails(proxy);
const allKeys = Reflect.ownKeys(originalObject);
console.log(allKeys[3]); // Symbol(fav book)
Copier après la connexion

现在,我们需要修改全局 Reflect 对象,或者修改 util 流程绑定,以防止它们在特定的 Node.js 实例中使用。但这是一个可怕的兔子洞。

本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript教程视频栏目!

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:segmentfault.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal