Der Inhalt dieses Artikels befasst sich mit der Verwendung des Symboltyps in JavaScript. Es hat einen gewissen Referenzwert. Freunde in Not können sich darauf beziehen. Ich hoffe, es wird Ihnen hilfreich sein.
Symbole ist ein neuer Datentyp, der in ES6 eingeführt wurde und JS einige Vorteile bringt, insbesondere wenn es um Objekteigenschaften geht. Aber was können sie für uns tun, was Strings nicht können?
Bevor wir uns mit Symbolen befassen, werfen wir einen Blick auf einige JavaScript-Funktionen, die vielen Entwicklern möglicherweise nicht bekannt sind.
Hintergrund
Datentypen in js werden im Allgemeinen in zwei Typen unterteilt: Werttyp und Referenztyp
Werttyp (Grundtyp): numerischer Typ (Zahl) , Zeichentyp (String), Boolescher Typ (Boolean), Null- und unterdefinierte Referenztypen (Klassen): Funktionen, Objekte, Arrays usw.
Verstehen von Werttypen: gegenseitige Zuweisung zwischen Variablen, bezieht sich auf das Öffnen ein neuer Speicherplatz, der den neuen Variablen Variablenwerte zuweist und sie im neu geöffneten Speicher speichert, wirkt sich nicht gegenseitig aus, zum Beispiel:
var a=10; //开辟一块内存空间保存变量a的值“10”; var b=a; //给变量 b 开辟一块新的内存空间,将 a 的值 “10” 赋值一份保存到新的内存里; //a 和 b 的值以后无论如何变化,都不会影响到对方的值;
Einige Sprachen, wie z. B. C, verfügen über das Konzept der Referenzübergabe und der Wertübergabe. JavaScript hat ein ähnliches Konzept, bei dem basierend auf der Art der übergebenen Daten abgeleitet wird. Wenn ein Wert an eine Funktion übergeben wird, ändert die Neuzuweisung des Werts den Wert an der aufrufenden Stelle nicht. Wenn Sie jedoch einen Referenztyp ändern, wird der geänderte Wert auch dort geändert, wo er aufgerufen wird.
Referenztyp-Verständnis: Die gegenseitige Zuweisung zwischen Variablen ist nur ein Austausch von Zeigern und nicht das Kopieren eines Objekts (gewöhnliches Objekt, Funktionsobjekt, Array-Objekt) in eine neue Variable nur ein Objekt, nur ein weiterer Leitfaden~~; zum Beispiel:
var a={x:1,y:2} //需要开辟内存空间保存对象,变量 a 的值是一个地址,这个地址指向保存对象的空间; var b=a; // 将a 的指引地址赋值给 b,而并非复制一给对象且新开一块内存空间来保存; // 这个时候通过 a 来修改对象的属性,则通过 b 来查看属性时对象属性已经发生改变;
Ein Werttyp (mit Ausnahme des mysteriösen NaN-Werts) ist immer genau gleich einem anderen Werttyp mit demselben Wert, wie folgt:
const first = "abc" + "def"; const second = "ab" + "cd" + "ef"; console.log(first === second); // true
Aber Referenztypen mit derselben Struktur sind nicht gleich:
const obj1 = { name: "Intrinsic" }; const obj2 = { name: "Intrinsic" }; console.log(obj1 === obj2); // false // 但是,它们的 .name 属性是基本类型: console.log(obj1.name === obj2.name); // true
Objekte spielen eine wichtige Rolle in der JavaScript-Sprache und werden überall verwendet. Objekte werden oft als Sammlungen von Schlüssel/Wert-Paaren verwendet, ihre Verwendung auf diese Weise weist jedoch eine große Einschränkung auf: Bevor symbol erschien, konnten Objektschlüssel nur Zeichenfolgen sein, und wenn Sie versuchten, Nicht-Zeichen zu verwenden Wenn der Zeichenfolgenwert als Schlüssel des Objekts verwendet wird, wird der Wert wie folgt in eine Zeichenfolge umgewandelt:
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' }
Symbol() Die Funktion gibt den Symboltyp zurück. Der Wert dieses Typs verfügt über statische Eigenschaften und statische Methoden. Seine statischen Eigenschaften legen mehrere integrierte Mitgliedsobjekte offen; seine statischen Methoden legen die globale Symbolregistrierung offen und ähneln den integrierten Objektklassen, sind aber als Konstruktor nicht vollständig, da sie die Syntax nicht unterstützen: "new Symbol()"
. Die mit Symbol generierten Werte sind also nicht gleich:
const s1 = Symbol(); const s2 = Symbol(); console.log(s1 === s2); // false
Beim Instanziieren von symbol gibt es ein optionales erstes Argument, für das Sie optional eine Zeichenfolge angeben können. Dieser Wert soll zum Debuggen von Code verwendet werden, andernfalls hat er keinen wirklichen Einfluss auf das Symbol selbst.
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)
Symbol hat eine weitere wichtige Verwendung, sie können wie folgt als Schlüssel in Objekten verwendet werden:
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']
Auf den ersten Blick sieht das so aus So wie Sie Symbol verwenden können, um private Eigenschaften für ein Objekt zu erstellen, haben viele andere Programmiersprachen ihre eigenen privaten Eigenschaften in ihren Klassen. Das Weglassen privater Eigenschaften wurde immer als Manko von JavaScript angesehen.
Leider kann Code, der mit diesem Objekt interagiert, immer noch auf seine Eigenschaften zugreifen, deren Schlüssel symbol ist. Dies ist sogar in Fällen möglich, in denen der aufrufende Code noch keinen Zugriff auf das Symbol selbst hat. Die Reflect.ownKeys()
-Methode ist beispielsweise in der Lage, eine Liste aller Schlüssel für das Objekt abzurufen, einschließlich Zeichenfolgen und Symbolen:
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
Hinweis: Derzeit wird daran gearbeitet, das Hinzufügen privater Eigenschaften zu Klassen in zu bewältigen JavaScript. Der Name dieser Funktion lautet „Private Fields“ (Private Felder), und obwohl dies nicht allen Objekten zugute kommt, kommt es Objekten zugute, die Instanzen der Klasse sind. Private Felder sind ab Chrome 74 verfügbar.
-Notation profitiert möglicherweise nicht direkt davon, dass JavaScript private Eigenschaften für Objekte bereitstellt. Sie sind jedoch aus einem anderen Grund von Vorteil. Sie sind nützlich, wenn verschiedene Bibliotheken einem Objekt Eigenschaften hinzufügen möchten, ohne dass das Risiko von Namenskonflikten besteht.
Symbol Es ist etwas schwierig, private Eigenschaften für JavaScript-Objekte bereitzustellen, aber Symbol hat einen weiteren Vorteil: Es vermeidet das Risiko von Namenskonflikten, wenn verschiedene Bibliotheken Eigenschaften zu Objekten hinzufügen.
Stellen Sie sich eine Situation vor, in der zwei verschiedene Bibliotheken grundlegende Daten zu einem Objekt hinzufügen möchten. Möglicherweise möchten beide eine Art Kennung für das Objekt festlegen. Durch die einfache Verwendung von id
als Schlüssel besteht ein großes Risiko, dass mehrere Bibliotheken denselben Schlüssel verwenden.
function lib1tag(obj) { obj.id = 42; } function lib2tag(obj) { obj.id = 369; }
Durch die Verwendung von Symbolen kann jede Bibliothek bei der Instanziierung die erforderlichen Symbole generieren. Verwenden Sie dann den Wert des generierten Symbols als Eigenschaft des Objekts:
const library1property = Symbol('lib1'); function lib1tag(obj) { obj[library1property] = 42; } const library2property = Symbol('lib2'); function lib2tag(obj) { obj[library2property] = 369; }
Aus diesem Grund scheint Symbol für JavaScript von Vorteil zu sein.
Aber Sie fragen sich vielleicht, warum nicht jede Bibliothek bei der Instanziierung einfach eine zufällige Zeichenfolge generieren oder einen Namespace verwenden kann?
const library1property = uuid(); // random approach function lib1tag(obj) { obj[library1property] = 42; } const library2property = 'LIB2-NAMESPACE-id'; // namespaced approach function lib2tag(obj) { obj[library2property] = 369; }
这种方法是没错的,这种方法实际上与 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}'
如果我们为对象的属性名使用了 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
通过将 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)); // {}
在这点上,我们几乎重新创建了 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'
使用 _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)
现在,我们需要修改全局 Reflect 对象,或者修改 util 流程绑定,以防止它们在特定的 Node.js 实例中使用。但这是一个可怕的兔子洞。
本篇文章到这里就已经全部结束了,更多其他精彩内容可以关注PHP中文网的JavaScript教程视频栏目!
Das obige ist der detaillierte Inhalt vonWozu dient der Symboltyp in JavaScript?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!