Symbol符号是“唯一性”的类型

在TypeScript中,Symbol是一种特殊的、不可变的数据类型,它的主要用途是创建唯一的标识符。它在ES6中引入的js原始类型,ts也完全支持。

1.Symbol的基本用法(创建唯一的Symbol

使用Symbol()函数来创建一个Symbol。

每次创建都是唯一的

ts 复制代码
const sym1 = Symbol();
const sym2 = Symbol();

console.log(sym1 === sym2); // false,每个 Symbol 都是唯一的
2.Symbol的常见用途

用作对象属性时,防止冲突

ts 复制代码
  name: "Alice",
  [Symbol("id")]: 123,
  [Symbol("id")]: 456  // 不会覆盖!这是另一个唯一的 Symbol
};
console.log(User)
3.可选的描述,但不影响唯一性
ts 复制代码
Symbol("key") !== Symbol("key"); // true,它们仍是不同的
4.全局Symbol注册表(Symbol.for)-唯一性的可控例外
ts 复制代码
const s1 = Symbol.for("shared"); // 注册或获取全局 Symbol
const s2 = Symbol.for("shared");
console.log(s1 === s2); // true 
ts 复制代码
const 我的爱 = Symbol("forever");
const 你的爱 = Symbol("forever");

console.log(我的爱 === 你的爱); // false
// 因为每一份爱都是唯一的

// 但我们可以约定:
const 我们的爱 = Symbol.for("us"); // 全局唯一,属于我们两个人

console.log("尽管爱是唯一的,但我们选择了彼此,成为唯一的我们。");
ts 复制代码
// 第一次调用:创建并注册
const sym1 = Symbol.for("com.myapp.userCache");
console.log("第一次创建:", sym1); // Symbol(com.myapp.userCache)

// 第二次调用:从注册表中获取已存在的 Symbol
const sym2 = Symbol.for("com.myapp.userCache");
console.log("第二次获取:", sym2); // Symbol(com.myapp.userCache)

// 它们是完全相同的!
console.log(sym1 === sym2); // true 
内置symbols用来表达语言内部的行为:

1.Symbol.hasInstance方法被instanceof调用,用于判断一个对象是否是某个构造器的实例。

ts 复制代码
class ArrayLike {
  static [Symbol.hasInstance](obj) {
    return Array.isArray(obj) && obj.length > 0;
  }
}

console.log([1, 2, 3] instanceof ArrayLike); // true
console.log([] instanceof ArrayLike);        // false (空数组)
console.log("not array" instanceof ArrayLike); // false

2.symbol.isConcatSpreadale布尔值,控制Array.prototype.concat是都"展开"该对象的元素。

ts 复制代码
const arr1 = [1, 2];
const arr2 = [3, 4];

// 默认行为:展开
console.log(arr1.concat(arr2)); // [1, 2, 3, 4]

// 自定义:不展开
const notSpreadable = {
  [Symbol.isConcatSpreadable]: false,
  0: 'a',
  1: 'b',
  length: 2
};

console.log(arr1.concat(notSpreadable)); // [1, 2, {0: 'a', 1: 'b', length: 2}]

3.symbol.iterator返回对象的默认迭代器,用于for...of、扩展运算符等。

