这篇文章主要介绍“vscode工具函数Symbol如何使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vscode工具函数Symbol如何使用”文章能帮助大家解决问题。
什么是Symbol?
符号(
)是JavaScript中的一个原始数据类型,是ECMAScript 6标准引入的新特性。符号是一种类似于字符串的数据类型,但与字符串不同的是,符号是唯一的并且不可变的。
Symbol
的定义方法如下:
Symbol
const mySymbol = Symbol('my symbol');
每次调用
创建的值都是唯一的,即使对同一个参数调用两遍
Symbol
它们的值还是不一样的:
Symbol
Symbol("foo") === Symbol("foo"); // false
js的第六种基本数据类型
在
出现之前,
Symbol
已经有五种内置的基本数据类型:
Javascript
布尔值(
):表示真或假,只有两个取值:true和false。
Boolean
数字(
):表示整数或浮点数,可以使用十进制、十六进制、八进制、科学计数法等多种表示方式。
Number
字符串(
):表示文本字符串,可以使用单引号、双引号、反引号等方式表示。
String
空值(
):表示一个空值或不存在的对象。
Null
未定义(
):表示一个未定义的值或未声明的变量。
Undefined
则作为第六种基本数据类型加入到语言中:
Symbol
符号(
):表示唯一的、不可变的值,用于保护属性名、实现私有属性或方法等场景。
Symbol
Symbol的起源
在JavaScript诞生之初,对象属性只能使用字符串作为键,这导致了一些问题。例如,当两个不同的对象试图使用相同的字符串作为属性名时,可能会导致属性名冲突。此外,
中没有一种简单的方法来实现私有属性或方法。
JavaScript
其实对于
的追溯早在
Symbol
语言中就有体现:
Lisp
(setq x (intern "my-symbol"))
这里其实就是创建了一个名为
的符号对象,并将其赋值给变量
my-symbol
。
x
另外,ES6引入
其实离不开
Symbol
的身影,在
Ruby
中,可以使用冒号(
Ruby
)来创建符号。冒号后面跟着符号的名称,如:
:
:my_symbol
可以看到其实
的语法更加简洁,定义和使用都是用冒号区分:
Ruby
person = {
'name' => 'John',
'age' => 30,
:gender => 'Male'
}
puts person[:gender] # 输出:'Male'
所以,在这样的需求背景下,
在首批特性中包含了
ES6
也不足为奇了。
Symbol
Symbol的基本知识
定义与使用
在
中,可以使用
JavaScript
函数来创建一个符号,如下所示:
Symbol()
const mySymbol = Symbol();
函数可以接受一个描述性字符串作为参数,用于标识符号的含义,如下所示:
Symbol
const mySymbol = Symbol('my symbol');
需要注意的是,每个
函数调用都会返回一个唯一的符号,即使描述性字符串相同,它们也是不同的符号。
Symbol()
类型的值可以用作对象的属性名,如下所示:
Symbol
const mySymbol = Symbol('my symbol');
const myObject = {
[mySymbol]: 'hello'
};
console.log(myObject[mySymbol]); // 输出:'hello'
在上面的代码中,我们使用符号
作为对象
mySymbol
的属性名,并将其值设置为
myObject
。使用符号作为属性名的好处是它们不会与其他属性名冲突,并且对外不可见,因此可以用于实现私有属性或方法等场景。
'hello'
另外,JavaScript中的Symbol类型有两个特殊的方法
和
Symbol.for()
,用于创建全局符号和获取已经存在的全局符号。
Symbol.keyFor()
: 用于创建或获取一个全局符号,如果全局符号已经存在,则返回已经存在的符号,否则创建一个新的全局符号。例如:
Symbol.for()
const mySymbol = Symbol.for('my symbol');
const sameSymbol = Symbol.for('my symbol');
console.log(mySymbol === sameSymbol); // 输出:true
在上面的代码中,我们使用
方法来创建一个全局符号
Symbol.for()
,并将其赋值给
'my symbol'
变量。然后,我们再次使用
mySymbol
方法来获取同一个全局符号,赋值给
Symbol.for()
变量。由于全局符号已经存在,因此
sameSymbol
变量的值等于
sameSymbol
变量的值,输出
mySymbol
。
true
Symbol的重要属性
1. Symbol.iterator: 用于指定对象的默认迭代器,例如:
const myObject = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
};
for (const value of myObject) {
console.log(value);
}
// 输出:1 2 3
在上面的代码中,我们为
对象设置了
myObject
符号,并指定了一个生成器函数作为迭代器的实现。然后,我们可以使用
Symbol.iterator
循环迭代
for...of
对象,并输出其中的值。
myObject
2. Symbol.hasInstance: 用于定义一个对象是否为某个构造函数的实例。
方法接受一个参数,表示要检查的对象。该方法需要返回一个布尔值,表示该对象是否为该构造函数的实例。例如:
Symbol.hasInstance
class MyClass {
static [Symbol.hasInstance](obj) {
return obj instanceof Array;
}
}
console.log([] instanceof MyClass); // 输出:true
console.log({} instanceof MyClass); // 输出:false
在上面的代码中,我们定义了一个
类,并使用
MyClass
方法自定义了
Symbol.hasInstance
运算符的行为,使其检查对象是否为数组。当检查[]对象时,
instanceof
运算符返回
instanceof
,因为[]是
true
的实例;当检查
Array
对象时,
{}
运算符返回
instanceof
,因为{}不是
false
的实例。
Array
需要注意的是,
方法是一个静态方法,需要定义在构造函数的静态属性中。另外,
Symbol.hasInstance
方法不能被继承,因此子类需要重新定义该方法。
Symbol.hasInstance
3. Symbol.toStringTag: 用于自定义对象的默认字符串描述。
当调用
方法时,会使用该对象的
Object.prototype.toString()
属性作为默认的字符串描述,例如:
Symbol.toStringTag
class MyObject {
get [Symbol.toStringTag]() {
return 'MyObject';
}
}
const obj = new MyObject();
console.log(Object.prototype.toString.call(obj)); // 输出:'[object MyObject]'
在上面的代码中,我们定义了一个
类,并使用
MyObject
属性自定义了该类的默认字符串描述。然后,我们创建了一个
Symbol.toStringTag
对象,并使用
obj
方法获取其字符串描述,输出
Object.prototype.toString()
。
'[object MyObject]'
需要注意的是,
属性只有在调用
Symbol.toStringTag
方法时才会生效,对其他方法没有影响。另外,如果没有定义
Object.prototype.toString()
属性,则默认使用构造函数的名称作为字符串描述。
Symbol.toStringTag
4. Symbol.asyncIterator: 用于指定对象的默认异步迭代器。
当使用
循环迭代一个对象时,会调用该对象的
for await...of
方法获取异步迭代器。
Symbol.asyncIterator
方法需要返回一个异步迭代器对象,该对象实现了
Symbol.asyncIterator
方法,并返回一个
next()
对象。当迭代器迭代到结束时,
Promise
方法应该返回一个
next()
对象,该
Promise
对象的
Promise
属性为
value
,
undefined
属性为
done
。
true
例如,下面的代码演示了如何使用Symbol.asyncIterator属性定义一个异步迭代器:
const myObject = {
async *[Symbol.asyncIterator]() {
yield Promise.resolve(1);
yield Promise.resolve(2);
yield Promise.resolve(3);
}
};
(async function() {
for await (const value of myObject) {
console.log(value);
}
})();
// 输出:1 2 3
在上面的代码中,我们为
对象设置了
myObject
符号,并指定了一个异步生成器函数作为异步迭代器的实现。然后,我们使用
Symbol.asyncIterator
循环迭代
for await...of
对象,并输出其中的值。
myObject
需要注意的是,使用
属性定义的异步迭代器只能使用
Symbol.asyncIterator
循环进行迭代,不能使用普通的
for await...of
循环。此外,
for...of
属性只有在支持异步迭代器的环境中才能使用,例如
Symbol.asyncIterator
的版本必须在
Node.js
以上才支持异步迭代器。
10.0.0
Symbol的实现原理
作为基本数据类型实现比较简单,在最新的
symbol
代码实现如下:
v8
Symbol Factory::NewSymbolInternal(AllocationType allocation) {
DCHECK(allocation != AllocationType::kYoung);
// Statically ensure that it is safe to allocate symbols in paged spaces.
STATIC_ASSERT(Symbol::kSize <= kMaxRegularHeapObjectSize);
Symbol symbol = Symbol::cast(AllocateRawWithImmortalMap(
Symbol::kSize, allocation, read_only_roots().symbol_map()));
DisallowGarbageCollection no_gc;
// Generate a random hash value.
int hash = isolate()->GenerateIdentityHash(Name::kHashBitMask);
symbol.set_raw_hash_field(Name::kIsNotIntegerIndexMask |
(hash << Name::kHashShift));
symbol.set_description(read_only_roots().undefined_value(),
SKIP_WRITE_BARRIER);
symbol.set_flags(0);
DCHECK(!symbol.is_private());
return symbol;
}
该函数使用
方法为新的
AllocateRawWithImmortalMap()
对象分配内存,并将其强制转换为
Symbol
类型。接着,该函数使用
Symbol
类禁用垃圾回收器,以确保不会在生成哈希值的过程中触发垃圾回收。接下来,该函数使用
DisallowGarbageCollection
方法生成一个随机的哈希值,并将其存储在新的
GenerateIdentityHash()
对象中。然后,该函数将
Symbol
对象的描述设置为
Symbol
,并将其标志设置为
undefined
。最后,该函数返回新创建的Symbol对象。
0
所以使用
来唯一标识一个
hash
,在
symbol
内部还实现了
v8
来实现
symbol-table
的查找,本质上也是一个哈希表。
Symbol.for
为了简单起见,我们用
来模拟一下
js
的实现:
Symbol
const registry = {};
function createSymbol(description) {
const symbol = Object.create(null);
symbol.toString = () => `Symbol(${description || ''})`;
Object.defineProperty(symbol, 'description', {
value: description,
writable: false,
configurable: false,
enumerable: false,
});
return symbol;
}
function Symbol(description) {
if (typeof description !== 'undefined') {
description = String(description);
}
if (registry[description]) {
return registry[description];
}
const symbol = createSymbol(description);
registry[description] = symbol;
return symbol;
}
Symbol.for = function (key) {
if (registry[key]) {
return registry[key];
}
const symbol = createSymbol(key);
registry[key] = symbol;
return symbol;
};
Symbol.keyFor = function (symbol) {
for (const key in registry) {
if (registry.hasOwnProperty(key) && registry[key] === symbol) {
return key;
}
}
};
export default Symbol;
我们使用一个全局对象
来存储
registry
对象及其描述符信息。
Symbol
函数用于创建新的
createSymbol()
对象,其中使用了
Symbol
方法来创建一个没有原型的对象,并通过定义
Object.create()
和
toString()
属性来实现
description
对象的基本功能。
Symbol
函数用于创建新的
Symbol()
对象,它根据传入的描述符信息从
Symbol
中查找
registry
对象,如果找到了则返回已有的
Symbol
对象,否则创建新的
Symbol
对象并添加到
Symbol
中。
registry
Symbol的使用场景
在
Symbol
的应用其实不多,最新的代码只有:
VSCode
/**
* Can be passed into the Delayed to defer using a microtask
* */
export const MicrotaskDelay = Symbol('MicrotaskDelay');
在实际中,
经常被用于:
Symbol
1. 唯一属性键:Symbol可以作为对象属性的键,避免属性名冲突。
这在创建第三方库或插件时非常有用,因为可以确保库或插件的属性不会与其他代码意外冲突。
const uniqueKey = Symbol('uniqueKey');
const obj = {
[uniqueKey]: 'This value is uniquely keyed'
};
2. 定义私有属性(当然这一点现在ES规范已经有更好的方式了)
使用Symbol可以在对象上创建"私有"属性,它们不会被常规的属性枚举(如
,
for...in
或
Object.keys()
)包含在内。这有助于保护对象内部实现细节。
JSON.stringify()
3. 内置Symbol
JavaScript内置了一些具有特定功能的
。例如,
Symbol
可以定义对象的迭代行为,
Symbol.iterator
可以自定义
Symbol.toStringTag
方法的输出。
Object.prototype.toString.call()
4. 注册全局Symbol
方法允许在全局
Symbol.for()
注册表中创建或获取
Symbol
。这对于跨多个地方或模块使用相同的
Symbol
时非常有用。
Symbol
const globalSymbol = Symbol.for('globalSymbol');
const sameGlobalSymbol = Symbol.for('globalSymbol');
console.log(globalSymbol === sameGlobalSymbol); // true
Symbol的发展
在
上已经有两个关于
tc39
的提案:
Symbol
Symbols as WeakMap keys(Stage3)
作为一种新的数据类型,其功能和用途都比较有限,因此
Symbol
在
tc39
的基础上提出了一些新的提案,以扩展其功能和用途。其中一个比较重要的提案是
Symbol
,该提案已经进入到
Symbols as WeakMap keys
阶段。
Stage3
是一种新的集合类型,可以用于存储对象和关联的元数据。
WeakMap
的特点是键必须是对象,值可以是任意类型。
WeakMap
的另一个特点是,当键对象不再被引用时,
WeakMap
会自动删除该键值对,以避免内存泄漏。
WeakMap
提案的目的是将
Symbols as WeakMap keys
作为
Symbol
的键。这样,就可以在不影响
WeakMap
的自动垃圾回收机制的情况下,将
WeakMap
作为对象的元数据来使用。
Symbol
const weak = new WeakMap();
// Pun not intended: being a symbol makes it become a more symbolic key
const key = Symbol('my ref');
const someObject = { /* data data data */ };
weak.set(key, someObject);
Symbol Predicates Proposal(Stage2)
这是另一个关于Symbol的提案,添加了以下判断方法:
和
Symbol.isRegistered(symbol)
。
Symbol.isWellKnown(symbol)
其实对于库作者而言,了解更多关于
的信息是很重要的。根据使用情况,了解一个
Symbol
是否真正唯一、可伪造(已注册)或跨域共享(众所周知)可能非常关键。例如,将
Symbol
用作
Symbol
键需要确保
WeakMap
未被注册。该提案处于第二阶段,正在受到
Symbol
社区的广泛关注。如果被采纳,它将为
JavaScript
的应用带来更多的灵活性。
Symbol
function isWeakMapKey(key) {
switch (typeof key) {
case "object":
return key !== null;
case "function":
return true;
case "symbol":
return !Symbol.isRegistered(sym);
}
return false;
}
isWeakMapKey({}); // true
isWeakMapKey(Symbol()); // true
isWeakMapKey("foo"); // false
isWeakMapKey(Symbol.for("foo")); // false
isWeakMapKey(Symbol.asyncIterator); // true
您还可以检查是否获得了真正唯一的
:
Symbol
const isUniqueSymbol = sym => typeof sym === "symbol" && !(Symbol.isRegistered(sym) || Symbol.isWellKnown(sym));
isUniqueSymbol(Symbol()); // true
isUniqueSymbol(Symbol.for("foo")); // false
isUniqueSymbol(Symbol.asyncIterator); // false
isUniqueSymbol({}); // false
以上就是vscode工具函数Symbol如何使用的详细内容,更多关于vscode工具函数Symbol如何使用的资料请关注九品源码其它相关文章!