深入浅出typescript -- symbol类型

深入浅出typescript -- symbol类型

symbol我们平时用的比较少,所以可能了解也不是很多,这里就详细来说说symbol。

1、symbol 基本使用

symbol 是 ES6 新增的一种基本数据类型,它用来表示独一无二的值,可以通过 Symbol 构造函数生成。

ts 复制代码
const s = Symbol(); 
typeof s; // symbol

注意:Symbol 前面不能加 new关键字,直接调用即可创建一个独一无二的 symbol 类型的值。

可以在使用 Symbol 方法创建 symbol 类型值的时候传入一个参数,这个参数需要是一个字符串。如果传入的参数不是字符串,会先自动调用传入参数的 toString 方法转为字符串:

ts 复制代码
const s1 = Symbol("TypeScript"); 
const s2 = Symbol("Typescript"); 
console.log(s1 === s2); // false

上面代码的第三行可能会报一个错误:This condition will always return 'false' since the types 'unique symbol' and 'unique symbol' have no overlap. 这是因为编译器检测到这里的 s1 === s2 始终是false,所以编译器提醒这代码写的多余,建议进行优化。

上面使用Symbol创建了两个symbol对象,方法中都传入了相同的字符串,但是两个symbol值仍然是false,这就说明了 Symbol 方法会返回一个独一无二的值。Symbol 方法传入的这个字符串,就是方便我们区分 symbol 值的。可以调用 symbol 值的 toString 方法将它转为字符串:

ts 复制代码
const s1 = Symbol("Typescript"); 
console.log(s1.toString());  // 'Symbol(Typescript)'
console.log(Boolean(s));     // true 
console.log(!s);             // false

在TypeScript中使用symbol就是指定一个值的类型为symbol类型:

ts 复制代码
let a: symbol = Symbol()

TypeScript 中还有一个 unique symbol 类型,它是symbol的子类型,这种类型的值只能由Symbol()Symbol.for()创建,或者通过指定类型来指定变量是这种类型。这种类型的值只能用于常量的定义和用于属性名。需要注意,定义unique symbol类型的值,必须用 const 而不能用let来声明。下面来看在TypeScript中使用Symbol值作为属性名的例子:

ts 复制代码
const key1: unique symbol = Symbol()
let key2: symbol = Symbol()
const obj = {
    [key1]: 'value1',
    [key2]: 'value2'
}
console.log(obj[key1]) // value1
console.log(obj[key2]) // error 类型"symbol"不能作为索引类型使用。

2、symbol 作为属性名

在ES6中,对象的属性是支持表达式的,可以使用于一个变量来作为属性名,这对于代码的简化有很多用处,表达式必须放在大括号内:

ts 复制代码
let prop = "name"; 
const obj = { 
  [prop]: "TypeScript" 
};
console.log(obj.name); // 'TypeScript'

symbol 也可以作为属性名,因为symbol的值是独一无二的,所以当它作为属性名时,不会与其他任何属性名重复。当需要访问这个属性时,只能使用这个symbol值来访问(必须使用方括号形式来访问):

ts 复制代码
let name = Symbol(); 
let obj = { 
  [name]: "TypeScript" 
};
console.log(obj); // { Symbol(): 'TypeScript' }

console.log(obj[name]); // 'TypeScript' 
console.log(obj.name);  // undefined

在使用obj.name访问时,实际上是字符串name,这和访问普通字符串类型的属性名是一样的,要想访问属性名为symbol类型的属性时,必须使用方括号。方括号中的name才是我们定义的symbol类型的变量name。

3、symbol 属性名遍历

使用 Symbol 类型值作为属性名,这个属性是不会被 for...in遍历到的,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 、 JSON.stringify() 等方法获取到:

ts 复制代码
const name = Symbol("name"); 
const obj = { 
  [name]: "TypeScript", 
  age: 18 
};
for (const key in obj) { 
  console.log(key); 
}  
// => 'age' 
console.log(Object.keys(obj));  // ['age'] 
console.log(Object.getOwnPropertyNames(obj));  // ['age'] 
console.log(JSON.stringify(obj)); // '{ "age": 18 }