ts 复制代码
const myRange = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    const end = this.to;
    return {
      next() {
        if (current <= end) {
          return { done: false, value: current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for (const n of myRange) {
  console.log(n); // 1, 2, 3, 4, 5
}

4.symbol.matchString.prototype.match调用,自定义字符串匹配行为。

ts 复制代码
class CustomMatcher {
  [Symbol.match](str) {
    return str.includes("hello") ? ["Hello Found!"] : null;
  }
}

console.log("say hello world".match(new CustomMatcher())); 
// ["Hello Found!"]

5.symbol.replaceString.prototype.replace调用,自定义替换逻辑。

ts 复制代码
class VowelReplacer {
  [Symbol.replace](str, newChar = '*') {
    return str.replace(/[aeiou]/g, newChar);
  }
}

console.log("hello".replace(new VowelReplacer(), '#'));
// "h#ll#" 

6.symbol.searchString.prototype.search调用,返回匹配项的索引。

ts 复制代码
class FirstVowel {
  [Symbol.search](str) {
    return str.search(/[aeiou]/);
  }
}

console.log("hello".search(new FirstVowel())); // 1 (e 的位置)

7.symbol.splitstring.prototype.split调用,自定义分割逻辑。

ts 复制代码
class SplitOnVowels {
  [Symbol.split](str) {
    return str.split(/[aeiou]/);
  }
}

console.log("hello".split(new SplitOnVowels())); 
// ["h", "ll", ""]

8.symbol.species控制派生对象的构造器。例如map()、filter()返回什么类型。

ts 复制代码
class MyArray extends Array {
  static get [Symbol.species]() {
    return Array; // 让 map 返回普通数组,而不是 MyArray
  }
}

const arr = new MyArray(1, 2, 3);
const mapped = arr.map(x => x * 2);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true

9.symbol.toprimitive控制对象转原始值(string、number、default)的行为。

ts 复制代码
const money = {
  amount: 100,
  currency: "USD",
  [Symbol.toPrimitive](hint) {
    if (hint === "string") {
      return `${this.currency}${this.amount}`;
    } else if (hint === "number") {
      return this.amount;
    } else {
      return this.amount; // default
    }
  }
};

console.log(String(money));     // "USD100"
console.log(Number(money));     // 100
console.log(money + 50);        // 150 (number context)

10.symbol.toStringTag自定义object.prototype.toString的返回标签。

ts 复制代码
const myPet = {
  name: "Buddy",
  [Symbol.toStringTag]: "Dog",
};

console.log(Object.prototype.toString.call(myPet));
// "[object Dog]"

// 常用于自定义类
class ApiResponse {
  get [Symbol.toStringTag]() {
    return "ApiResponse";
  }
}
console.log(new ApiResponse()); // [object ApiResponse]

11.symbol.unscopables指定哪些属性在with语句中不应被绑定。

ts 复制代码
// Array 默认的 unscopables
Array.prototype[Symbol.unscopables] = {
  ...Array.prototype[Symbol.unscopables],
  keys: true, // 表示 'keys' 不应被 with 绑定
  values: true
};

// 演示
const obj = { keys: "custom keys value" };

with (["a", "b"]) {
  console.log(keys); // 仍然输出 "custom keys value",而不是数组的 keys() 方法
  // 因为 keys 被标记为 unscopables,不会从数组"泄漏"到作用域

补充:synbol.asyncIterator重要的内置symbol,用于异步迭代。

ts 复制代码
const asyncStream = {
  [Symbol.asyncIterator]() {
    const values = [1, 2, 3];
    let i = 0;
    return {
      async next() {
        await new Promise(r => setTimeout(r, 100)); // 模拟异步
        return i < values.length
          ? { value: values[i++], done: false }
          : { done: true };
      }
    };
  }
};

// 必须在 async 函数中使用
(async () => {
  for await (const x of asyncStream) {
    console.log(x); // 1, 2, 3(每100ms输出一个)
  }
})();

总结:

1.hasInstance 自定义instantceof判断逻辑

2.isConcatSpreadable 控制数组拼接是否展开

3.iterator/asyncIterator 使对象可迭代(同步/异步)

4.match/replace/search/split 自定义正义行为

5.species 控制派生对象类型(如map()返回类型)

6.toPrimitive 字典贵toString()标签

7.unscopables控制with语句的作用域绑定

相关推荐
月亮慢慢圆9 小时前
cookie,session和token的区别和用途
前端
郭邯9 小时前
vant-weapp源码解读(3)
前端·微信小程序
golang学习记9 小时前
从0 死磕全栈第3天:React Router (Vite + React + TS 版):构建小时站实战指南
前端
Dream耀9 小时前
Promise静态方法解析:从并发控制到竞态处理
前端·javascript·代码规范
JarvanMo10 小时前
2025 年真正有效的 App Store 优化(ASO)
前端·ios
{⌐■_■}10 小时前
【JavaScript】前端两种路由模式,Hash路由,History 路由
前端·javascript·哈希算法
前端老鹰10 小时前
HTML `<datalist>`:原生下拉搜索框,无需 JS 也能实现联想功能
前端·html
玲小珑10 小时前
LangChain.js 完全开发手册(五)Runnable 接口与任务编排系统
前端·langchain·ai编程