在 TypeScript (TS) 中,Symbol
是一种基本数据类型,用于创建独一无二的标识符。它是 ECMAScript 6(ES6)引入的新特性,并且也被 TypeScript 支持。Symbol
可以用于创建对象属性、类成员、和其他上下文中,以确保它们的唯一性,从而避免命名冲突和混淆。
下面是一些关于在 TypeScript 中使用 Symbol
的基本信息:
Symbol 的基本使用
-
Symbol 的基本使用:
使用Symbol()
函数可以创建一个新的 Symbol。每个 Symbol 都是唯一的,不能通过常规的方法进行比较。let a1:symbol = Symbol(1) let a2:symbol = Symbol(1) // console.log(a1 === a2) // false // console.log(a1 == a2) // false 因为内存地址都是不同的 // Symbol.for 去全局 symbol 有没有注册过这个 key,如果有直接拿来用,没有就去创建一个 // console.log(Symbol.for('a') === Symbol.for('a')); // true let obj = { name: 1, a1: 111, a2: 222 } // console.log(obj); // { name: 1, a1: 111, a2: 222 } let obj2 = { name: 1, [a1]: 111, // 使用索引签名 [a2]: 222 } console.log(obj2); // { name: 1, [Symbol(1)]: 111, [Symbol(1)]: 222 } // for in 不能读到 Symbol for(let key in obj) { console.log(key); // name } // keys 读不到 Symbol,只能读到 name console.log(Object.keys(obj)); // getOwnPropertyNames 读不到 Symbol,只能读到 name console.log(Object.getOwnPropertyNames(obj)); // getOwnPropertySymbols 读到 Symbol,但又读不到 name 了 console.log(Object.getOwnPropertySymbols(obj)); // Reflect.ownKeys 可以读到 Symbol 和 name console.log(Reflect.ownKeys(obj));
-
Symbol 用作对象属性:
Symbol
可以用作对象的属性名,以避免属性名冲突。因为 Symbol 是唯一的,所以不会意外覆盖其他属性。const myObj = { [mySymbol]: "This is a Symbol property" }; console.log(myObj[mySymbol]); // Output: This is a Symbol property
-
内置 Symbols:
TypeScript 和 JavaScript 提供了一些预定义的内置 Symbols,例如Symbol.iterator
、Symbol.toStringTag
等,用于指定对象的特定行为或元信息。 -
Symbol 作为类成员:
Symbol 可以用作类的成员,用于定义私有成员或特定的行为。const _privateSymbol = Symbol("private"); class MyClass { private[_privateSymbol] = "This is a private symbol property"; } const instance = new MyClass(); console.log(instance[_privateSymbol]); // Output: This is a private symbol property
-
Symbols 作为属性键的限制:
使用 Symbol 作为属性键可以在一定程度上限制外部对属性的访问。但并不是绝对的私有性,因为使用Object.getOwnPropertySymbols()
仍然可以获取到对象的 Symbol 属性。const obj = { [mySymbol]: "This is a Symbol property" }; const symbolKeys = Object.getOwnPropertySymbols(obj); console.log(obj[symbolKeys[0]]); // Output: This is a Symbol property
生成器
function * generator() {
yield Promise.resolve('xx')
yield 1;
yield 2;
yield '3';
}
const man = generator();
console.log(man.next()); // { value: Promise { 'xx' }, done: false }
console.log(man.next()); // { value: 1, done: false }
console.log(man.next()); // { value: 2, done: false }
console.log(man.next()); // { value: '3', done: false }
console.log(man.next()); // { value: undefined, done: true }
迭代器
将迭代器之前先讲几个数据类型:
// Set
let set:Set<number> = new Set([1,1,2,2,3,3])
console.log(set); // Set(3) { 1, 2, 3 }
// Map
let map:Map<any,any> = new Map()
let Arr = [1,2,4]
map.set(Arr,'xx')
// 这里是将 Arr 数组当作 map 元素的 key,'xx' 是 value 插入到 map 中,所以在获取数据的时候用的是 Arr
console.log(map.get(Arr)); // xx
// 伪数组
function args() {
console.log(arguments);
}
// let list = document.querySelectorAll('div'); // 需要浏览器环境,node 环境不支持
想要遍历以上等等数据类型,就要用到迭代器
let set:Set<number> = new Set([1,1,2,2,3,3])
let map:Map<any,any> = new Map()
let Arr = [1,2,4]
map.set(Arr,'xx')
const each = (value:any) => {
let i:any = value[Symbol.iterator]()
let next:any = {done:false}
while(!next.done){
next = i.next()
if(!next.done) {
console.log(next.value)
}
}
}
each(map) // [ [ 1, 2, 4 ], 'xx' ] 输出 key value 的数组
each(set) // 1 2 3
迭代器的语法糖
// for of 对象不可以使用
let set:Set<number> = new Set([1,1,2,2,3,3])
for (let value of set) {
console.log(value); // 1 2 3
}
解构的底层原理也是调用了迭代器
let [a,b,c] = [1,2,3]
console.log(a,b,c); // 1 2 3
let arr = [2,3,4]
let copy = [...arr]
console.log(copy); // [2, 3, 4]
在迭代器的语法糖中提到对象不可以使用 for of
,那么对象就始终不可以使用 for of
吗?no!我们可以用调用的Iterator
接口的方式解决这个问题:一个对象如果要具备可被for...of
循环调用的 Iterator
接口,就必须在Symbol.iterator
的属性上部署遍历器生成方法。
// 对象遍历的底层原理 迭代器
let obj = {
max: 5,
current: 0,
// 创建迭代器对象,使得 obj 变成了可迭代的
[Symbol.iterator]() {
return {
// 迭代器对象的作用域不同于外部对象,它需要自己存储这些值
max: this.max,
current: this.current,
// 定义了如何生成下一个迭代值
next() {
if (this.current < this.max) {
this.current++
return {
done: false,
value: this.current
}
} else {
return {
done: true
}
}
}
}
}
}
for (let value of obj) {
console.log(value); // 1 2 3 4 5
}
此时对象也可以使用 for of
啦~
同理,数组解构底层也是调用了 Symbol.iterator
。
let x = [...obj]
console.log(x); // [ 1, 2, 3, 4, 5 ]