虽然这些方法都不能访问到Symbol类型的属性名,但是Symbol类型的属性并不是私有属性,可以使用 Object.getOwnPropertySymbols 方法获取对象的所有symbol类型的属性名:

ts 复制代码
const name = Symbol("name"); 
const obj = { 
  [name]: "TypeScript", 
  age: 18 
};
const SymbolPropNames = Object.getOwnPropertySymbols(obj); 
console.log(SymbolPropNames); // [ Symbol(name) ] 
console.log(obj[SymbolPropNames[0]]); // 'TypeScript' 

除了这个方法,还可以使用ES6提供的 Reflect 对象的静态方法 Reflect.ownKeys ,它可以返回所有类型的属性名,Symbol 类型的也会返回:

ts 复制代码
const name = Symbol("name"); 
const obj = { 
  [name]: "TypeScript", 
  age: 18 
};
console.log(Reflect.ownKeys(obj)); // [ 'age', Symbol(name) ]

4、symbol 静态方法

Symbol 包含两个静态方法, for 和 keyFor 。

1)Symbol.for()

用Symbol创建的symbol类型的值都是独一无二的。使用 Symbol.for 方法传入字符串,会先检查有没有使用该字符串调用 Symbol.for 方法创建的 symbol 值。如果有,返回该值;如果没有,则使用该字符串新创建一个。使用该方法创建 symbol 值后会在全局范围进行注册。

ts 复制代码
const iframe = document.createElement("iframe"); 
iframe.src = String(window.location); 
document.body.appendChild(iframe); 

iframe.contentWindow.Symbol.for("TypeScript") === Symbol.for("TypeScript"); // true // 注意:如果你在JavaScript环境中这段代码是没有问题的,但是如果在TypeScript开发环境中,可能会报错:类型"Window"上不存在属性"Symbol"。 // 因为这里编译器推断出iframe.contentWindow是Window类型,但是TypeScript的声明文件中,对Window的定义缺少Symbol这个字段,所以会报错,

上面代码中,创建了一个iframe节点并把它放在body中,通过这个 iframe 对象的 contentWindow 拿到这个 iframe 的 window 对象,在 iframe.contentWindow上添加一个值就相当于在当前页面定义一个全局变量一样。可以看到,在 iframe 中定义的键为 TypeScript 的 symbol 值在和在当前页面定义的键为'TypeScript'的symbol 值相等,说明它们是同一个值。


2) Symbol.keyFor()

该方法传入一个 symbol 值,返回该值在全局注册的键名:

ts 复制代码
const sym = Symbol.for("TypeScript"); 
console.log(Symbol.keyFor(sym)); // 'TypeScript'
相关推荐
乘风gg13 分钟前
还在养虾吗?虾王已诞生:微信龙虾 ClawBot
前端·ai编程·claude
小小小小宇28 分钟前
LLM 长期记忆构建
前端
lichenyang45340 分钟前
从 Express 老项目到 NestJS + Docker:一次车辆管理系统的渐进式重构
前端
Momo__2 小时前
VueUse createReusableTemplate —— 单文件组件内的模板复用神器
前端·vue.js
程序员小富2 小时前
我开源了一个开发者专属的智能 JSON 工具,得到了媳妇高度认可
前端·vue.js·后端
小小小小宇2 小时前
程序员如何给 LLM 装工具以及看懂推理过程
前端
写代码的皮筏艇2 小时前
React中的forwardRef
前端·react.js·面试
槑有老呆2 小时前
花三个月工资请了个 AI 程序员,结果它连青岛啤酒股价都查不了
前端
风骏时光牛马2 小时前
Verilog开发常见问题汇总解析
前端
子兮曰2 小时前
AI Coding Method Map:一张图看懂 AI 编程的完整链路
前端·人工智能·后端