FEATURED · 精选文章

[TypeScript学习笔记-10]一个特殊且重要的类型:Symbol

发布时间 / 2026/8/23 23:35:03
来源 / 创域科博编辑部
栏目 / 资讯中心
[TypeScript学习笔记-10]一个特殊且重要的类型:Symbol 在 TypeScript 中Symbol是 ES6 引入的一种原始数据类型与number、string、boolean等基础类型并列。它表示一个独一无二、不可变的值通常被用作对象属性的键以确保属性名不会发生冲突。在 TypeScript 中声明一个 Symbol 类型的变量写法如下letsym:symbolSymbol(描述信息);这里的描述信息只是一个可选的描述字符串仅用于调试或日志输出并不会影响 Symbol 本身的唯一性。即使两个 Symbol 使用相同的描述它们也是完全不同的两个值。1. Symbol 具有怎样的特性举例Symbol 的核心特性可以概括为以下几点1.1 唯一性每次调用Symbol()都会生成一个全新的、独一无二的值即使描述相同也不相等consts1Symbol(key);consts2Symbol(key);console.log(s1s2);// false1.2 不可变性Symbol 一旦创建其值就无法被修改它本身是一个原始值不是对象因此也不能调用new Symbol()// 错误写法Symbol 是原始类型不能使用 new// const s new Symbol();// 正确写法constsSymbol(ok);1.3 可作为对象属性的键Symbol 最常见的用途是作为对象属性的键从而避免属性名冲突constuniqueKeySymbol(id);constobj{[uniqueKey]:123,name:Tom};console.log(obj[uniqueKey]);// 1231.4 不会被常规遍历方法枚举使用for...in或Object.keys()遍历对象时Symbol 类型的属性键不会被枚举出来constkeySymbol(hidden);constobj{name:Tom,[key]:secret};console.log(Object.keys(obj));// [name]console.log(Object.getOwnPropertySymbols(obj));// [ Symbol(hidden) ]2. unique Symbol 如何正确理解在 TypeScript 中如果仅仅把变量推导为symbol类型类型系统是无法分清具体的 Symbol 的。比如在如下的例子中在 TypeScript 看来foo和bar都是 Symbol 类型它可以让你写bar foo但这在逻辑上可能是不合理的。letfoo:symbolSymbol(Foo);letbar:symbolSymbol(bar);barfoo;为了解决这个问题TypeScript 引入了unique symbol。它是symbol的子类型Subtype代表某一个特定、具体、独一无二的 Symbol 类型。严格的使用限制因为unique symbol代表一个具体的、不可变的值所以 TypeScript 对它有极其严格的语法要求必须使用const声明不能用 let 或 var或者作为类Class的readonly static属性。不能手动显式指定类型为unique symbol却赋值一个普通变量它只能由 TypeScript 通过const自动推导出来。constfoo:uniquesymbolSymbol(Foo);// OK/// (TS) A variable whose type is a unique symbol type must be const.letbar:uniquesymbolSymbol(Bar);// (TS) A variable whose type is a unique symbol type must be const.varbaz:uniquesymbolSymbol(Baz);classcls{// OKstaticreadonlyfoo:uniquesymbolSymbol(Bar);// (TS) A property of a class whose type is a unique symbol type must be both static and readonly.staticbar:uniquesymbolSymbol(Bar);}3. Symbol 的具体应用场景Symbol是 TypeScript 中一个非常特殊的原始类型它的核心价值在于唯一性。无论是作为对象属性的键来避免冲突还是借助unique symbol在类型层面区分不同的标记Symbol 都为开发者提供了一种更安全、更灵活的设计手段。在实际项目中合理使用 Symbol 可以让代码结构更清晰也能减少因命名冲突而引发的潜在问题。3.1 防止对象属性名冲突当多个模块或库需要往同一个对象上挂载属性时使用 Symbol 作为键可以避免命名冲突constlibASymbol(libA);constlibBSymbol(libB);constsharedObj:any{};sharedObj[libA]A 库的数据;sharedObj[libB]B 库的数据;console.log(sharedObj[libA]);// A 库的数据console.log(sharedObj[libB]);// B 库的数据3.2 定义类的“隐藏”成员利用 Symbol 属性不会被常规方法枚举的特性可以在类中定义一些内部使用的成员const_countSymbol(count);classCounter{private[_count]:number0;increment(){this[_count];}getvalue(){returnthis[_count];}}constcnewCounter();c.increment();c.increment();console.log(c.value);// 23.3 实现全局注册表Symbol.forSymbol.for()会在全局注册表中查找并复用已存在的 Symbol适合在多个模块间共享同一个 Symbolconsts1Symbol.for(shared);consts2Symbol.for(shared);console.log(s1s2);// true4. 使用内置的 Symbol 常量JavaScript 提供了一些内置的 Symbol 常量用于自定义对象的基础行为。4.1 Symbol.iterator Symbol.asyncIteratorSymbol.iterator和Symbol.asyncIterator分别用于定义一个同步和异步迭代器 前者会在对象被用于for...of循环、解构赋值...展开运算符或Array.from()时被调用 后者则在for await...of循环遍历异步数据源被调用。constrange{from:1,to:3,[Symbol.iterator](){letcurrentthis.from;return{next:()currentthis.to?{value:current,done:false}:{done:true}};}};for(letnumofrange)console.log(num);// 输出: 1, 2, 34.2 Symbol.toPrimitiveSymbol.toPrimitive用于定义当前对象针对指定基元类型数字、字符串、布尔值的转换规则。自定义对象在被转换为原始类型值时的行为。对象参与数学运算如 、-或字符串拼接、比较时会被调用。在下面的演示程序中我们定义了常量对象PI并利用将此Symbol作为属性名称定义了类型转换函数。constPI{[Symbol.toPrimitive](hint:string){if(hintnumber)return3.14;if(hintstring)returnπ;returntrue;// 默认}};console.log(${PI});//πconsole.log(PIasany*Math.pow(2,2));// 12.564.3 Symbol.hasInstanceSymbol.hasInstance用来自定义 instanceof 运算符的识别逻辑。当执行obj instanceof MyClass时实际上是在调用MyClass[Symbol.hasInstance](obj)。在下面的演示程序中我们定义了表示质数的类型PrimeNumber并以此Symbol命名了一个静态方法用来确定指定的对象是否是一个质数。classPrimeNumber{static[Symbol.hasInstance](num:any){returnPrimeNumber.isPrimeNumber(num)}staticisPrimeNumber(num:any){if(num1||!Number.isInteger(num))returnfalse;if(num2)returntrue;if(num%20)returnfalse;constboundaryMath.sqrt(num);for(leti3;iboundary;i2){if(num%i0)returnfalse;}returntrue;}}console.log(2asanyinstanceofPrimeNumber)// Trueconsole.log(3asanyinstanceofPrimeNumber)// Trueconsole.log(4asanyinstanceofPrimeNumber)// False4.4 其它TypeScript还为很多常用的应用场景定义了对应的Symbol 比如当你在字符串上调用match、replace、search、split等方法且传入的参数是一个自定义对象时你可以通过以下符号拦截这些操作Symbol.match改变String.prototype.match()的行为。也用于 isRegExp 的内部判断Symbol.replace改变String.prototype.replace()的行为允许自定义替换逻辑Symbol.search改变String.prototype.search()的行为Symbol.split改变String.prototype.split()的行为。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